Skip to content

Commit 23b4e74

Browse files
committed
Improving swig bindings for Python, Java, and Ruby for access direct pixel byte array:
Example Code Snippets: Python: import openshot r = openshot.FFmpegReader("/home/jonathan/Pictures/ChatGPT Image Mar 27, 2025, 09_50_56 AM.png") r.Open() f = r.GetFrame(1) buf = f.GetPixelsBytes() w, h = f.GetWidth(), f.GetHeight() stride = f.GetBytesPerLine() Ruby: require "openshot" r = Openshot::FFmpegReader.new("/home/jonathan/Pictures/ChatGPT Image Mar 27, 2025, 09_50_56 AM.png") r.Open() f = r.GetFrame(1) buf = f.GetPixelsBytes w = f.GetWidth h = f.GetHeight stride = f.GetBytesPerLine
1 parent a26ed32 commit 23b4e74

3 files changed

Lines changed: 189 additions & 0 deletions

File tree

bindings/java/openshot.i

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,34 @@
5252
#%template() std::vector<std::pair<std::string, std::string>>;
5353
%template() std::vector<std::vector<float>>;
5454

55+
%inline %{
56+
typedef struct OpenShotByteBuffer {
57+
const unsigned char* data;
58+
int size;
59+
} OpenShotByteBuffer;
60+
%}
61+
62+
%typemap(jni) OpenShotByteBuffer "jbyteArray"
63+
%typemap(jstype) OpenShotByteBuffer "byte[]"
64+
%typemap(jtype) OpenShotByteBuffer "byte[]"
65+
%typemap(javaout) OpenShotByteBuffer {
66+
return $jnicall;
67+
}
68+
%typemap(out) OpenShotByteBuffer {
69+
if ($1.data && $1.size > 0) {
70+
jbyteArray jarr = jenv->NewByteArray($1.size);
71+
if (jarr == NULL) {
72+
SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "Unable to allocate byte array");
73+
return NULL;
74+
}
75+
jenv->SetByteArrayRegion(jarr, 0, $1.size,
76+
reinterpret_cast<const jbyte*>($1.data));
77+
$result = jarr;
78+
} else {
79+
$result = NULL;
80+
}
81+
}
82+
5583
%{
5684
#include "OpenShotVersion.h"
5785
#include "ReaderBase.h"
@@ -118,6 +146,45 @@
118146
/* Deprecated */
119147
%template(AudioDeviceInfoVector) std::vector<openshot::AudioDeviceInfo>;
120148

149+
%extend openshot::Frame {
150+
OpenShotByteBuffer GetPixelsBytes() {
151+
OpenShotByteBuffer out = {NULL, 0};
152+
std::shared_ptr<QImage> img = $self->GetImage();
153+
if (!img) return out;
154+
155+
const int size = img->bytesPerLine() * img->height();
156+
157+
const unsigned char* p = $self->GetPixels();
158+
if (!p || size <= 0) return out;
159+
160+
out.data = p;
161+
out.size = size;
162+
return out;
163+
}
164+
165+
OpenShotByteBuffer GetPixelsRowBytes(int row) {
166+
OpenShotByteBuffer out = {NULL, 0};
167+
std::shared_ptr<QImage> img = $self->GetImage();
168+
if (!img) return out;
169+
170+
if (row < 0 || row >= img->height()) {
171+
return out;
172+
}
173+
174+
const unsigned char* p = $self->GetPixels(row);
175+
if (!p) return out;
176+
177+
out.data = p;
178+
out.size = img->bytesPerLine();
179+
return out;
180+
}
181+
182+
int GetBytesPerLine() {
183+
std::shared_ptr<QImage> img = $self->GetImage();
184+
return img ? img->bytesPerLine() : 0;
185+
}
186+
}
187+
121188
%include "OpenShotVersion.h"
122189
%include "ReaderBase.h"
123190
%include "WriterBase.h"

bindings/python/openshot.i

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,74 @@
284284
}
285285
}
286286

