mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-10 04:37:55 +08:00
feat(gstreamer): send event on end of stream (#9634)
This commit is contained in:
@@ -168,6 +168,59 @@ Here's how to create a basic GStreamer player and load media:
|
||||
/* Start playback */
|
||||
lv_gstreamer_play(streamer);
|
||||
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_STATE_CHANGED` Sent when the stream state changes. The stream state can be retrieved via :cpp:expr:`lv_gstreamer_get_stream_state(e)`.
|
||||
|
||||
Event Handling
|
||||
--------------
|
||||
|
||||
Handle GStreamer events using LVGL's event system:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void gstreamer_event_cb(lv_event_t * e)
|
||||
{
|
||||
|
||||
lv_event_code_t code = lv_event_get_code(e);
|
||||
lv_obj_t * streamer = lv_event_get_target_obj(e);
|
||||
|
||||
if(code != LV_EVENT_STATE_CHANGED) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_gstreamer_stream_state_t stream_state = lv_gstreamer_get_stream_state(e);
|
||||
switch(stream_state) {
|
||||
case LV_GSTREAMER_STREAM_STATE_START:
|
||||
LV_LOG_USER("Stream ready - Duration: %" LV_PRIu32 " ms",
|
||||
lv_gstreamer_get_duration(streamer));
|
||||
LV_LOG_USER("\tStream resolution %" LV_PRId32 "x%" LV_PRId32, lv_image_get_src_width(streamer),
|
||||
lv_image_get_src_height(streamer));
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_END:
|
||||
LV_LOG_USER("Stream is over");
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_PLAY:
|
||||
LV_LOG_USER("Stream set to play");
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_PAUSE:
|
||||
LV_LOG_USER("Stream set to pause");
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_STOP:
|
||||
LV_LOG_USER("Stream set to stop");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add event callback */
|
||||
lv_obj_add_event_cb(streamer, gstreamer_event_cb, LV_EVENT_STATE_CHANGED, NULL);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Media Source Configuration
|
||||
--------------------------
|
||||
|
||||
@@ -242,29 +295,6 @@ Manage audio volume with built-in controls:
|
||||
/* Get current volume */
|
||||
uint8_t volume = lv_gstreamer_get_volume(streamer);
|
||||
|
||||
Event Handling
|
||||
--------------
|
||||
|
||||
Handle GStreamer events using LVGL's event system:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void gstreamer_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_event_code_t code = lv_event_get_code(e);
|
||||
lv_obj_t * streamer = lv_event_get_target_obj(e);
|
||||
|
||||
if(code == LV_EVENT_READY) {
|
||||
LV_LOG_USER("Stream ready - Duration: %" LV_PRIu32 " ms",
|
||||
lv_gstreamer_get_duration(streamer));
|
||||
LV_LOG_USER("Resolution: %" LV_PRId32 "x%" LV_PRId32,
|
||||
lv_image_get_src_width(streamer),
|
||||
lv_image_get_src_height(streamer));
|
||||
}
|
||||
}
|
||||
|
||||
/* Add event callback */
|
||||
lv_obj_add_event_cb(streamer, gstreamer_event_cb, LV_EVENT_ALL, NULL);
|
||||
|
||||
Widget Architecture
|
||||
*******************
|
||||
|
||||
@@ -18,7 +18,7 @@ static void update_duration_label(lv_obj_t * label, uint32_t duration);
|
||||
static void volume_observer_cb(lv_observer_t * observer, lv_subject_t * subject);
|
||||
static void update_position_slider(lv_timer_t * timer);
|
||||
static void play_pause_pressed(lv_event_t * e);
|
||||
static void streamer_ready(lv_event_t * e);
|
||||
static void stream_state_changed(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Loads a video from the internet using the gstreamer widget
|
||||
@@ -46,13 +46,6 @@ void lv_example_gstreamer_1(void)
|
||||
|
||||
lv_obj_center(event_data.streamer);
|
||||
|
||||
/* The LV_EVENT_READY will fire when the stream is ready at that point you can query the stream
|
||||
* information like its resolution and duration. See `streamer_ready` */
|
||||
lv_obj_add_event_cb(event_data.streamer, streamer_ready, LV_EVENT_READY, &event_data);
|
||||
|
||||
/* Play the stream immediately */
|
||||
lv_gstreamer_play(event_data.streamer);
|
||||
|
||||
/* Create a slider to modify the stream volume and a label to visualize the current value */
|
||||
volume_setter_create(&event_data);
|
||||
|
||||
@@ -61,6 +54,13 @@ void lv_example_gstreamer_1(void)
|
||||
* Also add a pause/play button*/
|
||||
control_bar_create(&event_data);
|
||||
|
||||
/* The LV_EVENT_STATE_CHANGED will fire when the stream is ready at that point we can query the stream
|
||||
* information like its resolution and duration. See `streamer_ready` */
|
||||
lv_obj_add_event_cb(event_data.streamer, stream_state_changed, LV_EVENT_STATE_CHANGED, &event_data);
|
||||
|
||||
/* Play the stream immediately */
|
||||
lv_gstreamer_play(event_data.streamer);
|
||||
|
||||
/* Create a timer that will update the slider position based on the stream position
|
||||
* Make it 3 times faster than the refresh rate for a smoother effect */
|
||||
lv_timer_create(update_position_slider, LV_DEF_REFR_PERIOD, &event_data);
|
||||
@@ -173,30 +173,50 @@ static void play_pause_pressed(lv_event_t * e)
|
||||
{
|
||||
event_data_t * event_data = (event_data_t *)lv_event_get_user_data(e);
|
||||
|
||||
if(lv_streq(lv_label_get_text(event_data->button_label), LV_SYMBOL_PLAY)) {
|
||||
lv_label_set_text(event_data->button_label, LV_SYMBOL_PAUSE);
|
||||
if(lv_streq(lv_label_get_text(event_data->button_label), LV_SYMBOL_REFRESH)) {
|
||||
lv_gstreamer_set_position(event_data->streamer, 0);
|
||||
lv_gstreamer_play(event_data->streamer);
|
||||
}
|
||||
else if(lv_streq(lv_label_get_text(event_data->button_label), LV_SYMBOL_PLAY)) {
|
||||
lv_gstreamer_play(event_data->streamer);
|
||||
}
|
||||
else {
|
||||
lv_label_set_text(event_data->button_label, LV_SYMBOL_PLAY);
|
||||
lv_gstreamer_pause(event_data->streamer);
|
||||
}
|
||||
}
|
||||
static void streamer_ready(lv_event_t * e)
|
||||
static void stream_state_changed(lv_event_t * e)
|
||||
{
|
||||
lv_event_code_t code = lv_event_get_code(e);
|
||||
event_data_t * event_data = (event_data_t *)lv_event_get_user_data(e);
|
||||
lv_obj_t * btn = event_data->pp_button;
|
||||
|
||||
lv_obj_t * streamer = event_data->streamer;
|
||||
|
||||
if(code == LV_EVENT_READY) {
|
||||
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
uint32_t duration = lv_gstreamer_get_duration(streamer);
|
||||
LV_LOG_USER("Video is starting");
|
||||
LV_LOG_USER("\tStream resolution %" LV_PRId32 "x%" LV_PRId32, lv_image_get_src_width(streamer),
|
||||
lv_image_get_src_height(streamer));
|
||||
LV_LOG_USER("\tStream duration %" LV_PRIu32, duration);
|
||||
update_duration_label(event_data->duration_label, duration);
|
||||
if(code != LV_EVENT_STATE_CHANGED) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_gstreamer_stream_state_t stream_state = lv_gstreamer_get_stream_state(e);
|
||||
switch(stream_state) {
|
||||
case LV_GSTREAMER_STREAM_STATE_START: {
|
||||
uint32_t duration = lv_gstreamer_get_duration(streamer);
|
||||
LV_LOG_USER("Stream is starting");
|
||||
LV_LOG_USER("\tStream resolution %" LV_PRId32 "x%" LV_PRId32, lv_image_get_src_width(streamer),
|
||||
lv_image_get_src_height(streamer));
|
||||
LV_LOG_USER("\tStream duration %" LV_PRIu32, duration);
|
||||
update_duration_label(event_data->duration_label, duration);
|
||||
break;
|
||||
}
|
||||
case LV_GSTREAMER_STREAM_STATE_END:
|
||||
LV_LOG_USER("Stream is over");
|
||||
lv_label_set_text_static(event_data->button_label, LV_SYMBOL_REFRESH);
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_PLAY:
|
||||
lv_label_set_text_static(event_data->button_label, LV_SYMBOL_PAUSE);
|
||||
break;
|
||||
case LV_GSTREAMER_STREAM_STATE_PAUSE:
|
||||
case LV_GSTREAMER_STREAM_STATE_STOP:
|
||||
lv_label_set_text_static(event_data->button_label, LV_SYMBOL_PLAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <glib.h>
|
||||
#include <gst/gstelementfactory.h>
|
||||
#include "../../core/lv_obj_class_private.h"
|
||||
#include "../../misc/lv_event_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@@ -41,10 +42,11 @@ static void lv_gstreamer_destructor(const lv_obj_class_t * class_p, lv_obj_t * o
|
||||
static void on_decode_pad_added(GstElement * element, GstPad * pad, gpointer user_data);
|
||||
static GstFlowReturn on_new_sample(GstElement * sink, gpointer user_data);
|
||||
static void gstreamer_timer_cb(lv_timer_t * timer);
|
||||
static void gstreamer_poll_bus(lv_gstreamer_t * streamer);
|
||||
static lv_result_t gstreamer_poll_bus(lv_gstreamer_t * streamer);
|
||||
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 VARIABLES
|
||||
@@ -184,7 +186,9 @@ void lv_gstreamer_play(lv_obj_t * obj)
|
||||
GstStateChangeReturn ret = gst_element_set_state(streamer->pipeline, GST_STATE_PLAYING);
|
||||
if(ret == GST_STATE_CHANGE_FAILURE) {
|
||||
LV_LOG_ERROR("Unable to play pipeline");
|
||||
return;
|
||||
}
|
||||
gstreamer_send_state_changed(streamer, LV_GSTREAMER_STREAM_STATE_PLAY);
|
||||
}
|
||||
|
||||
void lv_gstreamer_pause(lv_obj_t * obj)
|
||||
@@ -202,7 +206,9 @@ void lv_gstreamer_pause(lv_obj_t * obj)
|
||||
|
||||
if(ret == GST_STATE_CHANGE_FAILURE) {
|
||||
LV_LOG_ERROR("Unable to pause pipeline");
|
||||
return;
|
||||
}
|
||||
gstreamer_send_state_changed(streamer, LV_GSTREAMER_STREAM_STATE_PAUSE);
|
||||
}
|
||||
|
||||
void lv_gstreamer_stop(lv_obj_t * obj)
|
||||
@@ -219,7 +225,9 @@ void lv_gstreamer_stop(lv_obj_t * obj)
|
||||
GstStateChangeReturn ret = gst_element_set_state(streamer->pipeline, GST_STATE_READY);
|
||||
if(ret == GST_STATE_CHANGE_FAILURE) {
|
||||
LV_LOG_ERROR("Unable to stop pipeline");
|
||||
return;
|
||||
}
|
||||
gstreamer_send_state_changed(streamer, LV_GSTREAMER_STREAM_STATE_STOP);
|
||||
}
|
||||
void lv_gstreamer_set_position(lv_obj_t * obj, uint32_t position)
|
||||
{
|
||||
@@ -362,6 +370,15 @@ void lv_gstreamer_set_rate(lv_obj_t * obj, uint32_t rate)
|
||||
}
|
||||
}
|
||||
|
||||
lv_gstreamer_stream_state_t lv_gstreamer_get_stream_state(lv_event_t * e)
|
||||
{
|
||||
if(!e || e->code != LV_EVENT_STATE_CHANGED) {
|
||||
LV_LOG_WARN("Invalid event");
|
||||
return -1;
|
||||
}
|
||||
return *(lv_gstreamer_stream_state_t *)lv_event_get_param(e);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -383,7 +400,7 @@ static void lv_gstreamer_constructor(const lv_obj_class_t * class_p, lv_obj_t *
|
||||
LV_TRACE_OBJ_CREATE("finished");
|
||||
}
|
||||
|
||||
static void gstreamer_poll_bus(lv_gstreamer_t * streamer)
|
||||
static lv_result_t gstreamer_poll_bus(lv_gstreamer_t * streamer)
|
||||
{
|
||||
GstBus * bus = gst_element_get_bus(streamer->pipeline);
|
||||
GstMessage * msg;
|
||||
@@ -401,7 +418,12 @@ static void gstreamer_poll_bus(lv_gstreamer_t * streamer)
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_EOS:
|
||||
LV_LOG_INFO("End of stream");
|
||||
if(gstreamer_send_state_changed(streamer, LV_GSTREAMER_STREAM_STATE_END) == LV_RESULT_INVALID) {
|
||||
/* Object deleted inside event handler */
|
||||
gst_object_unref(bus);
|
||||
gst_message_unref(msg);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
break;
|
||||
case GST_MESSAGE_STATE_CHANGED: {
|
||||
GstState old_state, new_state;
|
||||
@@ -417,6 +439,7 @@ static void gstreamer_poll_bus(lv_gstreamer_t * streamer)
|
||||
gst_message_unref(msg);
|
||||
}
|
||||
gst_object_unref(bus);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
static void gstreamer_update_frame(lv_gstreamer_t * streamer)
|
||||
@@ -469,6 +492,11 @@ static void gstreamer_update_frame(lv_gstreamer_t * streamer)
|
||||
/* We send the event AFTER setting the image source so that users can query the
|
||||
* resolution on this specific event callback */
|
||||
if(first_frame) {
|
||||
if(gstreamer_send_state_changed(streamer, LV_GSTREAMER_STREAM_STATE_START) == LV_RESULT_INVALID) {
|
||||
/* Object deleted inside event handler */
|
||||
return;
|
||||
}
|
||||
/*Send READY event for backwards compatibility with v9.4*/
|
||||
lv_obj_send_event((lv_obj_t *)streamer, LV_EVENT_READY, streamer);
|
||||
}
|
||||
|
||||
@@ -481,7 +509,9 @@ static void gstreamer_timer_cb(lv_timer_t * timer)
|
||||
return;
|
||||
}
|
||||
|
||||
gstreamer_poll_bus(streamer);
|
||||
if(gstreamer_poll_bus(streamer) == LV_RESULT_INVALID) {
|
||||
return;
|
||||
}
|
||||
gstreamer_update_frame(streamer);
|
||||
}
|
||||
|
||||
@@ -646,4 +676,9 @@ static GstFlowReturn on_new_sample(GstElement * sink, gpointer user_data)
|
||||
g_async_queue_push(streamer->frame_queue, sample);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static lv_result_t gstreamer_send_state_changed(lv_gstreamer_t * streamer, lv_gstreamer_stream_state_t state)
|
||||
{
|
||||
return lv_obj_send_event((lv_obj_t *)streamer, LV_EVENT_STATE_CHANGED, &state);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -73,6 +73,14 @@ typedef enum {
|
||||
LV_GSTREAMER_STATE_PLAYING
|
||||
} lv_gstreamer_state_t;
|
||||
|
||||
typedef enum {
|
||||
LV_GSTREAMER_STREAM_STATE_START,
|
||||
LV_GSTREAMER_STREAM_STATE_PLAY,
|
||||
LV_GSTREAMER_STREAM_STATE_PAUSE,
|
||||
LV_GSTREAMER_STREAM_STATE_STOP,
|
||||
LV_GSTREAMER_STREAM_STATE_END
|
||||
} lv_gstreamer_stream_state_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
@@ -170,6 +178,13 @@ uint8_t lv_gstreamer_get_volume(lv_obj_t * gstreamer);
|
||||
*/
|
||||
void lv_gstreamer_set_rate(lv_obj_t * gstreamer, uint32_t rate);
|
||||
|
||||
/**
|
||||
* Retrieve the stream state from a STATE_CHANGED event callback
|
||||
* @param e pointer to the event
|
||||
* @return the stream state or -1 if `e` is invalid (i.e. NULL or does not match expected event)
|
||||
*/
|
||||
lv_gstreamer_stream_state_t lv_gstreamer_get_stream_state(lv_event_t * e);
|
||||
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
|
||||
Reference in New Issue
Block a user