diff --git a/INSTALL.md b/INSTALL.md
index 561a011a9..d99f59c09 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -50,7 +50,7 @@ Libraries and executables have been labeled in the list below to help distinguis
#### Qt 5 (libqt5)
* **(Library)**
-* Qt5 is used to display video, store image data, composite images,
+* Qt5/Qt6 is used to display video, store image data, composite images,
apply image effects, and many other utility functions,
such as file system manipulation, high resolution timers, etc.
@@ -201,6 +201,7 @@ Following are some of the flags you might need to set when generating your build
* `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/`
* `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered)
* `-DENABLE_MAGICK=0` (default: auto if discovered)
+* `-DUSE_QT6=AUTO|ON|OFF` (default: `AUTO`; prefers Qt6 when available and CMake ≥3.16, `ON` forces Qt6, `OFF` forces Qt5)
#### Options to compile bindings for a specific Python installation
* `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/`
diff --git a/bindings/java/openshot.i b/bindings/java/openshot.i
index 7aaf7d1e2..0e41ccf19 100644
--- a/bindings/java/openshot.i
+++ b/bindings/java/openshot.i
@@ -27,6 +27,10 @@
%include "std_vector.i"
%include "std_map.i"
%include
+%apply uint64_t { uintptr_t };
+
+// Ignore QWidget overloads (Qt types are not wrapped in Java bindings)
+%ignore openshot::QtPlayer::SetQWidget(QWidget *);
/* Unhandled STL Exception Handling */
%include
@@ -52,6 +56,34 @@
#%template() std::vector>;
%template() std::vector>;
+%inline %{
+typedef struct OpenShotByteBuffer {
+ const unsigned char* data;
+ int size;
+} OpenShotByteBuffer;
+%}
+
+%typemap(jni) OpenShotByteBuffer "jbyteArray"
+%typemap(jstype) OpenShotByteBuffer "byte[]"
+%typemap(jtype) OpenShotByteBuffer "byte[]"
+%typemap(javaout) OpenShotByteBuffer {
+ return $jnicall;
+}
+%typemap(out) OpenShotByteBuffer {
+ if ($1.data && $1.size > 0) {
+ jbyteArray jarr = jenv->NewByteArray($1.size);
+ if (jarr == NULL) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "Unable to allocate byte array");
+ return NULL;
+ }
+ jenv->SetByteArrayRegion(jarr, 0, $1.size,
+ reinterpret_cast($1.data));
+ $result = jarr;
+ } else {
+ $result = NULL;
+ }
+}
+
%{
#include "OpenShotVersion.h"
#include "ReaderBase.h"
@@ -118,6 +150,45 @@
/* Deprecated */
%template(AudioDeviceInfoVector) std::vector;
+%extend openshot::Frame {
+ OpenShotByteBuffer GetPixelsBytes() {
+ OpenShotByteBuffer out = {NULL, 0};
+ std::shared_ptr img = $self->GetImage();
+ if (!img) return out;
+
+ const int size = img->bytesPerLine() * img->height();
+
+ const unsigned char* p = $self->GetPixels();
+ if (!p || size <= 0) return out;
+
+ out.data = p;
+ out.size = size;
+ return out;
+ }
+
+ OpenShotByteBuffer GetPixelsRowBytes(int row) {
+ OpenShotByteBuffer out = {NULL, 0};
+ std::shared_ptr img = $self->GetImage();
+ if (!img) return out;
+
+ if (row < 0 || row >= img->height()) {
+ return out;
+ }
+
+ const unsigned char* p = $self->GetPixels(row);
+ if (!p) return out;
+
+ out.data = p;
+ out.size = img->bytesPerLine();
+ return out;
+ }
+
+ int GetBytesPerLine() {
+ std::shared_ptr img = $self->GetImage();
+ return img ? img->bytesPerLine() : 0;
+ }
+}
+
%include "OpenShotVersion.h"
%include "ReaderBase.h"
%include "WriterBase.h"
diff --git a/bindings/python/openshot.i b/bindings/python/openshot.i
index 612bfb11d..ac932de4a 100644
--- a/bindings/python/openshot.i
+++ b/bindings/python/openshot.i
@@ -27,6 +27,9 @@
%include "std_vector.i"
%include "std_map.i"
%include
+%apply uint64_t { uintptr_t };
+
+class QWidget;
/* Unhandled STL Exception Handling */
%include
@@ -34,6 +37,8 @@
/* Include shared pointer code */
%include
+%typemap(in) QWidget *;
+
/* Mark these classes as shared_ptr classes */
#ifdef USE_IMAGEMAGICK
%shared_ptr(Magick::Image)
@@ -98,6 +103,100 @@
#include "Timeline.h"
#include "Qt/VideoCacheThread.h"
#include "ZmqLogger.h"
+#include
+
+static void *openshot_swig_pylong_as_ptr(PyObject *obj) {
+ if (!obj) {
+ return nullptr;
+ }
+
+ unsigned long long ull = PyLong_AsUnsignedLongLong(obj);
+ if (!PyErr_Occurred()) {
+ return reinterpret_cast(static_cast(ull));
+ }
+ PyErr_Clear();
+
+ long long ll = PyLong_AsLongLong(obj);
+ if (!PyErr_Occurred()) {
+ return reinterpret_cast(static_cast(ll));
+ }
+ PyErr_Clear();
+
+ return nullptr;
+}
+
+static void *openshot_swig_get_qwidget_ptr(PyObject *obj) {
+ if (!obj || obj == Py_None) {
+ return nullptr;
+ }
+
+ if (PyLong_Check(obj)) {
+ void *ptr = openshot_swig_pylong_as_ptr(obj);
+ return ptr;
+ }
+
+ const char *sip_modules[] = {"sip", "PyQt6.sip", "PyQt5.sip"};
+ for (size_t i = 0; i < (sizeof(sip_modules) / sizeof(sip_modules[0])); ++i) {
+ PyObject *mod = PyImport_ImportModule(sip_modules[i]);
+ if (!mod) {
+ PyErr_Clear();
+ continue;
+ }
+ PyObject *unwrap = PyObject_GetAttrString(mod, "unwrapinstance");
+ if (unwrap && PyCallable_Check(unwrap)) {
+ PyObject *addr = PyObject_CallFunctionObjArgs(unwrap, obj, NULL);
+ if (addr) {
+ void *ptr = openshot_swig_pylong_as_ptr(addr);
+ Py_DECREF(addr);
+ if (ptr) {
+ Py_DECREF(unwrap);
+ Py_DECREF(mod);
+ return ptr;
+ }
+ }
+ }
+ Py_XDECREF(unwrap);
+ Py_DECREF(mod);
+ }
+
+ const char *shiboken_modules[] = {"shiboken6", "shiboken2"};
+ for (size_t i = 0; i < (sizeof(shiboken_modules) / sizeof(shiboken_modules[0])); ++i) {
+ PyObject *mod = PyImport_ImportModule(shiboken_modules[i]);
+ if (!mod) {
+ PyErr_Clear();
+ continue;
+ }
+ PyObject *get_ptr = PyObject_GetAttrString(mod, "getCppPointer");
+ if (get_ptr && PyCallable_Check(get_ptr)) {
+ PyObject *ptrs = PyObject_CallFunctionObjArgs(get_ptr, obj, NULL);
+ if (ptrs) {
+ PyObject *addr = ptrs;
+ if (PyTuple_Check(ptrs) && PyTuple_Size(ptrs) > 0) {
+ addr = PyTuple_GetItem(ptrs, 0);
+ }
+ void *ptr = openshot_swig_pylong_as_ptr(addr);
+ Py_DECREF(ptrs);
+ if (ptr) {
+ Py_DECREF(get_ptr);
+ Py_DECREF(mod);
+ return ptr;
+ }
+ }
+ }
+ Py_XDECREF(get_ptr);
+ Py_DECREF(mod);
+ }
+
+ return nullptr;
+}
+
+static int openshot_swig_is_qwidget(PyObject *obj) {
+ void *ptr = openshot_swig_get_qwidget_ptr(obj);
+ if (ptr) {
+ return 1;
+ }
+ return obj == Py_None ? 1 : 0;
+}
%}
@@ -138,6 +237,22 @@
}
}
+%typemap(in) QWidget * {
+ void *ptr = openshot_swig_get_qwidget_ptr($input);
+ if (!ptr && $input != Py_None) {
+ SWIG_exception_fail(SWIG_TypeError, "Expected QWidget or Qt binding widget");
+ }
+ $1 = reinterpret_cast(ptr);
+}
+
+%typemap(typecheck) QWidget * {
+ $1 = openshot_swig_is_qwidget($input);
+}
+
+%typemap(out) uintptr_t openshot::QtPlayer::GetRendererQObject {
+ $result = PyLong_FromLongLong((long long)(intptr_t)$1);
+}
+
/* Wrap std templates (list, vector, etc...) */
@@ -284,6 +399,74 @@
}
}
+%extend openshot::Frame {
+ PyObject* GetPixelsBytes() {
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ PyObject* result = NULL;
+
+ std::shared_ptr img = $self->GetImage();
+ if (!img) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ const Py_ssize_t size =
+ static_cast(img->bytesPerLine()) *
+ static_cast(img->height());
+
+ const unsigned char* p = img->constBits();
+ if (!p || size <= 0) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ result = PyBytes_FromStringAndSize(reinterpret_cast(p), size);
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ PyObject* GetPixelsRowBytes(int row) {
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ PyObject* result = NULL;
+
+ std::shared_ptr img = $self->GetImage();
+ if (!img) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ if (row < 0 || row >= img->height()) {
+ PyErr_SetString(PyExc_IndexError, "row out of range");
+ PyGILState_Release(gstate);
+ return NULL;
+ }
+
+ const unsigned char* p = img->constScanLine(row);
+ if (!p) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ const Py_ssize_t row_bytes = static_cast(img->bytesPerLine());
+ result = PyBytes_FromStringAndSize(reinterpret_cast(p), row_bytes);
+ PyGILState_Release(gstate);
+ return result;
+ }
+
+ int GetBytesPerLine() {
+ std::shared_ptr img = $self->GetImage();
+ return img ? img->bytesPerLine() : 0;
+ }
+}
+
%include "OpenShotVersion.h"
%include "ReaderBase.h"
%include "WriterBase.h"
diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i
index 655241be6..dc5b54a6a 100644
--- a/bindings/ruby/openshot.i
+++ b/bindings/ruby/openshot.i
@@ -27,6 +27,10 @@
%include "std_vector.i"
%include "std_map.i"
%include
+%apply uint64_t { uintptr_t };
+
+// Ignore QWidget overloads (Qt types are not wrapped in Ruby bindings)
+%ignore openshot::QtPlayer::SetQWidget(QWidget *);
/* Unhandled STL Exception Handling */
%include
@@ -52,6 +56,21 @@
%template() std::vector>;
%template() std::vector>;
+%inline %{
+typedef struct OpenShotByteBuffer {
+ const unsigned char* data;
+ int size;
+} OpenShotByteBuffer;
+%}
+
+%typemap(out) OpenShotByteBuffer {
+ if ($1.data && $1.size > 0) {
+ $result = rb_str_new(reinterpret_cast($1.data), $1.size);
+ } else {
+ $result = Qnil;
+ }
+}
+
%{
/* Ruby and FFmpeg define competing RSHIFT macros,
* so we move Ruby's out of the way for now. We'll
@@ -136,6 +155,45 @@
/* Deprecated */
%template(AudioDeviceInfoVector) std::vector;
+%extend openshot::Frame {
+ OpenShotByteBuffer GetPixelsBytes() {
+ OpenShotByteBuffer out = {NULL, 0};
+ std::shared_ptr img = $self->GetImage();
+ if (!img) return out;
+
+ const int size = img->bytesPerLine() * img->height();
+
+ const unsigned char* p = $self->GetPixels();
+ if (!p || size <= 0) return out;
+
+ out.data = p;
+ out.size = size;
+ return out;
+ }
+
+ OpenShotByteBuffer GetPixelsRowBytes(int row) {
+ OpenShotByteBuffer out = {NULL, 0};
+ std::shared_ptr img = $self->GetImage();
+ if (!img) return out;
+
+ if (row < 0 || row >= img->height()) {
+ rb_raise(rb_eIndexError, "row out of range");
+ }
+
+ const unsigned char* p = $self->GetPixels(row);
+ if (!p) return out;
+
+ out.data = p;
+ out.size = img->bytesPerLine();
+ return out;
+ }
+
+ int GetBytesPerLine() {
+ std::shared_ptr img = $self->GetImage();
+ return img ? img->bytesPerLine() : 0;
+ }
+}
+
%include "OpenShotVersion.h"
%include "ReaderBase.h"
%include "WriterBase.h"
@@ -222,4 +280,3 @@
%include "effects/Shift.h"
%include "effects/Wave.h"
-
diff --git a/doc/INSTALL-LINUX.md b/doc/INSTALL-LINUX.md
index fcc0a21a3..d297f038c 100644
--- a/doc/INSTALL-LINUX.md
+++ b/doc/INSTALL-LINUX.md
@@ -44,7 +44,8 @@ list below to help distinguish between them.
### Qt 5 (libqt5)
* http://www.qt.io/qt5/ `(Library)`
- * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Qt5/Qt6 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Use the CMake option `-DUSE_QT6=ON|OFF|AUTO` (default AUTO) to pick a Qt major version; Qt6 builds require CMake 3.16+.
### CMake (cmake)
* http://www.cmake.org/ `(Executable)`
@@ -163,9 +164,19 @@ software packages available to download and install.
python3-dev \
qtbase5-dev \
qtmultimedia5-dev \
- swig
+ swig \
+ python3-zmq \
+ python3-pyqt5.qtwebengine
+
```
+If you want to build against Qt6 on Ubuntu 24.04 or newer, install the Qt6 dev stack instead and configure with the default `USE_QT6=AUTO` (prefers Qt6 when available) or `-DUSE_QT6=ON` to force it:
+
+```
+sudo apt install qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-svg-dev
+```
+
+
## Linux Build Instructions (libopenshot-audio)
To compile libopenshot-audio, we need to go through a few additional steps to manually build and
install it. Launch a terminal and enter:
diff --git a/doc/INSTALL-MAC.md b/doc/INSTALL-MAC.md
index 7e5665287..183603207 100644
--- a/doc/INSTALL-MAC.md
+++ b/doc/INSTALL-MAC.md
@@ -44,7 +44,8 @@ list below to help distinguish between them.
### Qt 5 (libqt5)
* http://www.qt.io/qt5/ `(Library)`
- * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Qt5/Qt6 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Use the CMake option `-DUSE_QT6=ON|OFF|AUTO` (default AUTO) to pick a Qt major version; Qt6 builds require CMake 3.16+.
### CMake (cmake)
* http://www.cmake.org/ `(Executable)`
diff --git a/doc/INSTALL-WINDOWS.md b/doc/INSTALL-WINDOWS.md
index 4f04cb62c..2fe1e464a 100644
--- a/doc/INSTALL-WINDOWS.md
+++ b/doc/INSTALL-WINDOWS.md
@@ -46,7 +46,8 @@ have been labeled in the list below to help distinguish between them.
### Qt 5 (libqt5)
* http://www.qt.io/qt5/ `(Library)`
- * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Qt5/Qt6 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+ * Use the CMake option `-DUSE_QT6=ON|OFF|AUTO` (default AUTO) to pick a Qt major version; Qt6 builds require CMake 3.16+.
### CMake (cmake)
* http://www.cmake.org/ `(Executable)`
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 12b5447e8..f36ea6fe9 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -11,8 +11,45 @@
include(GNUInstallDirs)
-# Dependencies
-find_package(Qt5 COMPONENTS Gui REQUIRED)
+# Qt selection matches the main library (USE_QT6 option, CMake 3.16+ needed for Qt6)
+set(_qt6_allowed TRUE)
+if(CMAKE_VERSION VERSION_LESS "3.16")
+ set(_qt6_allowed FALSE)
+ if(USE_QT6 STREQUAL "ON")
+ message(FATAL_ERROR "USE_QT6=ON requires CMake 3.16 or newer")
+ endif()
+endif()
+if(USE_QT6 STREQUAL "OFF")
+ set(_qt6_allowed FALSE)
+endif()
+
+set(_qt_selection "")
+if(USE_QT6 STREQUAL "ON")
+ if(NOT _qt6_allowed)
+ message(FATAL_ERROR "Qt6 support disabled by CMake version; set USE_QT6=OFF or upgrade CMake")
+ endif()
+ set(_qt_selection "Qt6")
+elseif(USE_QT6 STREQUAL "OFF")
+ set(_qt_selection "Qt5")
+else()
+ if(_qt6_allowed)
+ find_package(Qt6 COMPONENTS Core QUIET)
+ if(Qt6_FOUND)
+ set(_qt_selection "Qt6")
+ endif()
+ endif()
+ if(NOT _qt_selection)
+ set(_qt_selection "Qt5")
+ endif()
+endif()
+
+if(_qt_selection STREQUAL "Qt6")
+ set(QT_VERSION_MAJOR 6)
+else()
+ set(QT_VERSION_MAJOR 5)
+endif()
+
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui REQUIRED)
############### CLI EXECUTABLES ################
# Create test executable
@@ -27,7 +64,7 @@ target_compile_definitions(openshot-example PRIVATE
target_link_libraries(openshot-example openshot)
add_executable(openshot-html-example ExampleHtml.cpp)
-target_link_libraries(openshot-html-example openshot Qt5::Gui)
+target_link_libraries(openshot-html-example openshot Qt${QT_VERSION_MAJOR}::Gui)
############### PLAYER EXECUTABLE ################
# Create test executable
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6e22574c5..7c249bf69 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -314,6 +314,9 @@ endif()
### Qt Toolkit
###
+set(USE_QT6 "AUTO" CACHE STRING "Select Qt major version (AUTO, ON=Qt6, OFF=Qt5)")
+set_property(CACHE USE_QT6 PROPERTY STRINGS AUTO ON OFF)
+
set(_qt_components Core Gui Widgets)
# We also need QtSvg unless we have Resvg insetead.
@@ -321,16 +324,57 @@ if(NOT HAVE_RESVG)
list(APPEND _qt_components Svg)
endif()
-find_package(Qt5 COMPONENTS ${_qt_components} REQUIRED)
+# Qt6 packages require newer CMake; gate support so older systems still build Qt5.
+set(_qt6_allowed TRUE)
+if(CMAKE_VERSION VERSION_LESS "3.16")
+ set(_qt6_allowed FALSE)
+ if(USE_QT6 STREQUAL "ON")
+ message(FATAL_ERROR "USE_QT6=ON requires CMake 3.16 or newer")
+ endif()
+endif()
+if(USE_QT6 STREQUAL "OFF")
+ set(_qt6_allowed FALSE)
+endif()
+
+set(_qt_selection "")
+if(USE_QT6 STREQUAL "ON")
+ if(NOT _qt6_allowed)
+ message(FATAL_ERROR "Qt6 support disabled by CMake version; set USE_QT6=OFF or upgrade CMake")
+ endif()
+ set(_qt_selection "Qt6")
+elseif(USE_QT6 STREQUAL "OFF")
+ set(_qt_selection "Qt5")
+else()
+ if(_qt6_allowed)
+ find_package(Qt6 COMPONENTS Core QUIET)
+ if(Qt6_FOUND)
+ set(_qt_selection "Qt6")
+ endif()
+ endif()
+ if(NOT _qt_selection)
+ set(_qt_selection "Qt5")
+ endif()
+endif()
+
+if(_qt_selection STREQUAL "Qt6")
+ set(QT_VERSION_MAJOR 6)
+else()
+ set(QT_VERSION_MAJOR 5)
+endif()
+
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${_qt_components} REQUIRED)
foreach(_qt_comp IN LISTS _qt_components)
- if(TARGET Qt5::${_qt_comp})
- target_link_libraries(openshot PUBLIC Qt5::${_qt_comp})
+ if(TARGET Qt${QT_VERSION_MAJOR}::${_qt_comp})
+ target_link_libraries(openshot PUBLIC Qt${QT_VERSION_MAJOR}::${_qt_comp})
endif()
endforeach()
+string(REPLACE ";" ", " _qt_comp_list "${_qt_components}")
+message(STATUS "Found Qt${QT_VERSION_MAJOR} ${Qt${QT_VERSION_MAJOR}Core_VERSION_STRING} (components: ${_qt_comp_list})")
+
# Keep track of Qt version, to embed in our version header
-set(QT_VERSION_STR ${Qt5Core_VERSION_STRING} CACHE STRING "Qt version linked with" FORCE)
+set(QT_VERSION_STR ${Qt${QT_VERSION_MAJOR}Core_VERSION_STRING} CACHE STRING "Qt version linked with" FORCE)
mark_as_advanced(QT_VERSION_STR)
################### FFMPEG #####################
diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp
index f0514e766..5e2095944 100644
--- a/src/Qt/PlayerDemo.cpp
+++ b/src/Qt/PlayerDemo.cpp
@@ -43,7 +43,7 @@ PlayerDemo::PlayerDemo(QWidget *parent)
vbox->addWidget(menu, 0);
vbox->addWidget(video, 1);
- vbox->setMargin(0);
+ vbox->setContentsMargins(0, 0, 0, 0);
vbox->setSpacing(0);
resize(600, 480);
diff --git a/src/Qt/VideoRenderer.cpp b/src/Qt/VideoRenderer.cpp
index f835d6ec6..ff526e48c 100644
--- a/src/Qt/VideoRenderer.cpp
+++ b/src/Qt/VideoRenderer.cpp
@@ -23,7 +23,7 @@ VideoRenderer::~VideoRenderer()
}
/// Override QWidget which needs to be painted
-void VideoRenderer::OverrideWidget(int64_t qwidget_address)
+void VideoRenderer::OverrideWidget(uintptr_t qwidget_address)
{
// re-cast QWidget pointer (long) as an actual QWidget
override_widget = reinterpret_cast(qwidget_address);
diff --git a/src/Qt/VideoRenderer.h b/src/Qt/VideoRenderer.h
index 21c6d9465..968fb6625 100644
--- a/src/Qt/VideoRenderer.h
+++ b/src/Qt/VideoRenderer.h
@@ -14,6 +14,7 @@
#define OPENSHOT_VIDEO_RENDERER_H
#include "../RendererBase.h"
+#include
#include
#include
#include
@@ -30,7 +31,7 @@ class VideoRenderer : public QObject, public openshot::RendererBase
~VideoRenderer();
/// Override QWidget which needs to be painted
- void OverrideWidget(int64_t qwidget_address);
+ void OverrideWidget(uintptr_t qwidget_address);
signals:
void present(const QImage &image);
diff --git a/src/QtPlayer.cpp b/src/QtPlayer.cpp
index d015d3a3e..174687c93 100644
--- a/src/QtPlayer.cpp
+++ b/src/QtPlayer.cpp
@@ -211,15 +211,22 @@ namespace openshot
return reader;
}
- // Set the QWidget pointer to display the video on (as a LONG pointer id)
- void QtPlayer::SetQWidget(int64_t qwidget_address) {
+ // Set the QWidget pointer to display the video on (as a pointer-sized unsigned id)
+ void QtPlayer::SetQWidget(uintptr_t qwidget_address) {
// Update override QWidget address on the video renderer
p->renderer->OverrideWidget(qwidget_address);
}
- // Get the Renderer pointer address (for Python to cast back into a QObject)
- int64_t QtPlayer::GetRendererQObject() {
- return (int64_t)(VideoRenderer*)p->renderer;
+ // Set the QWidget pointer to display the video on (using a real QWidget pointer)
+ void QtPlayer::SetQWidget(QWidget *widget) {
+ SetQWidget(reinterpret_cast(widget));
+ }
+
+ // Get the Renderer pointer address (for Python to cast back into a VideoRenderer)
+ uintptr_t QtPlayer::GetRendererQObject() {
+ auto* vr = static_cast(p->renderer);
+ uintptr_t addr = reinterpret_cast(vr);
+ return addr;
}
// Get the Playback speed
@@ -247,3 +254,9 @@ namespace openshot
volume = new_volume;
}
}
+#ifdef __ANDROID__
+#include
+#ifndef ANDROID_LOG_WARN
+#define ANDROID_LOG_WARN 5
+#endif
+#endif
diff --git a/src/QtPlayer.h b/src/QtPlayer.h
index 2f7ba7698..71c7df2c1 100644
--- a/src/QtPlayer.h
+++ b/src/QtPlayer.h
@@ -14,6 +14,7 @@
#ifndef OPENSHOT_QT_PLAYER_H
#define OPENSHOT_QT_PLAYER_H
+#include
#include
#include
@@ -21,6 +22,8 @@
#include "Qt/PlayerPrivate.h"
#include "RendererBase.h"
+class QWidget;
+
namespace openshot
{
using AudioDeviceList = std::vector>;
@@ -82,12 +85,15 @@ namespace openshot
void SetTimelineSource(const std::string &json);
/// Set the QWidget which will be used as the display (note: QLabel works well). This does not take a
- /// normal pointer, but rather a LONG pointer id (and it re-casts the QWidget pointer inside libopenshot).
+ /// normal pointer, but rather a pointer-sized unsigned integer (and it re-casts the QWidget pointer inside libopenshot).
/// This is required due to SIP and SWIG incompatibility in the Python bindings.
- void SetQWidget(int64_t qwidget_address);
+ void SetQWidget(uintptr_t qwidget_address);
+
+ /// Set the QWidget which will be used as the display (for SWIG/Python bindings)
+ void SetQWidget(QWidget *widget);
- /// Get the Renderer pointer address (for Python to cast back into a QObject)
- int64_t GetRendererQObject();
+ /// Get the Renderer pointer address (for Python to cast back into a VideoRenderer)
+ uintptr_t GetRendererQObject();
/// Get the Playback speed
float Speed();
diff --git a/src/RendererBase.h b/src/RendererBase.h
index a7e0363ec..49ed3cc31 100644
--- a/src/RendererBase.h
+++ b/src/RendererBase.h
@@ -14,6 +14,7 @@
#define OPENSHOT_RENDERER_BASE_H
#include "Frame.h"
+#include
#include // for realloc
#include
@@ -35,7 +36,7 @@ namespace openshot
void paint(const std::shared_ptr & frame);
/// Allow manual override of the QWidget that is used to display
- virtual void OverrideWidget(int64_t qwidget_address) = 0;
+ virtual void OverrideWidget(uintptr_t qwidget_address) = 0;
protected:
RendererBase();
diff --git a/src/effects/LensFlare.cpp b/src/effects/LensFlare.cpp
index e59b35cde..a8286b317 100644
--- a/src/effects/LensFlare.cpp
+++ b/src/effects/LensFlare.cpp
@@ -107,14 +107,19 @@ static QColor shifted_hsv(const QColor &base, float h_shift,
float s_scale, float v_scale,
float a_scale = 1.0f)
{
+ // Qt6 switched getHsvF/setHsvF to float; keep compatibility with Qt5 (qreal)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ float h, s, v, a;
+#else
qreal h, s, v, a;
+#endif
base.getHsvF(&h, &s, &v, &a);
if (s == 0.0)
h = 0.0;
- h = std::fmod(h + h_shift + 1.0, 1.0);
- s = std::clamp(s * s_scale, 0.0, 1.0);
- v = std::clamp(v * v_scale, 0.0, 1.0);
- a = std::clamp(a * a_scale, 0.0, 1.0);
+ h = static_cast(std::fmod(static_cast(h + h_shift + 1.0), 1.0));
+ s = std::clamp(s * s_scale, static_cast(0.0), static_cast(1.0));
+ v = std::clamp(v * v_scale, static_cast(0.0), static_cast(1.0));
+ a = std::clamp(a * a_scale, static_cast(0.0), static_cast(1.0));
QColor out;
out.setHsvF(h, s, v, a);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 50d71b3f4..2e7e72ab3 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -42,6 +42,7 @@ set(OPENSHOT_TESTS
KeyFrame
Point
Profiles
+ QtPlayer
QtImageReader
ReaderBase
Settings
diff --git a/tests/Caption.cpp b/tests/Caption.cpp
index bc5768f0c..96d49a445 100644
--- a/tests/Caption.cpp
+++ b/tests/Caption.cpp
@@ -63,7 +63,9 @@ TEST_CASE("caption effect", "[libopenshot][caption]") {
int argc = 1;
char* argv[1] = {(char*)""};
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#endif
QApplication app(argc, argv);
QApplication::processEvents();
@@ -202,4 +204,4 @@ TEST_CASE("caption effect", "[libopenshot][caption]") {
// Close QApplication
app.quit();
-}
\ No newline at end of file
+}
diff --git a/tests/QtPlayer.cpp b/tests/QtPlayer.cpp
new file mode 100644
index 000000000..1bc8fd5f6
--- /dev/null
+++ b/tests/QtPlayer.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file
+ * @brief Unit tests for openshot::QtPlayer
+ * @author OpenShot Studios, LLC
+ *
+ * @ref License
+ */
+
+// Copyright (c) 2008-2025 OpenShot Studios, LLC
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "openshot_catch.h"
+
+#include
+
+#include "QtPlayer.h"
+#include "Qt/VideoRenderer.h"
+
+class QWidget;
+
+namespace {
+class TestRenderer : public openshot::RendererBase
+{
+public:
+ uintptr_t last_widget = 0;
+
+ void OverrideWidget(uintptr_t qwidget_address) override
+ {
+ last_widget = qwidget_address;
+ }
+
+protected:
+ void render(std::shared_ptr image) override
+ {
+ (void) image;
+ }
+};
+} // namespace
+
+TEST_CASE("QtPlayer_GetRendererQObject_ReturnsVideoRendererAddress", "[libopenshot][qtplayer]")
+{
+ auto renderer = std::make_unique();
+ openshot::QtPlayer player(renderer.get());
+
+ auto addr = player.GetRendererQObject();
+ CHECK(addr == reinterpret_cast(renderer.get()));
+}
+
+TEST_CASE("QtPlayer_SetQWidget_Overload_ForwardsPointer", "[libopenshot][qtplayer]")
+{
+ TestRenderer renderer;
+ openshot::QtPlayer player(&renderer);
+
+ char dummy = 0;
+ auto *widget = reinterpret_cast(&dummy);
+ player.SetQWidget(widget);
+
+ CHECK(renderer.last_widget == reinterpret_cast(widget));
+}