feat(gstreamer): integrate webrtc plugin (#9799)
Some checks failed
Arduino Lint / lint (push) Has been cancelled
Build Examples with C++ Compiler / build-examples (push) Has been cancelled
MicroPython CI / Build esp32 port (push) Has been cancelled
MicroPython CI / Build rp2 port (push) Has been cancelled
MicroPython CI / Build stm32 port (push) Has been cancelled
MicroPython CI / Build unix port (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_NORMAL_8BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_SDL - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build ESP IDF ESP32S3 (push) Has been cancelled
C/C++ CI / Run tests with 32bit build (push) Has been cancelled
C/C++ CI / Run tests with 64bit build (push) Has been cancelled
BOM Check / bom-check (push) Has been cancelled
Verify that lv_conf_internal.h matches repository state / verify-conf-internal (push) Has been cancelled
Verify GDB constants are up-to-date / verify-gdb-consts (push) Has been cancelled
Verify the widget property name / verify-property-name (push) Has been cancelled
Verify code formatting / verify-formatting (push) Has been cancelled
Compare file templates with file names / template-check (push) Has been cancelled
Build docs / build-and-deploy (push) Has been cancelled
Test API JSON generator / Test API JSON (push) Has been cancelled
Install LVGL using CMake / build-examples (push) Has been cancelled
Check Makefile / Build using Makefile (push) Has been cancelled
Check Makefile for UEFI / Build using Makefile for UEFI (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/benchmark_results_comment/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/filter_docker_logs/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/serialize_results/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 32b - lv_conf_perf32b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 64b - lv_conf_perf64b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Save PR Number (push) Has been cancelled
Hardware Performance Test / Hardware Performance Benchmark (push) Has been cancelled
Hardware Performance Test / HW Benchmark - Save PR Number (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_32B - Ubuntu (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_64B - Ubuntu (push) Has been cancelled
Port repo release update / run-release-branch-updater (push) Has been cancelled
Verify Font License / verify-font-license (push) Has been cancelled
Verify Kconfig / verify-kconfig (push) Has been cancelled

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
leonardo salvatore
2026-03-17 07:49:29 +01:00
committed by GitHub
parent 8fa1c29773
commit 5435384d49
4 changed files with 129 additions and 20 deletions

View File

@@ -27,6 +27,7 @@ LVGL's GStreamer implementation provides comprehensive media playback capabiliti
* Video4Linux2 (V4L2) camera devices on Linux
* Audio capture from ALSA and PulseAudio devices
* Test sources for audio and video development
* WebRTC streaming for real-time communication applications
**URI Scheme Support:**
@@ -42,6 +43,10 @@ Using the URI factory (:c:macro:`LV_GSTREAMER_FACTORY_URI_DECODE`), you can spec
GStreamer's ``uridecodebin`` automatically selects the appropriate source element and decoder based on the URI scheme and media format.
.. note::
WebRTC streaming is supported via a dedicated WebRTC source factory (for example, ``webrtcsrc``) configured with a signalling URI (for example, ``signaller::uri``), and is not provided through :c:macro:`LV_GSTREAMER_FACTORY_URI_DECODE` / ``uridecodebin``.
**Playback Control:**
* Play, pause, and stop operations
@@ -255,6 +260,15 @@ The GStreamer widget supports various media sources through different factories:
"/path/to/video.mp4");
**WebRTC Factory:**
.. code-block:: c
/* WebRTC stream */
lv_gstreamer_set_src(streamer, LV_GSTREAMER_FACTORY_WEBRTCSRC,
LV_GSTREAMER_PROPERTY_WEBRTCSRC,
"ws://signalserver:port/");
Playback Control
----------------
@@ -326,6 +340,20 @@ Once media is loaded (LV_EVENT_READY), you can access:
- Current volume level via ``lv_gstreamer_get_volume()``
- Current playback state via ``lv_gstreamer_get_state()``
WebRTC Notes
************
WebRTC is using the Rust plugin "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
So to be able to stream to an LVGL player application you will need to run a signaller server and then a simpler pipeline like so:
.. code-block:: bash
./gst-webrtc-signalling-server &
gst-launch-1.0 videotestsrc pattern=ball ! webrtcsink
Then, in your LVGL application, configure WebRTC by passing the signalling server URI (for example, ``ws://localhost:8443``) to ``lv_gstreamer_set_src(..., LV_GSTREAMER_PROPERTY_WEBRTCSRC, ...)``.
.. _gstreamer_example:
Example

View File

@@ -40,7 +40,11 @@ void lv_example_gstreamer_1(void)
* specify various URI schemes as media sources including local files (file://),
* web streams (http://, https://), RTSP streams (rtsp://), UDP streams (udp://),
* and many others. GStreamer's uridecodebin automatically selects the appropriate
* source element and decoder based on the URI scheme and media format. */
* source element and decoder based on the URI scheme and media format.
* WebRTC streams, however, are provided via a dedicated WebRTC source (webrtcsrc)
* configured with a signalling server URI (e.g. ws://[ipsignallerserver]:[port])
* through its signaller-related properties, rather than via LV_GSTREAMER_FACTORY_URI_DECODE.
*/
lv_gstreamer_set_src(event_data.streamer, LV_GSTREAMER_FACTORY_URI_DECODE, LV_GSTREAMER_PROPERTY_URI_DECODE,
"https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm");

View File

@@ -47,6 +47,8 @@ static void gstreamer_update_frame(lv_gstreamer_t * streamer);
static lv_result_t gstreamer_make_and_add_to_pipeline(lv_gstreamer_t * streamer,
const lv_gstreamer_pipeline_element_t * elements, size_t element_count);
static lv_result_t gstreamer_send_state_changed(lv_gstreamer_t * streamer, lv_gstreamer_stream_state_t state);
static bool gstreamer_element_has_property(GstElement * element, const char * property_name);
static bool gstreamer_set_child_proxy_string(GstElement * element, const char * property_name, const char * value);
/**********************
* STATIC VARIABLES
@@ -135,31 +137,53 @@ lv_result_t lv_gstreamer_set_src(lv_obj_t * obj, const char * factory_name, cons
}
if(property != NULL && source != NULL) {
g_object_set(G_OBJECT(head), property, source, NULL);
/* LV_GSTREAMER_PROPERTY_WEBRTCSRC is a child-proxy property path */
if(lv_streq(property, LV_GSTREAMER_PROPERTY_WEBRTCSRC)) {
if(!gstreamer_set_child_proxy_string(head, property, source)) {
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to set '%s' via child proxy", property);
return LV_RESULT_INVALID;
}
}
else {
g_object_set(G_OBJECT(head), property, source, NULL);
}
}
/* The uri decode source element will automatically handle parsing and decoding for us
* for other source types, we need to add a parser and a decoder ourselves element*/
if(!lv_streq(LV_GSTREAMER_FACTORY_URI_DECODE, factory_name)) {
GstElement * decodebin = gst_element_factory_make("decodebin", "lv_gstreamer_decodebin");
if(!decodebin) {
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to create decodebin element");
return LV_RESULT_INVALID;
}
if(!gst_bin_add(GST_BIN(pipeline), decodebin)) {
gst_object_unref(decodebin);
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to add decodebin element to pipeline");
return LV_RESULT_INVALID;
/* webrtcsrc plugins require its own handling to be able to connect on the first available video stream */
if(lv_streq(LV_GSTREAMER_FACTORY_WEBRTCSRC, factory_name)) {
LV_LOG_INFO("Setting up webrtc pipeline");
if(gstreamer_element_has_property(head, "connect-to-first-producer")) {
g_object_set(G_OBJECT(head), "connect-to-first-producer", TRUE, NULL);
}
else {
LV_LOG_WARN("webrtcsrc property 'connect-to-first-producer' is not available in this plugin build");
}
}
else {
GstElement * decodebin = gst_element_factory_make("decodebin", "lv_gstreamer_decodebin");
if(!decodebin) {
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to create decodebin element");
return LV_RESULT_INVALID;
}
if(!gst_bin_add(GST_BIN(pipeline), decodebin)) {
gst_object_unref(decodebin);
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to add decodebin element to pipeline");
return LV_RESULT_INVALID;
}
if(!gst_element_link(head, decodebin)) {
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to link source with parsebin elements");
return LV_RESULT_INVALID;
if(!gst_element_link(head, decodebin)) {
gst_object_unref(pipeline);
LV_LOG_ERROR("Failed to link source with parsebin elements");
return LV_RESULT_INVALID;
}
head = decodebin;
}
head = decodebin;
}
/* At this point we don't yet know the input format
@@ -412,7 +436,18 @@ static lv_result_t gstreamer_poll_bus(lv_gstreamer_t * streamer)
GError * err;
gchar * debug;
gst_message_parse_error(msg, &err, &debug);
LV_LOG_ERROR("GStreamer error: %s", err->message);
GstObject * src = GST_MESSAGE_SRC(msg);
const gchar * name = NULL;
if(src != NULL) {
name = GST_OBJECT_NAME(src);
}
if(!name) {
name = "unknown";
}
LV_LOG_ERROR("GStreamer error from %s: %s", name, err->message);
if(debug && debug[0] != '\0') {
LV_LOG_ERROR("GStreamer error details: %s", debug);
}
g_error_free(err);
g_free(debug);
break;
@@ -563,11 +598,50 @@ static lv_result_t gstreamer_make_and_add_to_pipeline(lv_gstreamer_t * streamer,
return LV_RESULT_OK;
}
static bool gstreamer_element_has_property(GstElement * element, const char * property_name)
{
LV_ASSERT_NULL(element);
LV_ASSERT_NULL(property_name);
GObjectClass * klass = G_OBJECT_GET_CLASS(element);
return g_object_class_find_property(klass, property_name) != NULL;
}
static bool gstreamer_set_child_proxy_string(GstElement * element, const char * property_name, const char * value)
{
LV_ASSERT_NULL(element);
LV_ASSERT_NULL(property_name);
LV_ASSERT_NULL(value);
if(!GST_IS_CHILD_PROXY(element)) {
LV_LOG_WARN("Element does not support child proxy for property '%s'", property_name);
return false;
}
GObject * target = NULL;
GParamSpec * pspec = NULL;
if(!gst_child_proxy_lookup(GST_CHILD_PROXY(element), property_name, &target, &pspec)) {
LV_LOG_WARN("Property '%s' not found in child proxy", property_name);
return false;
}
if(target) {
g_object_unref(target);
}
GValue gvalue = G_VALUE_INIT;
g_value_init(&gvalue, G_TYPE_STRING);
g_value_set_string(&gvalue, value);
gst_child_proxy_set_property(GST_CHILD_PROXY(element), property_name, &gvalue);
g_value_unset(&gvalue);
return true;
}
static void on_decode_pad_added(GstElement * element, GstPad * pad, gpointer user_data)
{
LV_UNUSED(element);
lv_gstreamer_t * streamer = (lv_gstreamer_t *)user_data;
GstCaps * caps = gst_pad_get_current_caps(pad);
GstCaps * caps = gst_pad_query_caps(pad, NULL);
GstStructure * structure = gst_caps_get_structure(caps, 0);
const gchar * name = gst_structure_get_name(structure);

View File

@@ -62,6 +62,9 @@ extern "C" {
#define LV_GSTREAMER_FACTORY_APP "appsrc"
#define LV_GSTREAMER_PROPERTY_APP NULL
#define LV_GSTREAMER_FACTORY_WEBRTCSRC "webrtcsrc"
#define LV_GSTREAMER_PROPERTY_WEBRTCSRC "signaller::uri"
/**********************
* TYPEDEFS
**********************/