287+
%extend openshot::Frame {
288+
PyObject* GetPixelsBytes() {
289+
PyGILState_STATE gstate = PyGILState_Ensure();
290+
PyObject* result = NULL;
291+
292+
std::shared_ptr<QImage> img = $self->GetImage();
293+
if (!img) {
294+
Py_INCREF(Py_None);
295+
result = Py_None;
296+
PyGILState_Release(gstate);
297+
return result;
298+
}
299+
300+
const Py_ssize_t size =
301+
static_cast<Py_ssize_t>(img->bytesPerLine()) *
302+
static_cast<Py_ssize_t>(img->height());
303+
304+
const unsigned char* p = img->constBits();
305+
if (!p || size <= 0) {
306+
Py_INCREF(Py_None);
307+
result = Py_None;
308+
PyGILState_Release(gstate);
309+
return result;
310+
}
311+
312+
result = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(p), size);
313+
PyGILState_Release(gstate);
314+
return result;
315+
}
316+
317+
PyObject* GetPixelsRowBytes(int row) {
318+
PyGILState_STATE gstate = PyGILState_Ensure();
319+
PyObject* result = NULL;
320+
321+
std::shared_ptr<QImage> img = $self->GetImage();
322+
if (!img) {
323+
Py_INCREF(Py_None);
324+
result = Py_None;
325+
PyGILState_Release(gstate);
326+
return result;
327+
}
328+
329+
if (row < 0 || row >= img->height()) {
330+
PyErr_SetString(PyExc_IndexError, "row out of range");
331+
PyGILState_Release(gstate);
332+
return NULL;
333+
}
334+
335+
const unsigned char* p = img->constScanLine(row);
336+
if (!p) {
337+
Py_INCREF(Py_None);
338+
result = Py_None;
339+
PyGILState_Release(gstate);
340+
return result;
341+
}
342+
343+
const Py_ssize_t row_bytes = static_cast<Py_ssize_t>(img->bytesPerLine());
344+
result = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(p), row_bytes);
345+
PyGILState_Release(gstate);
346+
return result;
347+
}
348+
349+
int GetBytesPerLine() {
350+
std::shared_ptr<QImage> img = $self->GetImage();
351+
return img ? img->bytesPerLine() : 0;
352+
}
353+
}
354+
287355
%include "OpenShotVersion.h"
288356
%include "ReaderBase.h"
289357
%include "WriterBase.h"

bindings/ruby/openshot.i

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@
5252
%template() std::vector<std::pair<std::string, std::string>>;
5353
%template() std::vector<std::vector<float>>;
5454

55+
%inline %{
56+
typedef struct OpenShotByteBuffer {
57+
const unsigned char* data;
58+
int size;
59+
} OpenShotByteBuffer;
60+
%}
61+
62+
%typemap(out) OpenShotByteBuffer {
63+
if ($1.data && $1.size > 0) {
64+
$result = rb_str_new(reinterpret_cast<const char*>($1.data), $1.size);
65+
} else {
66+
$result = Qnil;
67+
}
68+
}
69+
5570
%{
5671
/* Ruby and FFmpeg define competing RSHIFT macros,
5772
* so we move Ruby's out of the way for now. We'll
@@ -136,6 +151,45 @@
136151
/* Deprecated */
137152
%template(AudioDeviceInfoVector) std::vector<openshot::AudioDeviceInfo>;
138153

154+
%extend openshot::Frame {
155+
OpenShotByteBuffer GetPixelsBytes() {
156+
OpenShotByteBuffer out = {NULL, 0};
157+
std::shared_ptr<QImage> img = $self->GetImage();
158+
if (!img) return out;
159+
160+
const int size = img->bytesPerLine() * img->height();
161+
162+
const unsigned char* p = $self->GetPixels();
163+
if (!p || size <= 0) return out;
164+
165+
out.data = p;
166+
out.size = size;
167+
return out;
168+
}
169+
170+
OpenShotByteBuffer GetPixelsRowBytes(int row) {
171+
OpenShotByteBuffer out = {NULL, 0};
172+
std::shared_ptr<QImage> img = $self->GetImage();
173+
if (!img) return out;
174+
175+
if (row < 0 || row >= img->height()) {
176+
rb_raise(rb_eIndexError, "row out of range");
177+
}
178+
179+
const unsigned char* p = $self->GetPixels(row);
180+
if (!p) return out;
181+
182+
out.data = p;
183+
out.size = img->bytesPerLine();
184+
return out;
185+
}
186+
187+
int GetBytesPerLine() {
188+
std::shared_ptr<QImage> img = $self->GetImage();
189+
return img ? img->bytesPerLine() : 0;
190+
}
191+
}
192+
139193
%include "OpenShotVersion.h"
140194
%include "ReaderBase.h"
141195
%include "WriterBase.h"

0 commit comments

Comments
 (0)