Initial GDExtension demo code, which can initialize a Timeline and FFmpegReader instance, print Profiles, and send an Image into Godot 4.4/4.5. This is highly experimental, and disabled by default. Requires ENABLE_GODOT=1 to build, and manually copying the gdextension and bindings/godot/*.so file into your Godot project bin. NOTE: This also required a locally compiled version of Godot and Godot-cpp.

This commit is contained in:
Jonathan Thomas
2025-05-31 16:00:10 -05:00
parent ff1b7b6ced
commit efedc09cd9
8 changed files with 264 additions and 0 deletions

View File

@@ -20,6 +20,10 @@ IF(NOT DEFINED ENABLE_JAVA)
SET(ENABLE_JAVA 0)
ENDIF()
IF(NOT DEFINED ENABLE_GODOT)
SET(ENABLE_GODOT 0)
ENDIF()
############### INCLUDE EACH LANGUAGE BINDING ################
IF (ENABLE_PYTHON)
add_subdirectory(python)
@@ -32,3 +36,7 @@ ENDIF (ENABLE_RUBY)
IF (ENABLE_JAVA)
add_subdirectory(java)
ENDIF (ENABLE_JAVA)
IF (ENABLE_GODOT)
add_subdirectory(godot)
ENDIF (ENABLE_GODOT)

View File

@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.17)
project(libopenshot_godot)
# Clone and build GoDot CPP
# git submodule add https://github.com/godotengine/godot-cpp.git external/godot-cpp
# git submodule update --init --recursive
# cd external/godot-cpp
# scons -c
# scons platform=linux generate_bindings=yes -j$(nproc)
set(GODOT_CPP_PATH ${CMAKE_SOURCE_DIR}/external/godot-cpp)
include_directories(
${GODOT_CPP_PATH}/include
${GODOT_CPP_PATH}/gen/include
${GODOT_CPP_PATH}/gdextension
${CMAKE_SOURCE_DIR}/include
)
link_directories(${GODOT_CPP_PATH}/bin)
add_library(libopenshot_godot SHARED
register_types.cpp
osg_timeline.cpp
)
target_link_libraries(libopenshot_godot
${GODOT_CPP_PATH}/bin/libgodot-cpp.linux.template_debug.x86_64.a
openshot
)
set_target_properties(libopenshot_godot PROPERTIES
OUTPUT_NAME "libopenshot_godot"
PREFIX ""
)

View File

@@ -0,0 +1,7 @@
[configuration]
entry_symbol = "example_library_init"
compatibility_minimum = "4.1"
[libraries]
linux.debug.x86_64 = "res://bin/libopenshot_godot.so"

View File

@@ -0,0 +1,104 @@
/**
* @file
* @brief Source file for Godot wrapper
* @author Jonathan Thomas <jonathan@openshot.org>
*
* @ref License
*/
// Copyright (c) 2008-2025 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "osg_timeline.h"
#include "FFmpegReader.h"
#include "Profiles.h"
#include "Timeline.h"
#include "godot_cpp/classes/image.hpp"
#include "godot_cpp/classes/image_texture.hpp"
#include "godot_cpp/variant/utility_functions.hpp"
#include "godot_cpp/core/class_db.hpp"
using namespace godot;
void ExampleClass::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_file", "path"), &ExampleClass::load_file);
ClassDB::bind_method(D_METHOD("print_type", "variant"), &ExampleClass::print_type);
ClassDB::bind_method(D_METHOD("print_json"), &ExampleClass::print_json);
ClassDB::bind_method(D_METHOD("get_image", "frame_number"), &ExampleClass::get_image);
}
ExampleClass::ExampleClass() {
constructor_called = true;
print_line("Constructor called!");
// Create example timeline
timeline = new openshot::Timeline(
1920, 1080,
openshot::Fraction(30, 1),
44100, 2,
openshot::LAYOUT_STEREO);
print_line("Timeline instantiated!");
}
ExampleClass::~ExampleClass() {
print_line("Destructor called!");
delete timeline;
timeline = nullptr;
delete reader;
reader = nullptr;
}
void ExampleClass::load_file(const String path) {
if (reader == nullptr)
{
// Create example reader
reader = new openshot::FFmpegReader(path.utf8().get_data(), true);
reader->Open();
}
}
void ExampleClass::print_type(const Variant &p_variant) const {
print_line(vformat("Type: %d", p_variant.get_type()));
}
void ExampleClass::print_json(const Variant &p_variant) {
print_line("print_json!");
openshot::Profile p("/home/jonathan/apps/openshot-qt/src/profiles/01920x1080p2997_16-09");
std::string s = timeline->Json();
String output = "OpenShot Profile JSON: " + String(s.c_str());
UtilityFunctions::print(output);
}
Ref<Image> ExampleClass::get_image(const int64_t frame_number) {
if (reader && reader->IsOpen())
{
// Load video frame
auto frame = reader->GetFrame(frame_number);
std::shared_ptr<QImage> qimg = frame->GetImage();
// Convert ARGB32_Premultiplied to RGBA8888, keeping premultiplied alpha
QImage rgba_image = qimg->convertToFormat(QImage::Format_RGBA8888);
// Copy pixel data
int width = rgba_image.width();
int height = rgba_image.height();
PackedByteArray buffer;
buffer.resize(width * height * 4);
memcpy(buffer.ptrw(), rgba_image.constBits(), buffer.size());
// Create Godot Image
Ref<Image> image = Image::create(width, height, false, Image::FORMAT_RGBA8);
image->set_data(width, height, false, Image::FORMAT_RGBA8, buffer);
print_line(vformat("✅ Image created: %dx%d (premultiplied alpha)", width, height));
return image;
}
// Empty image
return Ref<Image>();
}

