diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c index 52affeaaa8..0950e76206 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c @@ -67,9 +67,11 @@ enum PROP_0, PROP_START_BITRATE, + PROP_LLHLS_ENABLED, }; #define DEFAULT_START_BITRATE 0 +#define DEFAULT_LLHLS_ENABLED TRUE /* Maximum values for mpeg-ts DTS values */ #define MPEG_TS_MAX_PTS (((((guint64)1) << 33) * (guint64)100000) / 9) @@ -233,6 +235,9 @@ gst_hls_demux_set_property (GObject * object, guint prop_id, case PROP_START_BITRATE: demux->start_bitrate = g_value_get_uint (value); break; + case PROP_LLHLS_ENABLED: + demux->llhls_enabled = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -249,6 +254,9 @@ gst_hls_demux_get_property (GObject * object, guint prop_id, case PROP_START_BITRATE: g_value_set_uint (value, demux->start_bitrate); break; + case PROP_LLHLS_ENABLED: + g_value_set_boolean (value, demux->llhls_enabled); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -277,6 +285,11 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass) 0, G_MAXUINT, DEFAULT_START_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_LLHLS_ENABLED, + g_param_spec_boolean ("llhls-enabled", "Enable LL-HLS support", + "Enable support for LL-HLS (Low Latency HLS) downloads", + DEFAULT_LLHLS_ENABLED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + element_class->change_state = GST_DEBUG_FUNCPTR (gst_hls_demux_change_state); gst_element_class_add_static_pad_template (element_class, &sinktemplate); @@ -302,6 +315,7 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass) static void gst_hls_demux2_init (GstHLSDemux * demux) { + demux->llhls_enabled = DEFAULT_LLHLS_ENABLED; demux->keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_mutex_init (&demux->keys_lock); } @@ -497,7 +511,6 @@ gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward, GstFlowReturn ret = GST_FLOW_OK; GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux; - GstM3U8MediaSegment *new_position; GST_DEBUG_OBJECT (stream, "is_variant:%d media:%p current_variant:%p forward:%d ts:%" @@ -514,17 +527,22 @@ gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward, } } - /* FIXME: Allow jumping to partial segments in LL-HLS? */ - new_position = - gst_hls_media_playlist_seek (hls_stream->playlist, forward, flags, ts); - if (new_position) { + /* Allow jumping to partial segments in the last 2 segments in LL-HLS */ + if (hls_stream->llhls_enabled) + flags |= GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL; + + GstM3U8SeekResult seek_result; + if (gst_hls_media_playlist_seek (hls_stream->playlist, forward, flags, ts, + &seek_result)) { if (hls_stream->current_segment) gst_m3u8_media_segment_unref (hls_stream->current_segment); - hls_stream->current_segment = new_position; - hls_stream->in_partial_segments = FALSE; + hls_stream->current_segment = seek_result.segment; + hls_stream->in_partial_segments = seek_result.found_partial_segment; + hls_stream->part_idx = seek_result.part_idx; + hls_stream->reset_pts = TRUE; if (final_ts) - *final_ts = new_position->stream_time; + *final_ts = seek_result.stream_time; } else { GST_WARNING_OBJECT (stream, "Seeking failed"); ret = GST_FLOW_ERROR; @@ -547,6 +565,8 @@ create_common_hls_stream (GstHLSDemux * demux, const gchar * name) GstAdaptiveDemux2Stream *stream; stream = g_object_new (GST_TYPE_HLS_DEMUX_STREAM, "name", name, NULL); + GST_HLS_DEMUX_STREAM (stream)->llhls_enabled = demux->llhls_enabled; + gst_adaptive_demux2_add_stream ((GstAdaptiveDemux *) demux, stream); return stream; @@ -943,9 +963,21 @@ gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) ret = gst_hls_demux_setup_streams (demux); if (simple_media_playlist) { + GstM3U8SeekResult seek_result; + hlsdemux->main_stream->playlist = simple_media_playlist; - hlsdemux->main_stream->current_segment = - gst_hls_media_playlist_get_starting_segment (simple_media_playlist); + + if (!gst_hls_media_playlist_get_starting_segment (simple_media_playlist, + hlsdemux->main_stream->llhls_enabled, &seek_result)) { + GST_DEBUG_OBJECT (hlsdemux->main_stream, + "Failed to find a segment to start at"); + return FALSE; + } + hlsdemux->main_stream->current_segment = seek_result.segment; + hlsdemux->main_stream->in_partial_segments = + seek_result.found_partial_segment; + hlsdemux->main_stream->part_idx = seek_result.part_idx; + setup_initial_playlist (hlsdemux, simple_media_playlist); gst_hls_update_time_mappings (hlsdemux, simple_media_playlist); gst_hls_media_playlist_dump (simple_media_playlist); @@ -1687,6 +1719,7 @@ gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream) if (hls_stream->current_segment == NULL) { /* We can't advance, we just return OK for now and let the base class * trigger a new download (or fail and resync itself) */ + GST_DEBUG_OBJECT (stream, "Can't advance - current_segment is NULL"); return GST_FLOW_OK; } @@ -1905,7 +1938,7 @@ gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream) new_segment = gst_hls_media_playlist_advance_fragment (hlsdemux_stream->playlist, hlsdemux_stream->current_segment, stream->demux->segment.rate > 0, - TRUE /* FIXME: Only in LL-HLS mode */ ); + hlsdemux_stream->llhls_enabled); if (new_segment) { hlsdemux_stream->reset_pts = FALSE; @@ -2225,23 +2258,32 @@ gst_hls_demux_reset_for_lost_sync (GstHLSDemux * hlsdemux) if (hls_stream->is_variant) { GstHLSTimeMap *map; + GstM3U8SeekResult seek_result; + /* Resynchronize the variant stream */ g_assert (stream->current_position != GST_CLOCK_STIME_NONE); - hls_stream->current_segment = - gst_hls_media_playlist_get_starting_segment (hls_stream->playlist); - hls_stream->current_segment->stream_time = stream->current_position; - gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist, - hls_stream->current_segment); - GST_DEBUG_OBJECT (stream, - "Resynced variant playlist to %" GST_STIME_FORMAT, - GST_STIME_ARGS (stream->current_position)); - map = - gst_hls_find_time_map (hlsdemux, - hls_stream->current_segment->discont_sequence); - if (map) - map->internal_time = GST_CLOCK_TIME_NONE; - gst_hls_update_time_mappings (hlsdemux, hls_stream->playlist); - gst_hls_media_playlist_dump (hls_stream->playlist); + if (gst_hls_media_playlist_get_starting_segment (hls_stream->playlist, + hls_stream->llhls_enabled, &seek_result)) { + hls_stream->current_segment = seek_result.segment; + hls_stream->in_partial_segments = seek_result.found_partial_segment; + hls_stream->part_idx = seek_result.part_idx; + + hls_stream->current_segment->stream_time = stream->current_position; + gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist, + hls_stream->current_segment); + GST_DEBUG_OBJECT (stream, + "Resynced variant playlist to %" GST_STIME_FORMAT, + GST_STIME_ARGS (stream->current_position)); + map = + gst_hls_find_time_map (hlsdemux, + hls_stream->current_segment->discont_sequence); + if (map) + map->internal_time = GST_CLOCK_TIME_NONE; + gst_hls_update_time_mappings (hlsdemux, hls_stream->playlist); + gst_hls_media_playlist_dump (hls_stream->playlist); + } else { + GST_ERROR_OBJECT (stream, "Failed to locate a segment to restart at!"); + } } else { /* Force playlist update for the rendition streams, it will resync to the * variant stream on the next round */ @@ -2314,7 +2356,7 @@ gst_hls_demux_stream_update_media_playlist (GstHLSDemux * demux, GST_STIME_ARGS (stream->current_segment->stream_time), GST_STR_NULL (stream->current_segment->uri)); - /* Use best-effort techniques to find the correponding current media segment + /* Use best-effort techniques to find the corresponding current media segment * in the new playlist. This might be off in some cases, but it doesn't matter * since we will be checking the embedded timestamp later */ new_segment = @@ -2496,18 +2538,17 @@ gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream) if (hlsdemux_stream->current_segment == NULL) { GST_LOG_OBJECT (stream, "No current segment"); if (stream->current_position == GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (stream, "Setting up initial segment"); - hlsdemux_stream->current_segment = - gst_hls_media_playlist_get_starting_segment - (hlsdemux_stream->playlist); + GstM3U8SeekResult seek_result; - if (hlsdemux_stream->current_segment->partial_only) { - /* FIXME: We might find an independent partial segment - * that's still old enough (beyond the part_hold_back threshold) - * but closer to the live edge than the start of the segment. This - * check should be done inside get_starting_segment() */ - hlsdemux_stream->in_partial_segments = TRUE; - hlsdemux_stream->part_idx = 0; + GST_DEBUG_OBJECT (stream, "Setting up initial segment"); + + if (gst_hls_media_playlist_get_starting_segment + (hlsdemux_stream->playlist, hlsdemux_stream->llhls_enabled, + &seek_result)) { + hlsdemux_stream->current_segment = seek_result.segment; + hlsdemux_stream->in_partial_segments = + seek_result.found_partial_segment; + hlsdemux_stream->part_idx = seek_result.part_idx; } } else { if (gst_hls_media_playlist_has_lost_sync (hlsdemux_stream->playlist, diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.h index 78f8b6c48b..e0f2512b16 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.h @@ -95,6 +95,11 @@ struct _GstHLSDemuxStream /* A stream either variants or renditions */ gboolean is_variant; + /* A copy of the demuxer flag, stored when the + * stream is created, so it can't change after + * the stream starts downloading things */ + gboolean llhls_enabled; + /* Rendition-specific fields */ GstStreamType rendition_type; /* FIXME: Also used by variant streams */ gchar *lang; @@ -192,6 +197,9 @@ struct _GstHLSDemux2 /* Initial bitrate to use before any bandwidth measurement */ guint start_bitrate; + /* Whether LL-HLS (Low Latency HLS) features are enabled */ + gboolean llhls_enabled; + /* Decryption key cache: url => GstHLSKey */ GHashTable *keys; GMutex keys_lock; diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c index 57f9904348..c9800c3e7b 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c @@ -1215,28 +1215,116 @@ gst_hls_media_playlist_has_same_data (GstHLSMediaPlaylist * self, * segment group that disappears before we're done with it. * We want a segment or partial that contains a keyframe if possible */ -GstM3U8MediaSegment * +gboolean gst_hls_media_playlist_seek (GstHLSMediaPlaylist * playlist, gboolean forward, - GstSeekFlags flags, GstClockTimeDiff ts) + GstSeekFlags flags, GstClockTimeDiff ts, GstM3U8SeekResult * seek_result) { gboolean snap_nearest = (flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST; gboolean snap_after = (flags & GST_SEEK_FLAG_SNAP_AFTER) == GST_SEEK_FLAG_SNAP_AFTER; + gboolean want_keyunit = (flags & GST_SEEK_FLAG_KEY_UNIT); guint idx; GstM3U8MediaSegment *res = NULL; + guint res_part_idx = 0; + GstClockTime partial_window_start = GST_CLOCK_TIME_NONE; - GST_DEBUG ("ts:%" GST_STIME_FORMAT " forward:%d playlist uri: %s", + GST_DEBUG ("target ts:%" GST_STIME_FORMAT " forward:%d playlist uri: %s", GST_STIME_ARGS (ts), forward, playlist->uri); + /* Can't seek if there's no segments */ + if (playlist->segments->len < 1) + return FALSE; + + /* Calculate the threshold at which we might start inspecting partial segments */ + if (flags & GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL) { + GstM3U8MediaSegment *last_seg = + g_ptr_array_index (playlist->segments, playlist->segments->len - 1); + GstClockTime playlist_end = last_seg->stream_time + last_seg->duration; + + if (playlist_end >= 2 * playlist->targetduration) + partial_window_start = playlist_end - 2 * playlist->targetduration; + else + partial_window_start = last_seg->stream_time; + + GST_DEBUG ("Partial segment threshold %" GST_TIME_FORMAT, + GST_TIME_ARGS (partial_window_start)); + } + for (idx = 0; idx < playlist->segments->len; idx++) { GstM3U8MediaSegment *cand = g_ptr_array_index (playlist->segments, idx); - /* If only full segments was request, skip any segment that - * only has EXT-X-PARTs attached */ - if (cand->partial_only && !(flags & GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL)) - continue; + if (flags & GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL && + GST_CLOCK_TIME_IS_VALID (partial_window_start) && + cand->stream_time + cand->duration > partial_window_start) { + /* Permitted to land at a partial segment, but only do so if + * they are in the last 2 target durations of the playlist, so we can + * be fairly sure we'll have to time download them all before + * they get removed. + * + * 6.2.2: EXT-X-PART tags SHOULD be removed from the Playlist after they are + * greater than three Target Durations from the end of the Playlist. + * Clients MUST be able to download the Partial Segment for at least + * three Target Durations after the EXT-X-PART tag is removed from the + * Playlist. + */ + if (cand->partial_segments != NULL) { + guint part_idx; + guint last_independent_idx = 0; + for (part_idx = 0; part_idx < cand->partial_segments->len; part_idx++) { + GstM3U8PartialSegment *part = + g_ptr_array_index (cand->partial_segments, part_idx); + + GST_LOG ("Inspecting partial segment sn:%" G_GINT64_FORMAT + " idx %u stream_time:%" GST_STIME_FORMAT " duration:%" + GST_TIME_FORMAT, cand->sequence, part_idx, + GST_STIME_ARGS (part->stream_time), + GST_TIME_ARGS (part->duration)); + + if ((forward & snap_after) || snap_nearest) { + if (!want_keyunit || part->independent) { + if (part->stream_time >= ts || + (snap_nearest + && (ts - part->stream_time < part->duration / 2))) { + res = cand; + res_part_idx = part_idx; + goto partial_seg_out; + } + } + } else if (!forward && snap_after) { + GstClockTime next_pos = cand->stream_time + cand->duration; + + if (!want_keyunit || part->independent) { + if (next_pos <= ts && ts < next_pos + cand->duration) { + res = cand; + res_part_idx = part_idx; + goto partial_seg_out; + } + } + } else if (part->stream_time <= ts + && ts < part->stream_time + part->duration) { + res = cand; + if (!want_keyunit || part->independent) + res_part_idx = part_idx; + else + res_part_idx = last_independent_idx; + goto partial_seg_out; + } + + if (part->independent) + last_independent_idx = part_idx; + } + } + } else if (cand->partial_only) { + /* If only full segments were requested or we're still outside the partial segment + * window, skip the last segment if it only has EXT-X-PARTs attached */ + continue; + } + + /* For full segment alignment, we ignore the KEY_UNIT flag and assume + * all segments have a keyframe, since HLS doesn't give us reliable info + * about that */ if ((forward & snap_after) || snap_nearest) { if (cand->stream_time >= ts || (snap_nearest && (ts - cand->stream_time < cand->duration / 2))) { @@ -1262,12 +1350,37 @@ out: GST_DEBUG ("Returning segment sn:%" G_GINT64_FORMAT " stream_time:%" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, res->sequence, GST_STIME_ARGS (res->stream_time), GST_TIME_ARGS (res->duration)); - gst_m3u8_media_segment_ref (res); - } else { - GST_DEBUG ("Couldn't find a match"); + + seek_result->stream_time = res->stream_time; + seek_result->segment = gst_m3u8_media_segment_ref (res); + seek_result->found_partial_segment = res->partial_only; + seek_result->part_idx = 0; + return TRUE; } - return res; + GST_DEBUG ("Couldn't find a match"); + return FALSE; + +partial_seg_out: + if (res && res->partial_segments != NULL + && res_part_idx < res->partial_segments->len) { + GstM3U8PartialSegment *part = + g_ptr_array_index (res->partial_segments, res_part_idx); + + GST_DEBUG ("Returning partial segment sn:%" G_GINT64_FORMAT + " part_idx %u stream_time:%" GST_STIME_FORMAT " duration:%" + GST_TIME_FORMAT, res->sequence, res_part_idx, + GST_STIME_ARGS (part->stream_time), GST_TIME_ARGS (part->duration)); + + seek_result->stream_time = part->stream_time; + seek_result->segment = gst_m3u8_media_segment_ref (res); + seek_result->found_partial_segment = TRUE; + seek_result->part_idx = res_part_idx; + return TRUE; + } + + GST_DEBUG ("Couldn't find a match"); + return FALSE; } static gboolean @@ -1780,10 +1893,11 @@ gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * playlist, return res; } -GstM3U8MediaSegment * -gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self) +gboolean +gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self, + gboolean low_latency, GstM3U8SeekResult * seek_result) { - GstM3U8MediaSegment *res; + GstM3U8MediaSegment *res = NULL; GST_DEBUG ("playlist %s", self->uri); @@ -1791,20 +1905,79 @@ gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self) /* For non-live, we just grab the first one */ res = g_ptr_array_index (self->segments, 0); } else { - /* Live playlist */ - res = - g_ptr_array_index (self->segments, - MAX ((gint) self->segments->len - GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE - - 1, 0)); + GstClockTime hold_back = GST_CLOCK_TIME_NONE; + /* Live playlist. If low-latency, use the PART-HOLD-BACK specified distance + * from the end, otherwise HOLD-BACK distance or if that's not provided, + * then 3 target durations */ + if (low_latency) { + if (GST_CLOCK_TIME_IS_VALID (self->part_hold_back)) + hold_back = self->part_hold_back; + else if (GST_CLOCK_TIME_IS_VALID (self->partial_targetduration)) + hold_back = 3 * self->partial_targetduration; + } else { + if (GST_CLOCK_TIME_IS_VALID (self->hold_back)) + hold_back = self->hold_back; + else if (GST_CLOCK_TIME_IS_VALID (self->targetduration)) + hold_back = 3 * self->targetduration; + } + + if (GST_CLOCK_TIME_IS_VALID (hold_back)) { + GstSeekFlags flags = GST_SEEK_FLAG_SNAP_BEFORE | GST_SEEK_FLAG_KEY_UNIT; + GstM3U8MediaSegment *last_seg = + g_ptr_array_index (self->segments, self->segments->len - 1); + GstClockTime playlist_duration = + last_seg->stream_time + last_seg->duration; + GstClockTime target_ts; + + /* Clamp the hold back so we don't go below zero */ + if (hold_back > playlist_duration) + hold_back = playlist_duration; + + target_ts = playlist_duration - hold_back; + + GST_DEBUG ("Hold back is %" GST_TIME_FORMAT + " Looking for a segment before %" GST_TIME_FORMAT, + GST_TIME_ARGS (hold_back), GST_TIME_ARGS (target_ts)); + + if (low_latency) + flags |= GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL; + + if (gst_hls_media_playlist_seek (self, TRUE, flags, target_ts, + seek_result)) { +#ifndef GST_DISABLE_GST_DEBUG + GstClockTime distance_from_edge = + playlist_duration - seek_result->stream_time; + + GST_DEBUG ("Found starting position %" GST_TIME_FORMAT " which is %" + GST_TIME_FORMAT " from the live edge", + GST_TIME_ARGS (seek_result->stream_time), + GST_TIME_ARGS (distance_from_edge)); +#endif + return TRUE; + } + } + + /* Worst case fallback, start 3 fragments from the end */ + if (res == NULL) { + res = + g_ptr_array_index (self->segments, + MAX ((gint) self->segments->len - + GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE - 1, 0)); + } } if (res) { GST_DEBUG ("Using segment sn:%" G_GINT64_FORMAT " dsn:%" G_GINT64_FORMAT, res->sequence, res->discont_sequence); - gst_m3u8_media_segment_ref (res); + + seek_result->stream_time = res->stream_time; + seek_result->segment = gst_m3u8_media_segment_ref (res); + seek_result->found_partial_segment = FALSE; + seek_result->part_idx = 0; + return TRUE; } - return res; + return FALSE; } /* Calls this to carry over stream time, DSN, ... from one playlist to another. diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h index cee63e34bd..49b075ba95 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h @@ -290,8 +290,9 @@ gst_hls_media_playlist_advance_fragment (GstHLSMediaPlaylist * m3u8, gboolean forward, gboolean allow_partial_only_segment); -GstM3U8MediaSegment * -gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist *self); +gboolean +gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist *self, gboolean low_latency, + GstM3U8SeekResult *seek_result); GstClockTime gst_hls_media_playlist_get_duration (GstHLSMediaPlaylist * m3u8); @@ -311,11 +312,12 @@ gboolean gst_hls_media_playlist_has_lost_sync (GstHLSMediaPlaylist * m3u8, GstClockTime position); -GstM3U8MediaSegment * -gst_hls_media_playlist_seek (GstHLSMediaPlaylist *playlist, +gboolean +gst_hls_media_playlist_seek (GstHLSMediaPlaylist *playlist, gboolean forward, GstSeekFlags flags, - GstClockTimeDiff ts); + GstClockTimeDiff ts, + GstM3U8SeekResult *seek_result); gboolean gst_hls_media_playlist_find_position (GstHLSMediaPlaylist *playlist, diff --git a/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c b/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c index 73e0cf8dab..c59ef876ea 100644 --- a/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c +++ b/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c @@ -675,11 +675,15 @@ GST_START_TEST (test_advance_fragment) { GstHLSMediaPlaylist *pl; GstM3U8MediaSegment *mf; + GstM3U8SeekResult seek_result; pl = load_m3u8 (BYTE_RANGES_PLAYLIST); /* Check the next fragment */ - mf = gst_hls_media_playlist_get_starting_segment (pl); + fail_unless (gst_hls_media_playlist_get_starting_segment (pl, FALSE, + &seek_result) == TRUE); + + mf = seek_result.segment; fail_unless (mf != NULL); assert_equals_int (mf->discont, FALSE); assert_equals_string (mf->uri, "http://media.example.com/all.ts");