From 382b78251bef9498fd486fd037a5a5d8c84410f7 Mon Sep 17 00:00:00 2001 From: "F. Duncanh" Date: Thu, 18 Dec 2025 13:08:23 -0500 Subject: [PATCH] various hls improvements (seek to start_pos --- lib/http_handlers.h | 18 ++++----- lib/raop.h | 2 + renderers/video_renderer.c | 78 +++++++++++++++++++++++--------------- renderers/video_renderer.h | 2 +- uxplay.cpp | 7 ++-- 5 files changed, 61 insertions(+), 46 deletions(-) diff --git a/lib/http_handlers.h b/lib/http_handlers.h index 2363876..3f46af7 100644 --- a/lib/http_handlers.h +++ b/lib/http_handlers.h @@ -400,8 +400,8 @@ http_handler_playback_info(raop_conn_t *conn, http_request_t *request, http_resp playback_info.num_seekable_time_ranges = 1; time_range_t time_ranges_seekable[1]; - time_ranges_seekable[0].start = 0.0; - time_ranges_seekable[0].duration = playback_info.position; + time_ranges_seekable[0].start = playback_info.seek_start; + time_ranges_seekable[0].duration = playback_info.seek_duration; playback_info.seekableTimeRanges = (void *) &time_ranges_seekable; *response_datalen = create_playback_info_plist_xml(&playback_info, response_data); @@ -727,7 +727,6 @@ delete_short_playlist(raop_t *raop, int id) { static void http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *response, char **response_data, int *response_datalen) { - raop_t *raop = conn->raop; char* playback_location = NULL; char* client_proc_name = NULL; @@ -785,10 +784,12 @@ http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *r /* check if playlist is already downloaded and stored (may have been interruoted by advertisements ) */ if (id >= 0) { - printf("use: airplay_video[%d] %p %s %s\n", id, raop->airplay_video[id], playback_uuid, get_playback_uuid(raop->airplay_video[id])); + //printf("====use EXISTING airplay_video[%d] %p %s %s\n", id, raop->airplay_video[id], playback_uuid, get_playback_uuid(raop->airplay_video[id])); + plist_mem_free(playback_uuid); + plist_free(req_root_node); int current_video = raop->current_video; if (current_video >= 0 && current_video != id) { - delete_short_playlist(raop, current_video); + delete_short_playlist(raop, current_video); } raop->current_video = id; airplay_video = hls_get_current_video(raop); @@ -796,11 +797,10 @@ http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *r set_apple_session_id(airplay_video, apple_session_id, strlen(apple_session_id)); float resume_pos = get_resume_position_seconds(airplay_video); float start_pos = get_start_position_seconds(airplay_video); + //printf("========= %f ============call on_video_play===== %f ==========\n", start_pos, resume_pos); raop->callbacks.on_video_play(raop->callbacks.cls, get_playback_location(airplay_video), resume_pos > start_pos ? resume_pos : start_pos); - plist_mem_free(playback_uuid); - plist_free(req_root_node); return; } @@ -836,7 +836,6 @@ http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *r set_playback_uuid(airplay_video, playback_uuid, strlen(playback_uuid)); plist_mem_free (playback_uuid); count++; - printf("created new airplay_video %p %s\n\n", airplay_video, get_playback_uuid(airplay_video)); } else { logger_log(raop->logger, LOGGER_ERR, "failed to allocate airplay_video[%d]\n", id); exit(-1); @@ -944,7 +943,6 @@ http_handler_hls(raop_conn_t *conn, http_request_t *request, http_response_t *r raop_t *raop = conn->raop; if (raop->current_video == -1) { logger_log(raop->logger, LOGGER_ERR,"airplay_video playlist not found"); - *response_datalen = 0; http_response_init(response, "HTTP/1.1", 404, "Not Found"); return; } @@ -973,7 +971,6 @@ http_handler_hls(raop_conn_t *conn, http_request_t *request, http_response_t *r *response_datalen = (int ) len; } else { logger_log(raop->logger, LOGGER_ERR,"requested master playlist %s not found", url); - *response_datalen = 0; } } else { @@ -988,7 +985,6 @@ http_handler_hls(raop_conn_t *conn, http_request_t *request, http_response_t *r "Requested media_playlist %s has %5d chunks, total duration %9.3f secs", url, chunks, duration); } else { logger_log(raop->logger, LOGGER_ERR,"requested media playlist %s not found", url); - *response_datalen = 0; } } diff --git a/lib/raop.h b/lib/raop.h index 38038cd..0facd36 100644 --- a/lib/raop.h +++ b/lib/raop.h @@ -41,6 +41,8 @@ typedef struct playback_info_s { uint32_t stallcount; double duration; double position; + double seek_start; + double seek_duration; float rate; bool ready_to_play; bool playback_buffer_empty; diff --git a/renderers/video_renderer.c b/renderers/video_renderer.c index 36e1b37..9b98bf1 100644 --- a/renderers/video_renderer.c +++ b/renderers/video_renderer.c @@ -732,7 +732,20 @@ static void get_stream_status_name(GstStreamStatusType type, char *name, size_t return; } } - + +static void hls_video_seek_to_start_position(GstElement *pipeline) { + if (hls_requested_start_position && hls_seek_enabled && hls_requested_start_position >= hls_seek_start + && hls_requested_start_position <= hls_seek_end) { + g_print("***************** seek to hls_requested_start_position %" GST_TIME_FORMAT "\n", GST_TIME_ARGS(hls_requested_start_position)); + if (gst_element_seek_simple (pipeline, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, hls_requested_start_position)) { + hls_requested_start_position = 0; + } else { + g_print("*** seek to requested_start_position failed\n"); + } + } +} + static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) { GstState old_state, new_state; const gchar no_state[] = ""; @@ -764,6 +777,14 @@ static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *m video_renderer_t *renderer = renderer_type[type]; + gint64 pos = -1; + if (hls_video) { + gst_element_query_position (renderer_type[type]->pipeline, GST_FORMAT_TIME, &pos); + if (hls_requested_start_position && pos >= hls_requested_start_position) { + hls_requested_start_position = 0; + } + } + if (logger_debug) { gchar *name = NULL; GstElement *element = NULL; @@ -776,10 +797,6 @@ static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *m old_state_name = name; new_state_name = type_name; } - gint64 pos = -1; - if (hls_video) { - gst_element_query_position (renderer->pipeline, GST_FORMAT_TIME, &pos); - } if (GST_CLOCK_TIME_IS_VALID(pos)) { g_print("GStreamer %s bus message %s %s %s %s; position: %" GST_TIME_FORMAT "\n" ,renderer->codec, GST_MESSAGE_SRC_NAME(message), GST_MESSAGE_TYPE_NAME(message), old_state_name, new_state_name, GST_TIME_ARGS(pos)); @@ -792,28 +809,12 @@ static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *m } } - /* 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 = -1; - if (!GST_CLOCK_TIME_IS_VALID(hls_duration)) { - gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &hls_duration); - } - gst_element_query_position (renderer->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->pipeline, GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, hls_requested_start_position)) { - hls_requested_start_position = 0; - } - } - } + if (hls_video && !GST_CLOCK_TIME_IS_VALID(hls_duration)) { + gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &hls_duration); + } + + if (hls_requested_start_position && hls_seek_enabled) { + hls_video_seek_to_start_position(renderer->pipeline); } switch (GST_MESSAGE_TYPE (message)) { @@ -881,16 +882,18 @@ static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *m } break; case GST_MESSAGE_STATE_CHANGED: - if (hls_video && logger_debug && strstr(GST_MESSAGE_SRC_NAME(message), "hls-playbin")) { + if (hls_video && strstr(GST_MESSAGE_SRC_NAME(message), "hls-playbin")) { GstState old_state, new_state; gst_message_parse_state_changed (message, &old_state, &new_state, NULL); + if (logger_debug) { 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; - } + break; + } hls_playing = TRUE; GstQuery *query = NULL; query = gst_query_new_seeking(GST_FORMAT_TIME); @@ -906,6 +909,11 @@ static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *m g_printerr ("Seeking query failed."); } gst_query_unref (query); + + if (hls_requested_start_position && hls_seek_enabled) { + hls_video_seek_to_start_position(renderer_type[type]->pipeline); + } + } if (renderer->autovideo) { char *sink = strstr(GST_MESSAGE_SRC_NAME(message), "-actual-sink-"); @@ -1018,17 +1026,25 @@ int video_renderer_choose_codec (bool video_is_jpeg, bool video_is_h265) { } return 0; } + -bool video_get_playback_info(double *duration, double *position, float *rate, bool *buffer_empty, bool *buffer_full) { +bool video_get_playback_info(double *duration, double *position, double *seek_start, double *seek_duration, float *rate, bool *buffer_empty, bool *buffer_full) { gint64 pos = 0; GstState state; *duration = 0.0; *position = -1.0; + *seek_start = 0.0; + *seek_duration = 0.0; *rate = 0.0f; if (!renderer) { return true; } + if (hls_seek_enabled) { + *seek_start = ((double) hls_seek_start) / GST_SECOND; + *seek_duration = ((double) (hls_seek_end - hls_seek_start)) / GST_SECOND; + } + *buffer_empty = (bool) hls_buffer_empty; *buffer_full = (bool) hls_buffer_full; gst_element_get_state(renderer->pipeline, &state, NULL, 0); diff --git a/renderers/video_renderer.h b/renderers/video_renderer.h index ab4618e..73b13a1 100644 --- a/renderers/video_renderer.h +++ b/renderers/video_renderer.h @@ -68,7 +68,7 @@ unsigned int video_renderer_listen(void *loop, int id); void video_renderer_destroy (); void video_renderer_size(float *width_source, float *height_source, float *width, float *height); bool waiting_for_x11_window(); -bool video_get_playback_info(double *duration, double *position, float *rate, bool *buffer_empty, bool *buffer_full); +bool video_get_playback_info(double *duration, double *position, double *seek_start, double *seek_duration, float *rate, bool *buffer_empty, bool *buffer_full); int video_renderer_choose_codec (bool video_is_jpeg, bool video_is_h265); unsigned int video_renderer_listen(void *loop, int id); bool video_renderer_eos_watch(); diff --git a/uxplay.cpp b/uxplay.cpp index 4dc4c91..a2f7764 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -2496,7 +2496,7 @@ extern "C" void on_video_play(void *cls, const char* location, const float start url.append(location); relaunch_video = true; preserve_connections = true; - LOGD("********************on_video_play: location = %s***********************", url.c_str()); + LOGI("********************on_video_play: location = %s*** start position %f ********************", url.c_str(), start_position); reset_loop = true; } @@ -2519,12 +2519,12 @@ extern "C" void on_video_rate(void *cls, const float rate) { extern "C" float on_video_playlist_remove (void *cls) { - double duration, position; + double duration, position, seek_start, seek_end; float rate; bool buffer_empty, buffer_full; LOGI("************************* on_video_playlist_remove\n"); video_renderer_pause(); - video_get_playback_info(&duration, &position, &rate, &buffer_empty, &buffer_full); + video_get_playback_info(&duration, &position, &seek_start, &seek_end, &rate, &buffer_empty, &buffer_full); return (float) position; } @@ -2536,6 +2536,7 @@ extern "C" float on_video_playlist_remove (void *cls) { extern "C" void on_video_acquire_playback_info (void *cls, playback_info_t *playback_info) { int buffering_level; bool still_playing = video_get_playback_info(&playback_info->duration, &playback_info->position, + &playback_info->seek_start, &playback_info->seek_duration, &playback_info->rate, &playback_info->playback_buffer_empty, &playback_info->playback_buffer_full);