diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 83a47537..42a2ca75 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/bindings/godot/CMakeLists.txt b/bindings/godot/CMakeLists.txt new file mode 100644 index 00000000..4c6cc314 --- /dev/null +++ b/bindings/godot/CMakeLists.txt @@ -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 "" +) diff --git a/bindings/godot/libopenshot_godot.gdextension b/bindings/godot/libopenshot_godot.gdextension new file mode 100644 index 00000000..67342f60 --- /dev/null +++ b/bindings/godot/libopenshot_godot.gdextension @@ -0,0 +1,7 @@ +[configuration] + +entry_symbol = "example_library_init" +compatibility_minimum = "4.1" + +[libraries] +linux.debug.x86_64 = "res://bin/libopenshot_godot.so" \ No newline at end of file diff --git a/bindings/godot/osg_timeline.cpp b/bindings/godot/osg_timeline.cpp new file mode 100644 index 00000000..0b358b36 --- /dev/null +++ b/bindings/godot/osg_timeline.cpp @@ -0,0 +1,104 @@ +/** +* @file + * @brief Source file for Godot wrapper + * @author Jonathan Thomas + * + * @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 ExampleClass::get_image(const int64_t frame_number) { + if (reader && reader->IsOpen()) + { + // Load video frame + auto frame = reader->GetFrame(frame_number); + std::shared_ptr 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::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(); +} + + diff --git a/bindings/godot/osg_timeline.h b/bindings/godot/osg_timeline.h new file mode 100644 index 00000000..10ebca17 --- /dev/null +++ b/bindings/godot/osg_timeline.h @@ -0,0 +1,42 @@ +/** +* @file + * @brief Header file for Godot wrapper + * @author Jonathan Thomas + * + * @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 get_image(int64_t frame_number); + +private: + openshot::Timeline* timeline = nullptr; + openshot::FFmpegReader* reader = nullptr; + bool constructor_called = false; +}; diff --git a/bindings/godot/register_types.cpp b/bindings/godot/register_types.cpp new file mode 100644 index 00000000..9665be37 --- /dev/null +++ b/bindings/godot/register_types.cpp @@ -0,0 +1,48 @@ +/** +* @file + * @brief Source file for registering Godot wrapper + * @author Jonathan Thomas + * + * @ref License + */ + +// Copyright (c) 2008-2025 OpenShot Studios, LLC +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "register_types.h" + +#include +#include +#include +#include + +#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(); +} + +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(); + } +} \ No newline at end of file diff --git a/bindings/godot/register_types.h b/bindings/godot/register_types.h new file mode 100644 index 00000000..b5027125 --- /dev/null +++ b/bindings/godot/register_types.h @@ -0,0 +1,19 @@ +/** +* @file + * @brief Header file for registering Godot wrapper + * @author Jonathan Thomas + * + * @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 \ No newline at end of file diff --git a/external/godot-cpp b/external/godot-cpp new file mode 160000 index 00000000..6388e26d --- /dev/null +++ b/external/godot-cpp @@ -0,0 +1 @@ +Subproject commit 6388e26dd8a42071f65f764a3ef3d9523dda3d6e