From 7c82c34b32a0f4a426fbdaffe54b689f4d9fe7bf Mon Sep 17 00:00:00 2001 From: "F. Duncanh" Date: Tue, 22 Apr 2025 21:04:27 -0400 Subject: [PATCH] support for HLS start_position --- renderers/video_renderer.c | 93 ++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/renderers/video_renderer.c b/renderers/video_renderer.c index 7c6af65..704daba 100644 --- a/renderers/video_renderer.c +++ b/renderers/video_renderer.c @@ -46,7 +46,12 @@ static bool use_x11 = false; #endif static bool logger_debug = false; static bool video_terminate = false; -static gint64 start_position = 0; +static gint64 hls_requested_start_position = 0; +static gint64 hls_seek_start = 0; +static gint64 hls_seek_end = 0; +static gint64 hls_duration; +static gboolean hls_seek_enabled; +static gboolean hls_playing; #define NCODECS 2 /* renderers for h264 and h265 */ @@ -205,6 +210,11 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide logger = render_logger; logger_debug = (logger_get_level(logger) >= LOGGER_DEBUG); video_terminate = false; + hls_seek_enabled = FALSE; + hls_playing = FALSE; + hls_seek_start = -1; + hls_seek_end = -1; + hls_duration = -1; /* this call to g_set_application_name makes server_name appear in the X11 display window title bar, */ /* (instead of the program name uxplay taken from (argv[0]). It is only set one time. */ @@ -229,8 +239,8 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide renderer_type[i] = (video_renderer_t *) calloc(1, sizeof(video_renderer_t)); g_assert(renderer_type[i]); renderer_type[i]->autovideo = auto_videosink; - renderer_type[i]->id = i; - renderer_type[i]->bus = NULL; + renderer_type[i]->id = i; + renderer_type[i]->bus = NULL; if (hls_video) { /* use playbin3 to play HLS video: replace "playbin3" by "playbin" to use playbin2 */ switch (playbin_version) { @@ -248,10 +258,9 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide g_assert(renderer_type[i]->pipeline); renderer_type[i]->appsrc = NULL; renderer_type[i]->codec = hls; - /* if we are not using autovideosink, build a videossink based on the stricng "videosink" */ + /* if we are not using autovideosink, build a videossink based on the string "videosink" */ if(strcmp(videosink, "autovideosink")) { - GstElement *playbin_videosink = make_video_sink(videosink, videosink_options); - + GstElement *playbin_videosink = make_video_sink(videosink, videosink_options); if (!playbin_videosink) { logger_log(logger, LOGGER_ERR, "video_renderer_init: failed to create playbin_videosink"); } else { @@ -597,17 +606,41 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void } } + /* monitor hls video position until seek to hls_start_position is achieved */ + if (hls_video && hls_requested_start_position) { + if (strstr(GST_MESSAGE_SRC_NAME(message), "sink")) { + gint64 pos; + if (!GST_CLOCK_TIME_IS_VALID(hls_duration)) { + gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &hls_duration); + } + gst_element_query_position (renderer_type[type]->pipeline, GST_FORMAT_TIME, &pos); + //g_print("HLS position %" GST_TIME_FORMAT " requested_start_position %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT " %s\n", + // GST_TIME_ARGS(pos), GST_TIME_ARGS(hls_requested_start_position), GST_TIME_ARGS(hls_duration), + // (hls_seek_enabled ? "seek enabled" : "seek not enabled")); + if (pos > hls_requested_start_position) { + hls_requested_start_position = 0; + } + if ( hls_requested_start_position && pos < hls_requested_start_position && hls_seek_enabled) { + g_print("***************** seek to hls_requested_start_position %" GST_TIME_FORMAT "\n", GST_TIME_ARGS(hls_requested_start_position)); + if (gst_element_seek_simple (renderer_type[type]->pipeline, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, hls_requested_start_position)) { + hls_requested_start_position = 0; + } + } + } + } + switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_DURATION: - renderer_type[type]->duration = GST_CLOCK_TIME_NONE; + hls_duration = GST_CLOCK_TIME_NONE; break; case GST_MESSAGE_BUFFERING: if (hls_video) { gint percent = -1; gst_message_parse_buffering(message, &percent); - if (percent >= 0) { + if (percent > 0) { renderer_type[type]->buffering_level = percent; - logger_log(logger, LOGGER_DEBUG, "Buffering :%u percent done", percent); + logger_log(logger, LOGGER_DEBUG, "Buffering :%d percent done", percent); if (percent < 100) { gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_PAUSED); } else { @@ -654,12 +687,31 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void } break; case GST_MESSAGE_STATE_CHANGED: - if (logger_debug && strstr(GST_MESSAGE_SRC_NAME(message), "hls-playbin")) { + if (hls_video && logger_debug && strstr(GST_MESSAGE_SRC_NAME(message), "hls-playbin")) { GstState old_state, new_state; gst_message_parse_state_changed (message, &old_state, &new_state, NULL); - g_print ("hls_playbin: Element %s changed state from %s to %s.\n", GST_OBJECT_NAME (message->src), + g_print ("****** hls_playbin: Element %s changed state from %s to %s.\n", GST_OBJECT_NAME (message->src), gst_element_state_get_name (old_state), gst_element_state_get_name (new_state)); + if (new_state != GST_STATE_PLAYING) { + break; + hls_playing = FALSE; + } + hls_playing = TRUE; + GstQuery *query; + query = gst_query_new_seeking(GST_FORMAT_TIME); + if (gst_element_query(renderer->pipeline, query)) { + gst_query_parse_seeking (query, NULL, &hls_seek_enabled, &hls_seek_start, &hls_seek_end); + if (hls_seek_enabled) { + g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (hls_seek_start), GST_TIME_ARGS (hls_seek_end)); + } else { + g_print ("Seeking is DISABLED for this stream.\n"); + } + } else { + g_printerr ("Seeking query failed."); + } + gst_query_unref (query); } if (renderer_type[type]->autovideo) { char *sink = strstr(GST_MESSAGE_SRC_NAME(message), "-actual-sink-"); @@ -797,12 +849,12 @@ bool video_get_playback_info(double *duration, double *position, float *rate) { break; } - if (!GST_CLOCK_TIME_IS_VALID(renderer->duration)) { - if (!gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &renderer->duration)) { + if (!GST_CLOCK_TIME_IS_VALID(hls_duration)) { + if (!gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &hls_duration)) { return true; } } - *duration = ((double) renderer->duration) / GST_SECOND; + *duration = ((double) hls_duration) / GST_SECOND; if (*duration) { if (gst_element_query_position (renderer->pipeline, GST_FORMAT_TIME, &pos) && GST_CLOCK_TIME_IS_VALID(pos)) { @@ -811,26 +863,27 @@ bool video_get_playback_info(double *duration, double *position, float *rate) { } logger_log(logger, LOGGER_DEBUG, "********* video_get_playback_info: position %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT " %s *********", - GST_TIME_ARGS (pos), GST_TIME_ARGS (renderer->duration), gst_element_state_get_name(state)); + GST_TIME_ARGS (pos), GST_TIME_ARGS (hls_duration), gst_element_state_get_name(state)); return true; } void video_renderer_set_start(float position) { int pos_in_micros = (int) (position * SECOND_IN_MICROSECS); - start_position = (gint64) (pos_in_micros * GST_USECOND); - logger_log(logger, LOGGER_DEBUG, "register HLS video start position %f %lld", position, start_position); + hls_requested_start_position = (gint64) (pos_in_micros * GST_USECOND); + logger_log(logger, LOGGER_DEBUG, "register HLS video start position %f %lld", position, + hls_requested_start_position); } void video_renderer_seek(float position) { int pos_in_micros = (int) (position * SECOND_IN_MICROSECS); gint64 seek_position = (gint64) (pos_in_micros * GST_USECOND); /* don't seek to within 1 microsecond of beginning or end of video */ - if (renderer->duration < 2000) return; + if (hls_duration < 2000) return; seek_position = seek_position < 1000 ? 1000 : seek_position; - seek_position = seek_position > renderer->duration - 1000 ? renderer->duration - 1000 : seek_position; + seek_position = seek_position > hls_duration - 1000 ? hls_duration - 1000 : seek_position; g_print("SCRUB: seek to %f secs = %" GST_TIME_FORMAT ", duration = %" GST_TIME_FORMAT "\n", position, - GST_TIME_ARGS(seek_position), GST_TIME_ARGS(renderer->duration)); + GST_TIME_ARGS(seek_position), GST_TIME_ARGS(hls_duration)); gboolean result = gst_element_seek_simple(renderer->pipeline, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), seek_position);