Loading...
Searching...
No Matches
GStreamerLoader.hpp
1#pragma once
2#include <ossia/detail/dylib_loader.hpp>
3
4#include <cstddef>
5#include <cstdint>
6#include <string_view>
7#include <vector>
8
9// Forward declarations of GStreamer types (all opaque)
10extern "C"
11{
12typedef struct _GstElement GstElement;
13typedef struct _GstBus GstBus;
14typedef struct _GstMessage GstMessage;
15typedef struct _GstCaps GstCaps;
16typedef struct _GstStructure GstStructure;
17typedef struct _GstSample GstSample;
18typedef struct _GstBuffer GstBuffer;
19typedef struct _GstPad GstPad;
20typedef struct _GstIterator GstIterator;
21
22// Enums that are passed by value
23typedef enum
24{
25 GST_STATE_VOID_PENDING = 0,
26 GST_STATE_NULL = 1,
27 GST_STATE_READY = 2,
28 GST_STATE_PAUSED = 3,
29 GST_STATE_PLAYING = 4
30} GstState;
31
32typedef enum
33{
34 GST_STATE_CHANGE_FAILURE = 0,
35 GST_STATE_CHANGE_SUCCESS = 1,
36 GST_STATE_CHANGE_ASYNC = 2,
37 GST_STATE_CHANGE_NO_PREROLL = 3
38} GstStateChangeReturn;
39
40typedef enum
41{
42 GST_MESSAGE_UNKNOWN = 0,
43 GST_MESSAGE_EOS = (1 << 0),
44 GST_MESSAGE_ERROR = (1 << 1),
45 GST_MESSAGE_WARNING = (1 << 2),
46 GST_MESSAGE_INFO = (1 << 3),
47 GST_MESSAGE_STATE_CHANGED = (1 << 5),
48} GstMessageType;
49
50typedef enum
51{
52 GST_MAP_READ = (1 << 0),
53 GST_MAP_WRITE = (1 << 1),
54} GstMapFlags;
55
56typedef enum
57{
58 GST_FLOW_OK = 0,
59 GST_FLOW_EOS = -3,
60 GST_FLOW_ERROR = -5,
61} GstFlowReturn;
62
63// GstMapInfo must be fully defined since it's stack-allocated
64typedef struct _GstMapInfo
65{
66 void* memory;
67 GstMapFlags flags;
68 uint8_t* data;
69 size_t size;
70 size_t maxsize;
71 void* user_data[4];
72 void* _gst_reserved[4];
74
75// GError for error handling
76typedef struct _GError
77{
78 uint32_t domain;
79 int code;
80 char* message;
81} GError;
82
83// gboolean is an int in GLib
84typedef int gboolean;
85typedef uint64_t GstClockTime;
86#define GST_CLOCK_TIME_NONE ((GstClockTime)-1)
87#define GST_SECOND ((GstClockTime)1000000000)
88
89// GLib type constants for gst_caps_new_simple varargs
90// GLib/GObject fundamental type IDs
91#define G_TYPE_BOOLEAN ((unsigned long)5 << 2)
92#define G_TYPE_INT ((unsigned long)6 << 2)
93#define G_TYPE_UINT ((unsigned long)7 << 2)
94#define G_TYPE_LONG ((unsigned long)8 << 2)
95#define G_TYPE_ULONG ((unsigned long)9 << 2)
96#define G_TYPE_INT64 ((unsigned long)10 << 2)
97#define G_TYPE_UINT64 ((unsigned long)11 << 2)
98#define G_TYPE_FLOAT ((unsigned long)14 << 2)
99#define G_TYPE_DOUBLE ((unsigned long)15 << 2)
100#define G_TYPE_STRING ((unsigned long)16 << 2)
101#define G_TYPE_ENUM ((unsigned long)12 << 2)
102#define G_TYPE_FLAGS ((unsigned long)13 << 2)
103
104// GType is unsigned long
105typedef unsigned long GType;
106
107// GParamSpec: property metadata
108typedef struct _GParamSpec
109{
110 void* g_type_instance; // GTypeInstance
111 const char* name;
112 unsigned int flags; // GParamFlags
113 GType value_type;
114 GType owner_type;
115 // Private fields follow — we only access name, flags, value_type
116 char* _nick;
117 char* _blurb;
118 void* qdata;
119 unsigned int ref_count;
120 unsigned int param_id;
121} GParamSpec;
122
123// Typed GParamSpec subtypes for range access
124typedef struct { GParamSpec parent; int minimum; int maximum; int default_value; } GParamSpecInt;
125typedef struct { GParamSpec parent; unsigned int minimum; unsigned int maximum; unsigned int default_value; } GParamSpecUInt;
126typedef struct { GParamSpec parent; long minimum; long maximum; long default_value; } GParamSpecLong;
127typedef struct { GParamSpec parent; unsigned long minimum; unsigned long maximum; unsigned long default_value; } GParamSpecULong;
128typedef struct { GParamSpec parent; int64_t minimum; int64_t maximum; int64_t default_value; } GParamSpecInt64;
129typedef struct { GParamSpec parent; uint64_t minimum; uint64_t maximum; uint64_t default_value; } GParamSpecUInt64;
130typedef struct { GParamSpec parent; float minimum; float maximum; float default_value; float epsilon; } GParamSpecFloat;
131typedef struct { GParamSpec parent; double minimum; double maximum; double default_value; double epsilon; } GParamSpecDouble;
132
133// GValue: generic value container (must match ABI — 24 bytes on 64-bit)
134typedef struct _GValue
135{
136 GType g_type;
137 union { int v_int; unsigned int v_uint; long v_long; unsigned long v_ulong;
138 int64_t v_int64; uint64_t v_uint64; float v_float; double v_double;
139 void* v_pointer; } data[2];
140} GValue;
141
142// GParamFlags
143#define G_PARAM_READABLE (1 << 0)
144#define G_PARAM_WRITABLE (1 << 1)
145#define G_PARAM_READWRITE (G_PARAM_READABLE | G_PARAM_WRITABLE)
146
147// Function signatures for dlsym
148
149// Core
150gboolean gst_init_check(int* argc, char*** argv, GError** error);
151GstElement* gst_parse_launch(const char* pipeline_description, GError** error);
152GstStateChangeReturn
153gst_element_set_state(GstElement* element, GstState state);
154GstStateChangeReturn gst_element_get_state(
155 GstElement* element, GstState* state, GstState* pending,
156 GstClockTime timeout);
157GstBus* gst_element_get_bus(GstElement* element);
158GstElement* gst_bin_get_by_name(GstElement* bin, const char* name);
159GstPad* gst_element_get_static_pad(GstElement* element, const char* name);
160char* gst_element_get_name(GstElement* element);
161
162// Pads
163GstPad* gst_pad_get_peer(GstPad* pad);
164GstCaps* gst_pad_get_allowed_caps(GstPad* pad);
165GstElement* gst_pad_get_parent_element(GstPad* pad);
166
167// Caps
168GstCaps* gst_pad_get_current_caps(GstPad* pad);
169GstCaps* gst_caps_new_simple(const char* media_type, const char* fieldname, ...);
170GstCaps* gst_caps_from_string(const char* string);
171void gst_caps_unref(GstCaps* caps);
172GstStructure* gst_caps_get_structure(const GstCaps* caps, unsigned int index);
173const char* gst_structure_get_name(const GstStructure* structure);
174gboolean gst_structure_get_int(
175 const GstStructure* structure, const char* fieldname, int* value);
176const char* gst_structure_get_string(
177 const GstStructure* structure, const char* fieldname);
178
179// Bus/messages
180GstMessage* gst_bus_timed_pop_filtered(
181 GstBus* bus, GstClockTime timeout, GstMessageType types);
182
183// Lifecycle
184void gst_object_unref(void* object);
185void gst_message_unref(GstMessage* msg);
186void gst_mini_object_unref(void* mini_object);
187void g_free(void* mem);
188void g_error_free(GError* error);
189
190// AppSink (input)
191GstSample* gst_app_sink_try_pull_sample(void* appsink, GstClockTime timeout);
192gboolean gst_app_sink_is_eos(void* appsink);
193
194// AppSrc (output)
195GstFlowReturn gst_app_src_push_buffer(void* appsrc, GstBuffer* buffer);
196GstFlowReturn gst_app_src_end_of_stream(void* appsrc);
197void gst_app_src_set_caps(void* appsrc, const GstCaps* caps);
198
199// Sample/Buffer
200GstBuffer* gst_sample_get_buffer(GstSample* sample);
201GstCaps* gst_sample_get_caps(GstSample* sample);
202GstBuffer* gst_buffer_new_allocate(void* allocator, size_t size, void* params);
203GstBuffer* gst_buffer_new_wrapped_full(
204 int flags, void* data, size_t maxsize, size_t offset, size_t size,
205 void* user_data, void (*notify)(void*));
206gboolean gst_buffer_map(GstBuffer* buffer, GstMapInfo* info, GstMapFlags flags);
207void gst_buffer_unmap(GstBuffer* buffer, GstMapInfo* info);
208
209// GObject property introspection (from libgobject-2.0)
210GParamSpec** g_object_class_list_properties(void* oclass, unsigned int* n_properties);
211void* g_type_class_ref(GType type);
212void g_type_class_unref(void* g_class);
213gboolean g_type_is_a(GType type, GType is_a_type); // Check type inheritance
214
215// GObject property access
216void g_object_set(void* object, const char* first_property_name, ...);
217void g_object_get(void* object, const char* first_property_name, ...);
218
219// GValue operations
220void g_value_init(GValue* value, GType g_type);
221void g_value_unset(GValue* value);
222void g_object_get_property(void* object, const char* property_name, GValue* value);
223void g_object_set_property(void* object, const char* property_name, const GValue* value);
224int g_value_get_int(const GValue* value);
225unsigned int g_value_get_uint(const GValue* value);
226float g_value_get_float(const GValue* value);
227double g_value_get_double(const GValue* value);
228gboolean g_value_get_boolean(const GValue* value);
229const char* g_value_get_string(const GValue* value);
230int64_t g_value_get_int64(const GValue* value);
231uint64_t g_value_get_uint64(const GValue* value);
232long g_value_get_long(const GValue* value);
233unsigned long g_value_get_ulong(const GValue* value);
234int g_value_get_enum(const GValue* value);
235void g_value_set_int(GValue* value, int v_int);
236void g_value_set_uint(GValue* value, unsigned int v_uint);
237void g_value_set_float(GValue* value, float v_float);
238void g_value_set_double(GValue* value, double v_double);
239void g_value_set_boolean(GValue* value, gboolean v_boolean);
240void g_value_set_string(GValue* value, const char* v_string);
241void g_value_set_int64(GValue* value, int64_t v_int64);
242void g_value_set_uint64(GValue* value, uint64_t v_uint64);
243void g_value_set_long(GValue* value, long v_long);
244void g_value_set_ulong(GValue* value, unsigned long v_ulong);
245void g_value_set_enum(GValue* value, int v_enum);
246}
247
248namespace Gfx::GStreamer
249{
250
251// Unified dynamic loader for libgstreamer-1.0, libgstapp-1.0, libglib-2.0
253{
254 // Core functions
255 decltype(&::gst_init_check) init_check{};
256 decltype(&::gst_parse_launch) parse_launch{};
257 decltype(&::gst_element_set_state) element_set_state{};
258 decltype(&::gst_element_get_state) element_get_state{};
259 decltype(&::gst_element_get_bus) element_get_bus{};
260 decltype(&::gst_bin_get_by_name) bin_get_by_name{};
261 decltype(&::gst_element_get_static_pad) element_get_static_pad{};
262 decltype(&::gst_element_get_name) element_get_name{};
263
264 // Pads
265 decltype(&::gst_pad_get_peer) pad_get_peer{};
266 decltype(&::gst_pad_get_allowed_caps) pad_get_allowed_caps{};
267 decltype(&::gst_pad_get_parent_element) pad_get_parent_element{};
268
269 // Caps
270 decltype(&::gst_pad_get_current_caps) pad_get_current_caps{};
271 decltype(&::gst_caps_new_simple) caps_new_simple{};
272 decltype(&::gst_caps_from_string) caps_from_string{};
273 decltype(&::gst_caps_unref) caps_unref{};
274 decltype(&::gst_caps_get_structure) caps_get_structure{};
275 decltype(&::gst_structure_get_name) structure_get_name{};
276 decltype(&::gst_structure_get_int) structure_get_int{};
277 decltype(&::gst_structure_get_string) structure_get_string{};
278
279 // Bus/messages
280 decltype(&::gst_bus_timed_pop_filtered) bus_timed_pop_filtered{};
281
282 // Lifecycle
283 decltype(&::gst_object_unref) object_unref{};
284 decltype(&::gst_message_unref) message_unref{};
285 decltype(&::gst_mini_object_unref) mini_object_unref{};
286 decltype(&::g_free) g_free{};
287 decltype(&::g_error_free) g_error_free{};
288
289 // AppSink (input)
290 decltype(&::gst_app_sink_try_pull_sample) app_sink_try_pull_sample{};
291 decltype(&::gst_app_sink_is_eos) app_sink_is_eos{};
292
293 // AppSrc (output)
294 decltype(&::gst_app_src_push_buffer) app_src_push_buffer{};
295 decltype(&::gst_app_src_end_of_stream) app_src_end_of_stream{};
296 decltype(&::gst_app_src_set_caps) app_src_set_caps{};
297
298 // Sample/Buffer
299 decltype(&::gst_sample_get_buffer) sample_get_buffer{};
300 decltype(&::gst_sample_get_caps) sample_get_caps{};
301 decltype(&::gst_buffer_new_allocate) buffer_new_allocate{};
302 decltype(&::gst_buffer_new_wrapped_full) buffer_new_wrapped_full{};
303 decltype(&::gst_buffer_map) buffer_map{};
304 decltype(&::gst_buffer_unmap) buffer_unmap{};
305
306 // GObject property introspection (from libgobject-2.0)
307 decltype(&::g_object_class_list_properties) object_class_list_properties{};
308 decltype(&::g_type_class_ref) type_class_ref{};
309 decltype(&::g_type_class_unref) type_class_unref{};
310 decltype(&::g_type_is_a) type_is_a{};
311 decltype(&::g_object_set_property) object_set_property{};
312 decltype(&::g_object_get_property) object_get_property{};
313 decltype(&::g_value_init) value_init{};
314 decltype(&::g_value_unset) value_unset{};
315 decltype(&::g_value_get_int) value_get_int{};
316 decltype(&::g_value_get_uint) value_get_uint{};
317 decltype(&::g_value_get_float) value_get_float{};
318 decltype(&::g_value_get_double) value_get_double{};
319 decltype(&::g_value_get_boolean) value_get_boolean{};
320 decltype(&::g_value_get_string) value_get_string{};
321 decltype(&::g_value_get_int64) value_get_int64{};
322 decltype(&::g_value_get_uint64) value_get_uint64{};
323 decltype(&::g_value_get_long) value_get_long{};
324 decltype(&::g_value_get_ulong) value_get_ulong{};
325 decltype(&::g_value_get_enum) value_get_enum{};
326 decltype(&::g_value_set_int) value_set_int{};
327 decltype(&::g_value_set_uint) value_set_uint{};
328 decltype(&::g_value_set_float) value_set_float{};
329 decltype(&::g_value_set_double) value_set_double{};
330 decltype(&::g_value_set_boolean) value_set_boolean{};
331 decltype(&::g_value_set_string) value_set_string{};
332 decltype(&::g_value_set_int64) value_set_int64{};
333 decltype(&::g_value_set_uint64) value_set_uint64{};
334 decltype(&::g_value_set_long) value_set_long{};
335 decltype(&::g_value_set_ulong) value_set_ulong{};
336 decltype(&::g_value_set_enum) value_set_enum{};
337
338 bool available{};
339
340 static const libgstreamer& instance()
341 {
342 static const libgstreamer self;
343 return self;
344 }
345
346private:
347 static ossia::dylib_loader load_core_library()
348 {
349#if defined(_WIN32)
350 return ossia::dylib_loader{
351 std::vector<std::string_view>{"gstreamer-1.0-0.dll"}};
352#elif defined(__APPLE__)
353 return ossia::dylib_loader{std::vector<std::string_view>{
354 "libgstreamer-1.0.0.dylib", "libgstreamer-1.0.dylib"}};
355#else
356 return ossia::dylib_loader{std::vector<std::string_view>{
357 "libgstreamer-1.0.so.0", "libgstreamer-1.0.so"}};
358#endif
359 }
360
361 static ossia::dylib_loader load_app_library()
362 {
363#if defined(_WIN32)
364 return ossia::dylib_loader{
365 std::vector<std::string_view>{"gstapp-1.0-0.dll"}};
366#elif defined(__APPLE__)
367 return ossia::dylib_loader{std::vector<std::string_view>{
368 "libgstapp-1.0.0.dylib", "libgstapp-1.0.dylib"}};
369#else
370 return ossia::dylib_loader{std::vector<std::string_view>{
371 "libgstapp-1.0.so.0", "libgstapp-1.0.so"}};
372#endif
373 }
374
375 static ossia::dylib_loader load_gobject_library()
376 {
377#if defined(_WIN32)
378 return ossia::dylib_loader{
379 std::vector<std::string_view>{"gobject-2.0-0.dll", "libgobject-2.0-0.dll"}};
380#elif defined(__APPLE__)
381 return ossia::dylib_loader{std::vector<std::string_view>{
382 "libgobject-2.0.0.dylib", "libgobject-2.0.dylib"}};
383#else
384 return ossia::dylib_loader{std::vector<std::string_view>{
385 "libgobject-2.0.so.0", "libgobject-2.0.so"}};
386#endif
387 }
388
389 static ossia::dylib_loader load_glib_library()
390 {
391#if defined(_WIN32)
392 return ossia::dylib_loader{
393 std::vector<std::string_view>{"glib-2.0-0.dll", "libglib-2.0-0.dll"}};
394#elif defined(__APPLE__)
395 return ossia::dylib_loader{std::vector<std::string_view>{
396 "libglib-2.0.0.dylib", "libglib-2.0.dylib"}};
397#else
398 return ossia::dylib_loader{std::vector<std::string_view>{
399 "libglib-2.0.so.0", "libglib-2.0.so"}};
400#endif
401 }
402
403#define GST_LOAD(lib, name) \
404 name = lib.symbol<decltype(&::gst_##name)>("gst_" #name)
405
407 try : m_core{load_core_library()}
408 , m_app{load_app_library()}
409 , m_gobject{load_gobject_library()}
410 , m_glib{load_glib_library()}
411 , available{true}
412 {
413 // Core
414 GST_LOAD(m_core, init_check);
415 GST_LOAD(m_core, parse_launch);
416 GST_LOAD(m_core, element_set_state);
417 GST_LOAD(m_core, element_get_state);
418 GST_LOAD(m_core, element_get_bus);
419 GST_LOAD(m_core, bin_get_by_name);
420 GST_LOAD(m_core, element_get_static_pad);
421 GST_LOAD(m_core, element_get_name);
422
423 // Pads
424 GST_LOAD(m_core, pad_get_peer);
425 GST_LOAD(m_core, pad_get_allowed_caps);
426 GST_LOAD(m_core, pad_get_parent_element);
427
428 // Caps
429 GST_LOAD(m_core, pad_get_current_caps);
430 GST_LOAD(m_core, caps_new_simple);
431 GST_LOAD(m_core, caps_from_string);
432 GST_LOAD(m_core, caps_unref);
433 GST_LOAD(m_core, caps_get_structure);
434 GST_LOAD(m_core, structure_get_name);
435 GST_LOAD(m_core, structure_get_int);
436 GST_LOAD(m_core, structure_get_string);
437
438 // Bus
439 GST_LOAD(m_core, bus_timed_pop_filtered);
440
441 // Lifecycle
442 GST_LOAD(m_core, object_unref);
443 GST_LOAD(m_core, message_unref);
444 GST_LOAD(m_core, mini_object_unref);
445 g_free = m_glib.symbol<decltype(&::g_free)>("g_free");
446 g_error_free = m_glib.symbol<decltype(&::g_error_free)>("g_error_free");
447
448 // AppSink
449 GST_LOAD(m_app, app_sink_try_pull_sample);
450 GST_LOAD(m_app, app_sink_is_eos);
451
452 // AppSrc
453 GST_LOAD(m_app, app_src_push_buffer);
454 GST_LOAD(m_app, app_src_end_of_stream);
455 GST_LOAD(m_app, app_src_set_caps);
456
457 // Sample/Buffer
458 GST_LOAD(m_core, sample_get_buffer);
459 GST_LOAD(m_core, sample_get_caps);
460 GST_LOAD(m_core, buffer_new_allocate);
461 GST_LOAD(m_core, buffer_new_wrapped_full);
462 GST_LOAD(m_core, buffer_map);
463 GST_LOAD(m_core, buffer_unmap);
464
465 // GObject property introspection
466#define GOBJ_LOAD(name) \
467 name = m_gobject.symbol<decltype(&::g_##name)>("g_" #name)
468
469 GOBJ_LOAD(object_class_list_properties);
470 GOBJ_LOAD(type_class_ref);
471 GOBJ_LOAD(type_class_unref);
472 GOBJ_LOAD(type_is_a);
473 GOBJ_LOAD(object_set_property);
474 GOBJ_LOAD(object_get_property);
475 GOBJ_LOAD(value_init);
476 GOBJ_LOAD(value_unset);
477 GOBJ_LOAD(value_get_int);
478 GOBJ_LOAD(value_get_uint);
479 GOBJ_LOAD(value_get_float);
480 GOBJ_LOAD(value_get_double);
481 GOBJ_LOAD(value_get_boolean);
482 GOBJ_LOAD(value_get_string);
483 GOBJ_LOAD(value_get_int64);
484 GOBJ_LOAD(value_get_uint64);
485 GOBJ_LOAD(value_get_long);
486 GOBJ_LOAD(value_get_ulong);
487 GOBJ_LOAD(value_get_enum);
488 GOBJ_LOAD(value_set_int);
489 GOBJ_LOAD(value_set_uint);
490 GOBJ_LOAD(value_set_float);
491 GOBJ_LOAD(value_set_double);
492 GOBJ_LOAD(value_set_boolean);
493 GOBJ_LOAD(value_set_string);
494 GOBJ_LOAD(value_set_int64);
495 GOBJ_LOAD(value_set_uint64);
496 GOBJ_LOAD(value_set_long);
497 GOBJ_LOAD(value_set_ulong);
498 GOBJ_LOAD(value_set_enum);
499
500#undef GOBJ_LOAD
501
502 // Verify critical symbols
503 if(!init_check || !parse_launch || !element_set_state
504 || !element_get_state || !bin_get_by_name || !object_unref
505 || !buffer_map || !buffer_unmap)
506 {
507 available = false;
508 }
509 }
510 catch(...)
511 {
512 }
513
514#undef GST_LOAD
515
516 ossia::dylib_loader m_core;
517 ossia::dylib_loader m_app;
518 ossia::dylib_loader m_gobject;
519 ossia::dylib_loader m_glib;
520};
521
522// Call once from any code that needs GStreamer
523inline bool gstreamer_init()
524{
525 static bool initialized = [] {
526 auto& g = libgstreamer::instance();
527 if(!g.available || !g.init_check)
528 return false;
529 GError* err = nullptr;
530 gboolean ok = g.init_check(nullptr, nullptr, &err);
531 if(err)
532 {
533 if(g.g_error_free)
534 g.g_error_free(err);
535 }
536 return ok != 0;
537 }();
538 return initialized;
539}
540
541}
Definition GStreamerLoader.hpp:77
Definition GStreamerLoader.hpp:109
Definition GStreamerLoader.hpp:135
Definition GStreamerLoader.hpp:65
Definition GStreamerLoader.hpp:131
Definition GStreamerLoader.hpp:130
Definition GStreamerLoader.hpp:128
Definition GStreamerLoader.hpp:124
Definition GStreamerLoader.hpp:126
Definition GStreamerLoader.hpp:129
Definition GStreamerLoader.hpp:125
Definition GStreamerLoader.hpp:127
Definition GStreamerLoader.hpp:253