View File

@@ -0,0 +1,42 @@
/**
* @file
* @brief Header file for Godot wrapper
* @author Jonathan Thomas <jonathan@openshot.org>
*
* @ref License
*/
// Copyright (c) 2008-2025 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "godot_cpp/classes/ref_counted.hpp"
#include "godot_cpp/classes/image.hpp"
#include "godot_cpp/variant/variant.hpp"
#include "Timeline.h"
#include "FFmpegReader.h"
using namespace godot;
class ExampleClass : public RefCounted {
GDCLASS(ExampleClass, RefCounted)
protected:
static void _bind_methods();
public:
ExampleClass();
~ExampleClass() override;
void load_file(String path);
void print_type(const Variant &p_variant) const;
void print_json(const Variant &p_variant);
Ref<Image> get_image(int64_t frame_number);
private:
openshot::Timeline* timeline = nullptr;
openshot::FFmpegReader* reader = nullptr;
bool constructor_called = false;
};

View File

@@ -0,0 +1,48 @@
/**
* @file
* @brief Source file for registering Godot wrapper
* @author Jonathan Thomas <jonathan@openshot.org>
*
* @ref License
*/
// Copyright (c) 2008-2025 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "register_types.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include "osg_timeline.h"
using namespace godot;
void initialize_gdextension_types(godot::ModuleInitializationLevel p_level) {
if (p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE) return;
godot::ClassDB::register_class<ExampleClass>();
}
void uninitialize_gdextension_types(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C"
{
// Initialization
GDExtensionBool GDE_EXPORT example_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.register_terminator(uninitialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}

View File

@@ -0,0 +1,19 @@
/**
* @file
* @brief Header file for registering Godot wrapper
* @author Jonathan Thomas <jonathan@openshot.org>
*
* @ref License
*/
// Copyright (c) 2008-2025 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef EXAMPLE_REGISTER_TYPES_H
#define EXAMPLE_REGISTER_TYPES_H
void initialize_gdextension_types();
void uninitialize_gdextension_types();
#endif // EXAMPLE_REGISTER_TYPES_H

1
external/godot-cpp vendored Submodule

Submodule external/godot-cpp added at 6388e26dd8