You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Improving swig to wrap pointers as PyLong_AsVoidPtr - to avoid Android memory addresses overflowing as an int.
This commit is contained in:
@@ -27,6 +27,10 @@
|
||||
%include "std_vector.i"
|
||||
%include "std_map.i"
|
||||
%include <stdint.i>
|
||||
%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 <std_except.i>
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
%include "std_vector.i"
|
||||
%include "std_map.i"
|
||||
%include <stdint.i>
|
||||
%apply uint64_t { uintptr_t };
|
||||
|
||||
class QWidget;
|
||||
|
||||
/* Unhandled STL Exception Handling */
|
||||
%include <std_except.i>
|
||||
@@ -34,6 +37,8 @@
|
||||
/* Include shared pointer code */
|
||||
%include <std_shared_ptr.i>
|
||||
|
||||
%typemap(in) QWidget *;
|
||||
|
||||
/* Mark these classes as shared_ptr classes */
|
||||
#ifdef USE_IMAGEMAGICK
|
||||
%shared_ptr(Magick::Image)
|
||||
@@ -98,6 +103,91 @@
|
||||
#include "Timeline.h"
|
||||
#include "Qt/VideoCacheThread.h"
|
||||
#include "ZmqLogger.h"
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
static void *openshot_swig_pylong_as_ptr(PyObject *obj) {
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
void *ptr = PyLong_AsVoidPtr(obj);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
return nullptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *openshot_swig_get_qwidget_ptr(PyObject *obj) {
|
||||
if (!obj || obj == Py_None) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyLong_Check(obj)) {
|
||||
return openshot_swig_pylong_as_ptr(obj);
|
||||
}
|
||||
|
||||
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 +228,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<QWidget*>(ptr);
|
||||
}
|
||||
|
||||
%typemap(typecheck) QWidget * {
|
||||
$1 = openshot_swig_is_qwidget($input);
|
||||
}
|
||||
|
||||
%typemap(out) uintptr_t openshot::QtPlayer::GetRendererQObject {
|
||||
$result = PyLong_FromVoidPtr(reinterpret_cast<void *>($1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Wrap std templates (list, vector, etc...) */
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
%include "std_vector.i"
|
||||
%include "std_map.i"
|
||||
%include <stdint.i>
|
||||
%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 <std_except.i>
|
||||
@@ -276,4 +280,3 @@ typedef struct OpenShotByteBuffer {
|
||||
%include "effects/Shift.h"
|
||||
%include "effects/Wave.h"
|
||||
|
||||
|
||||
|
||||
@@ -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*>(qwidget_address);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define OPENSHOT_VIDEO_RENDERER_H
|
||||
|
||||
#include "../RendererBase.h"
|
||||
#include <cstdint>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/QImage>
|
||||
#include <memory>
|
||||
@@ -30,7 +31,7 @@ public:
|
||||
~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);
|
||||
|
||||
+11
-5
@@ -211,15 +211,21 @@ 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<uintptr_t>(widget));
|
||||
}
|
||||
|
||||
// Get the Renderer pointer address (for Python to cast back into a VideoRenderer)
|
||||
uintptr_t QtPlayer::GetRendererQObject() {
|
||||
auto* vr = static_cast<VideoRenderer*>(p->renderer);
|
||||
return reinterpret_cast<uintptr_t>(vr);
|
||||
}
|
||||
|
||||
// Get the Playback speed
|
||||
|
||||
+10
-4
@@ -14,6 +14,7 @@
|
||||
#ifndef OPENSHOT_QT_PLAYER_H
|
||||
#define OPENSHOT_QT_PLAYER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
@@ -21,6 +22,8 @@
|
||||
#include "Qt/PlayerPrivate.h"
|
||||
#include "RendererBase.h"
|
||||
|
||||
class QWidget;
|
||||
|
||||
namespace openshot
|
||||
{
|
||||
using AudioDeviceList = std::vector<std::pair<std::string, std::string>>;
|
||||
@@ -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);
|
||||
|
||||
/// Get the Renderer pointer address (for Python to cast back into a QObject)
|
||||
int64_t GetRendererQObject();
|
||||
/// 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 VideoRenderer)
|
||||
uintptr_t GetRendererQObject();
|
||||
|
||||
/// Get the Playback speed
|
||||
float Speed();
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@
|
||||
#define OPENSHOT_RENDERER_BASE_H
|
||||
|
||||
#include "Frame.h"
|
||||
#include <cstdint>
|
||||
#include <cstdlib> // for realloc
|
||||
#include <memory>
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace openshot
|
||||
void paint(const std::shared_ptr<openshot::Frame> & 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();
|
||||
|
||||
@@ -42,6 +42,7 @@ set(OPENSHOT_TESTS
|
||||
KeyFrame
|
||||
Point
|
||||
Profiles
|
||||
QtPlayer
|
||||
QtImageReader
|
||||
ReaderBase
|
||||
Settings
|
||||
|
||||
@@ -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 <memory>
|
||||
|
||||
#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<QImage> image) override
|
||||
{
|
||||
(void) image;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("QtPlayer_GetRendererQObject_ReturnsVideoRendererAddress", "[libopenshot][qtplayer]")
|
||||
{
|
||||
auto renderer = std::make_unique<VideoRenderer>();
|
||||
openshot::QtPlayer player(renderer.get());
|
||||
|
||||
auto addr = player.GetRendererQObject();
|
||||
CHECK(addr == reinterpret_cast<uintptr_t>(renderer.get()));
|
||||
}
|
||||
|
||||
TEST_CASE("QtPlayer_SetQWidget_Overload_ForwardsPointer", "[libopenshot][qtplayer]")
|
||||
{
|
||||
TestRenderer renderer;
|
||||
openshot::QtPlayer player(&renderer);
|
||||
|
||||
char dummy = 0;
|
||||
auto *widget = reinterpret_cast<QWidget*>(&dummy);
|
||||
player.SetQWidget(widget);
|
||||
|
||||
CHECK(renderer.last_widget == reinterpret_cast<uintptr_t>(widget));
|
||||
}
|
||||
Reference in New Issue
Block a user