From 2fe641353d2c097a183ab22bb426e46e9020c6e3 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 21 Aug 2022 04:31:53 +1000 Subject: [PATCH] adaptivedemux2: Refactor stream methods into the stream Unlike the legacy elements, GstAdaptiveDemuxStream is a GObject now, so a bunch of things that were actually stream methods on the parent demux object can directly become stream methods now. Move the stream class out to a header of its own. Part-of: --- .../ext/adaptivedemux2/dash/gstdashdemux.c | 141 +++-- .../adaptivedemux2/gstadaptivedemux-private.h | 21 +- .../adaptivedemux2/gstadaptivedemux-stream.c | 576 ++++++++++++++++-- .../adaptivedemux2/gstadaptivedemux-stream.h | 348 +++++++++++ .../adaptivedemux2/gstadaptivedemux-types.h | 55 ++ .../ext/adaptivedemux2/gstadaptivedemux.c | 541 +--------------- .../ext/adaptivedemux2/gstadaptivedemux.h | 326 +--------- .../ext/adaptivedemux2/hls/gsthlsdemux.c | 154 ++--- 8 files changed, 1139 insertions(+), 1023 deletions(-) create mode 100644 subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.h create mode 100644 subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-types.h diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c index 691a31b3d2..382093757b 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c @@ -351,18 +351,12 @@ static void gst_dash_demux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_dash_demux_dispose (GObject * obj); -/* GstAdaptiveDemux */ -static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux); -static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux); -static void gst_dash_demux_reset (GstAdaptiveDemux * ademux); -static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux, - GstBuffer * buf); -static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek); +/* GstAdaptiveDemuxStream */ static GstFlowReturn gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream); -static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream * - stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts, - GstClockTimeDiff * final_ts); +static GstClockTime +gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * + stream); static gboolean gst_dash_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream); static GstFlowReturn @@ -371,25 +365,35 @@ static gboolean gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemux2Stream * stream); static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream, guint64 bitrate); +static GstClockTime +gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * + stream); +static GstFlowReturn +gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream, + GstBuffer * buffer); +static gboolean gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream * + stream); +static GstFlowReturn +gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream); +static gboolean +gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream); + +/* GstAdaptiveDemux */ +static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux); +static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux); +static void gst_dash_demux_reset (GstAdaptiveDemux * ademux); +static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux, + GstBuffer * buf); +static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek); +static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream * + stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts, + GstClockTimeDiff * final_ts); static gint64 gst_dash_demux_get_manifest_update_interval (GstAdaptiveDemux * demux); static GstFlowReturn gst_dash_demux_update_manifest_data (GstAdaptiveDemux * demux, GstBuffer * buf); -static GstClockTime -gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * - stream); static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux); static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux); -static GstFlowReturn gst_dash_demux_data_received (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); -static gboolean -gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static GstFlowReturn -gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static gboolean gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream * - stream); /* GstDashDemux2 */ static gboolean gst_dash_demux_setup_all_streams (GstDashDemux2 * demux); @@ -464,8 +468,32 @@ static void gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class = + GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass); gobject_class->finalize = gst_dash_demux_stream_finalize; + + adaptivedemux2stream_class->update_fragment_info = + gst_dash_demux_stream_update_fragment_info; + adaptivedemux2stream_class->has_next_fragment = + gst_dash_demux_stream_has_next_fragment; + adaptivedemux2stream_class->advance_fragment = + gst_dash_demux_stream_advance_fragment; + adaptivedemux2stream_class->get_fragment_waiting_time = + gst_dash_demux_stream_get_fragment_waiting_time; + adaptivedemux2stream_class->select_bitrate = + gst_dash_demux_stream_select_bitrate; + adaptivedemux2stream_class->get_presentation_offset = + gst_dash_demux_stream_get_presentation_offset; + + adaptivedemux2stream_class->start_fragment = + gst_dash_demux_stream_fragment_start; + adaptivedemux2stream_class->finish_fragment = + gst_dash_demux_stream_fragment_finished; + adaptivedemux2stream_class->data_received = + gst_dash_demux_stream_data_received; + adaptivedemux2stream_class->need_another_chunk = + gst_dash_demux_stream_need_another_chunk; } @@ -547,11 +575,10 @@ gst_dash_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start, } static GstClockTime -gst_dash_demux_get_presentation_offset (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream) { GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream; - GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux); + GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux); return gst_mpd_client2_get_stream_presentation_offset (dashdemux->client, dashstream->index); @@ -628,30 +655,12 @@ gst_dash_demux2_class_init (GstDashDemux2Class * klass) gstadaptivedemux_class->has_next_period = gst_dash_demux_has_next_period; gstadaptivedemux_class->advance_period = gst_dash_demux_advance_period; - gstadaptivedemux_class->stream_has_next_fragment = - gst_dash_demux_stream_has_next_fragment; - gstadaptivedemux_class->stream_advance_fragment = - gst_dash_demux_stream_advance_fragment; - gstadaptivedemux_class->stream_get_fragment_waiting_time = - gst_dash_demux_stream_get_fragment_waiting_time; + gstadaptivedemux_class->stream_seek = gst_dash_demux_stream_seek; - gstadaptivedemux_class->stream_select_bitrate = - gst_dash_demux_stream_select_bitrate; - gstadaptivedemux_class->stream_update_fragment_info = - gst_dash_demux_stream_update_fragment_info; gstadaptivedemux_class->get_live_seek_range = gst_dash_demux_get_live_seek_range; - gstadaptivedemux_class->get_presentation_offset = - gst_dash_demux_get_presentation_offset; gstadaptivedemux_class->get_period_start_time = gst_dash_demux_get_period_start_time; - - gstadaptivedemux_class->start_fragment = gst_dash_demux_stream_fragment_start; - gstadaptivedemux_class->finish_fragment = - gst_dash_demux_stream_fragment_finished; - gstadaptivedemux_class->data_received = gst_dash_demux_data_received; - gstadaptivedemux_class->need_another_chunk = - gst_dash_demux_need_another_chunk; } static void @@ -1827,10 +1836,10 @@ gst_dash_demux_stream_get_target_time (GstDashDemux2 * dashdemux, GstClockTime deadline; GstClockTime upstream_earliest_time; GstClockTime earliest_time = GST_CLOCK_TIME_NONE; - gdouble play_rate = gst_adaptive_demux_play_rate (stream->demux); + gdouble play_rate = gst_adaptive_demux_play_rate (demux); GstClockTime period_start = gst_dash_demux_get_period_start_time (demux); GstClockTime pts_offset = - gst_dash_demux_get_presentation_offset (demux, stream); + gst_dash_demux_stream_get_presentation_offset (stream); g_assert (min_skip > 0); @@ -2706,10 +2715,9 @@ _gst_buffer_split (GstBuffer * buffer, gint offset, gsize size) } static gboolean -gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream * stream) { - GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux); + GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux); GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream; GST_LOG_OBJECT (stream, "Actual position %" GST_TIME_FORMAT, @@ -2725,7 +2733,7 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux, * buffer. We need offsets to be consistent between moof and mdat */ if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units - && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux) + && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux) && dashstream->active_stream->mimeType == GST_STREAM_VIDEO) stream->discont = TRUE; @@ -2733,11 +2741,10 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux, } static GstFlowReturn -gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream) { GstClockTime consumed_duration; - GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux); + GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux); GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream; /* We need to mark every first buffer of a key unit as discont, @@ -2747,7 +2754,7 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux, * buffer. We need offsets to be consistent between moof and mdat */ if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units - && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux) + && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux) && dashstream->active_stream->mimeType == GST_STREAM_VIDEO) stream->discont = TRUE; @@ -2774,20 +2781,20 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux, consumed_duration = (stream->fragment.stream_time + stream->fragment.duration) - stream->current_position; - GST_LOG_OBJECT (demux, "Consumed duration after seeking: %" + GST_LOG_OBJECT (stream, "Consumed duration after seeking: %" GST_TIMEP_FORMAT, &consumed_duration); } else { consumed_duration = stream->fragment.duration; } - return gst_adaptive_demux2_stream_advance_fragment (demux, stream, + return gst_adaptive_demux2_stream_advance_fragment (stream, consumed_duration); } static gboolean -gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream * stream) +gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream) { - GstDashDemux2 *dashdemux = (GstDashDemux2 *) stream->demux; + GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux); GstAdaptiveDemux *demux = stream->demux; GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream; gboolean playing_forward = (demux->segment.rate > 0.0); @@ -3371,9 +3378,9 @@ gst_dash_demux_find_sync_samples (GstAdaptiveDemux * demux, static GstFlowReturn -gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_dash_demux_stream_handle_isobmff (GstAdaptiveDemux2Stream * stream) { + GstAdaptiveDemux *demux = stream->demux; GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream; GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buffer; @@ -3527,22 +3534,22 @@ gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux, if (sidx_advance) { ret = - gst_adaptive_demux2_stream_advance_fragment (demux, stream, + gst_adaptive_demux2_stream_advance_fragment (stream, SIDX_CURRENT_ENTRY (dash_stream)->duration); if (ret != GST_FLOW_OK) return ret; /* If we still have data available, recurse and use it up if possible */ if (gst_adapter_available (dash_stream->adapter) > 0) - return gst_dash_demux_handle_isobmff (demux, stream); + return gst_dash_demux_stream_handle_isobmff (stream); } return ret; } static GstFlowReturn -gst_dash_demux_data_received (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer) +gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream, + GstBuffer * buffer) { GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream; GstFlowReturn ret = GST_FLOW_OK; @@ -3574,7 +3581,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux, if (dash_stream->is_isobmff || stream->downloading_index) { /* SIDX index is also ISOBMMF */ - ret = gst_dash_demux_handle_isobmff (demux, stream); + ret = gst_dash_demux_stream_handle_isobmff (stream); } else if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) { gsize available; @@ -3622,7 +3629,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux, if (has_next) { GstFlowReturn new_ret; new_ret = - gst_adaptive_demux2_stream_advance_fragment (demux, stream, + gst_adaptive_demux2_stream_advance_fragment (stream, SIDX_CURRENT_ENTRY (dash_stream)->duration); /* only overwrite if it was OK before */ diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-private.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-private.h index cc62325171..630ba715c2 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-private.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-private.h @@ -25,9 +25,16 @@ #ifndef _GST_ADAPTIVE_DEMUX_PRIVATE_H_ #define _GST_ADAPTIVE_DEMUX_PRIVATE_H_ +#include #include #include +#include "gstadaptivedemux-types.h" +#include "gstadaptivedemux.h" +#include "gstadaptivedemuxutils.h" + +G_BEGIN_DECLS + #define NUM_LOOKBACK_FRAGMENTS 3 #define MAX_DOWNLOAD_ERROR_COUNT 3 @@ -166,10 +173,8 @@ GstFlowReturn gst_adaptive_demux_update_manifest (GstAdaptiveDemux *demux); void gst_adaptive_demux2_stream_wants_manifest_update (GstAdaptiveDemux * demux); void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err); -GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux * - demux, GstAdaptiveDemux2Stream * stream); -GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); +GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * stream); +GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream); GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux); gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux); @@ -177,10 +182,7 @@ gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux); void gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream); void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream); -gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); +gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream); GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts); @@ -193,6 +195,7 @@ gboolean gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux); void gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux); +GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream); void gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream); gboolean gst_adaptive_demux_handle_lost_sync (GstAdaptiveDemux * demux); @@ -240,4 +243,6 @@ GstFlowReturn gst_adaptive_demux_period_combine_stream_flows (GstAdap gboolean gst_adaptive_demux_period_has_pending_tracks (GstAdaptiveDemuxPeriod * period); void gst_adaptive_demux_period_check_input_wakeup_locked (GstAdaptiveDemuxPeriod * period, GstClockTimeDiff current_output_position); +G_END_DECLS + #endif diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c index 11cdcbe9c8..60eacebd5b 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c @@ -27,7 +27,7 @@ #include "config.h" #endif -#include "gstadaptivedemux.h" +#include "gstadaptivedemux-stream.h" #include "gstadaptivedemux-private.h" #include @@ -38,6 +38,18 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug); static void gst_adaptive_demux2_stream_finalize (GObject * object); static void gst_adaptive_demux2_stream_error (GstAdaptiveDemux2Stream * stream); +static GstFlowReturn +gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream * + stream, GstBuffer * buffer); +static GstFlowReturn +gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream * + stream); + +guint64 +gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream * + stream); +static void gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream + * stream); #define gst_adaptive_demux2_stream_parent_class parent_class G_DEFINE_ABSTRACT_TYPE (GstAdaptiveDemux2Stream, gst_adaptive_demux2_stream, @@ -49,6 +61,9 @@ gst_adaptive_demux2_stream_class_init (GstAdaptiveDemux2StreamClass * klass) GObjectClass *gobject_class = (GObjectClass *) klass; gobject_class->finalize = gst_adaptive_demux2_stream_finalize; + + klass->data_received = gst_adaptive_demux2_stream_data_received_default; + klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default; } static GType tsdemux_type = 0; @@ -255,10 +270,10 @@ schedule_another_chunk (GstAdaptiveDemux2Stream * stream) } static void -drain_inactive_tracks (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +drain_inactive_tracks (GstAdaptiveDemux2Stream * stream) { GList *iter; + GstAdaptiveDemux *demux = stream->demux; TRACKS_LOCK (demux); for (iter = stream->tracks; iter; iter = iter->next) { @@ -278,8 +293,8 @@ static void gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * stream, GstFlowReturn ret, GError * err) { - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux); - GstAdaptiveDemux *demux = stream->demux; + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); GST_DEBUG_OBJECT (stream, "%s download finish: %d %s - err: %p", uritype (stream), ret, @@ -313,12 +328,12 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) { /* We lost sync, seek back to live and return */ GST_WARNING_OBJECT (stream, "Lost sync when downloading"); - gst_adaptive_demux_handle_lost_sync (demux); + gst_adaptive_demux_handle_lost_sync (stream->demux); return; } else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) { /* The sub-class wants to stop the fragment immediately */ stream->fragment.finished = TRUE; - ret = klass->finish_fragment (demux, stream); + ret = klass->finish_fragment (stream); GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret, gst_flow_get_name (ret)); @@ -331,7 +346,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * || !klass->need_another_chunk (stream) || stream->fragment.chunk_size == 0) { stream->fragment.finished = TRUE; - ret = klass->finish_fragment (stream->demux, stream); + ret = klass->finish_fragment (stream); GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret, gst_flow_get_name (ret)); @@ -343,7 +358,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * /* For HLS, we might be enqueueing data into tracks that aren't * selected. Drain those ones out */ - drain_inactive_tracks (stream->demux, stream); + drain_inactive_tracks (stream); /* Now that we've called finish_fragment we can clear these flags the * sub-class might have checked */ @@ -387,7 +402,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * GST_LOG_OBJECT (stream, "Scheduling next_download() call"); stream->pending_cb_id = - gst_adaptive_demux_loop_call (demux->priv->scheduler_task, + gst_adaptive_demux_loop_call (stream->demux->priv->scheduler_task, (GSourceFunc) gst_adaptive_demux2_stream_next_download, gst_object_ref (stream), (GDestroyNotify) gst_object_unref); } @@ -414,13 +429,16 @@ gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream * stream, } static void -gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, gboolean first_and_live) +gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux2Stream * stream, + gboolean first_and_live) { + GstAdaptiveDemux *demux = stream->demux; GstClockTime period_start = gst_adaptive_demux_get_period_start_time (demux); GstClockTime offset = - gst_adaptive_demux2_stream_get_presentation_offset (demux, stream); + gst_adaptive_demux2_stream_get_presentation_offset (stream); + /* FIXME: Add a helper function to retrieve the demuxer segment + * using the SEGMENT_LOCK */ stream->parse_segment = demux->segment; /* The demuxer segment is just built from seek events, but for each stream @@ -478,8 +496,9 @@ gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux, * the segment time and base as calculated by the second case would be * equivalent. */ - GST_DEBUG_OBJECT (demux, "Using demux segment %" GST_SEGMENT_FORMAT, - &demux->segment); + GST_DEBUG_OBJECT (stream, "Using demux segment %" GST_SEGMENT_FORMAT, + &stream->parse_segment); + GST_DEBUG_OBJECT (demux, "period_start: %" GST_TIME_FORMAT " offset: %" GST_TIME_FORMAT, GST_TIME_ARGS (period_start), GST_TIME_ARGS (offset)); @@ -542,7 +561,7 @@ update_buffer_pts_and_demux_position_locked (GstAdaptiveDemux * demux, if (GST_CLOCK_STIME_IS_VALID (pos)) { GstClockTime offset = - gst_adaptive_demux2_stream_get_presentation_offset (demux, stream); + gst_adaptive_demux2_stream_get_presentation_offset (stream); pos += offset; @@ -574,8 +593,7 @@ gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream, GList *pending_events = NULL; if (stream->compute_segment) { - gst_adaptive_demux2_stream_prepare_segment (demux, stream, - stream->first_and_live); + gst_adaptive_demux2_stream_prepare_segment (stream, stream->first_and_live); stream->compute_segment = FALSE; stream->first_and_live = FALSE; } @@ -711,7 +729,8 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream, GstBuffer * buffer) { GstAdaptiveDemux *demux = stream->demux; - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); GstFlowReturn ret = GST_FLOW_OK; /* do not make any changes if the stream is cancelled */ @@ -728,7 +747,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream, * including the *actual* fragment ! */ if (stream->starting_fragment) { stream->starting_fragment = FALSE; - if (klass->start_fragment != NULL && !klass->start_fragment (demux, stream)) + if (klass->start_fragment != NULL && !klass->start_fragment (stream)) return GST_FLOW_ERROR; } @@ -738,7 +757,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream, "Received %s buffer of size %" G_GSIZE_FORMAT, uritype (stream), gst_buffer_get_size (buffer)); - ret = klass->data_received (demux, stream, buffer); + ret = klass->data_received (stream, buffer); if (ret != GST_FLOW_OK) { GST_DEBUG_OBJECT (stream, "data_received returned %s", @@ -759,7 +778,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream, GST_DEBUG_OBJECT (stream, "Pushing EOS to parser"); /* TODO push this on all pads */ - gst_event_set_seqnum (eos, stream->demux->priv->segment_seqnum); + gst_event_set_seqnum (eos, demux->priv->segment_seqnum); gst_pad_send_event (stream->parsebin_sink, eos); ret = GST_FLOW_ERROR; @@ -1177,7 +1196,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state, || last_status_code / 100 == 5)) { /* 4xx/5xx */ /* if current position is before available start, switch to next */ - if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) + if (!gst_adaptive_demux2_stream_has_next_fragment (stream)) goto flushing; if (live) { @@ -1195,7 +1214,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state, GST_DEBUG_OBJECT (demux, "Calling update_fragment_info"); - ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream); + ret = gst_adaptive_demux2_stream_update_fragment_info (stream); GST_DEBUG_OBJECT (stream, "update_fragment_info ret: %s", gst_flow_get_name (ret)); @@ -1205,8 +1224,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state, } else if (demux->segment.position > range_stop) { /* wait a bit to be in range, we don't have any locks at that point */ GstClockTime wait_time = - gst_adaptive_demux2_stream_get_fragment_waiting_time (demux, - stream); + gst_adaptive_demux2_stream_get_fragment_waiting_time (stream); if (wait_time > 0) { GST_DEBUG_OBJECT (stream, "Download waiting for %" GST_TIME_FORMAT, @@ -1230,7 +1248,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state, gst_adaptive_demux2_stream_handle_playlist_eos (stream); return; } - } else if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) { + } else if (!gst_adaptive_demux2_stream_has_next_fragment (stream)) { /* If this is the last fragment, consider failures EOS and not actual * errors. Due to rounding errors in the durations, the last fragment * might not actually exist */ @@ -1421,7 +1439,8 @@ static GstFlowReturn gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream) { GstAdaptiveDemux *demux = stream->demux; - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); gchar *url = NULL; /* FIXME : */ @@ -1760,7 +1779,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream) case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE: /* Get information about the fragment to download */ GST_DEBUG_OBJECT (demux, "Calling update_fragment_info"); - ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream); + ret = gst_adaptive_demux2_stream_update_fragment_info (stream); GST_DEBUG_OBJECT (stream, "Fragment info update result: %d %s", ret, gst_flow_get_name (ret)); @@ -1795,7 +1814,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream) /* wait for live fragments to be available */ if (live) { GstClockTime wait_time = - gst_adaptive_demux2_stream_get_fragment_waiting_time (demux, stream); + gst_adaptive_demux2_stream_get_fragment_waiting_time (stream); if (wait_time > 0) { GST_DEBUG_OBJECT (stream, "Download waiting for %" GST_TIME_FORMAT, @@ -1967,12 +1986,12 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream) static gboolean gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream) { - GstAdaptiveDemux *demux = stream->demux; - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); - if (!klass->stream_can_start) + if (!klass->can_start) return TRUE; - return klass->stream_can_start (demux, stream); + return klass->can_start (stream); } /** @@ -2096,3 +2115,492 @@ gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream * stream) return ret; } + +/* Called from the scheduler task */ +GstClockTime +gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream * + stream) +{ + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + + if (klass->get_presentation_offset == NULL) + return 0; + + return klass->get_presentation_offset (stream); +} + +GstFlowReturn +gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream * + stream) +{ + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + GstFlowReturn ret; + + g_return_val_if_fail (klass->update_fragment_info != NULL, GST_FLOW_ERROR); + + /* Make sure the sub-class will update bitrate, or else + * we will later */ + stream->fragment.finished = FALSE; + + GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT, + GST_TIME_ARGS (stream->current_position)); + + ret = klass->update_fragment_info (stream); + + GST_LOG_OBJECT (stream, "ret:%s uri:%s", + gst_flow_get_name (ret), stream->fragment.uri); + if (ret == GST_FLOW_OK) { + GST_LOG_OBJECT (stream, + "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, + GST_STIME_ARGS (stream->fragment.stream_time), + GST_TIME_ARGS (stream->fragment.duration)); + GST_LOG_OBJECT (stream, + "range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT, + stream->fragment.range_start, stream->fragment.range_end); + } + + return ret; +} + +static GstFlowReturn +gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream * + stream, GstBuffer * buffer) +{ + return gst_adaptive_demux2_stream_push_buffer (stream, buffer); +} + +static GstFlowReturn +gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream * + stream) +{ + /* No need to advance, this isn't a real fragment */ + if (G_UNLIKELY (stream->downloading_header || stream->downloading_index)) + return GST_FLOW_OK; + + return gst_adaptive_demux2_stream_advance_fragment (stream, + stream->fragment.duration); +} + +/* must be called from the scheduler */ +gboolean +gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream) +{ + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + gboolean ret = TRUE; + + if (klass->has_next_fragment) + ret = klass->has_next_fragment (stream); + + return ret; +} + +static gboolean +gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux * + demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate) +{ + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + + if (klass->select_bitrate) + return klass->select_bitrate (stream, bitrate); + return FALSE; +} + +GstClockTime +gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * + stream) +{ + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + + if (klass->get_fragment_waiting_time) + return klass->get_fragment_waiting_time (stream); + return 0; +} + +/* must be called from the scheduler */ +/* Called from: the ::finish_fragment() handlers when an *actual* fragment is + * done + * + * @duration: Is the duration of the advancement starting from + * stream->current_position which might not be the fragment duration after a + * seek. + */ +GstFlowReturn +gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stream * stream, + GstClockTime duration) +{ + if (stream->last_ret != GST_FLOW_OK) + return stream->last_ret; + + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + GstAdaptiveDemux *demux = stream->demux; + GstFlowReturn ret = GST_FLOW_OK; + + g_assert (klass->advance_fragment != NULL); + + GST_LOG_OBJECT (stream, + "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, + GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration)); + + stream->download_error_count = 0; + g_clear_error (&stream->last_error); + +#if 0 + /* FIXME - url has no indication of byte ranges for subsegments */ + /* FIXME: Reenable statistics sending? */ + gst_element_post_message (GST_ELEMENT_CAST (demux), + gst_message_new_element (GST_OBJECT_CAST (demux), + gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME, + "manifest-uri", G_TYPE_STRING, + demux->manifest_uri, "uri", G_TYPE_STRING, + stream->fragment.uri, "fragment-start-time", + GST_TYPE_CLOCK_TIME, stream->download_start_time, + "fragment-stop-time", GST_TYPE_CLOCK_TIME, + gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64, + stream->download_total_bytes, "fragment-download-time", + GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL))); +#endif + + /* Don't update to the end of the segment if in reverse playback */ + GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux); + if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) { + stream->parse_segment.position += duration; + stream->current_position += duration; + + GST_DEBUG_OBJECT (stream, + "stream position now %" GST_TIME_FORMAT, + GST_TIME_ARGS (stream->current_position)); + } + GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux); + + /* When advancing with a non 1.0 rate on live streams, we need to check + * the live seeking range again to make sure we can still advance to + * that position */ + if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) { + if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream)) + ret = GST_FLOW_EOS; + else + ret = klass->advance_fragment (stream); + } else if (gst_adaptive_demux_is_live (demux) + || gst_adaptive_demux2_stream_has_next_fragment (stream)) { + ret = klass->advance_fragment (stream); + } else { + ret = GST_FLOW_EOS; + } + + stream->download_start_time = + GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux)); + + /* Always check if we need to switch bitrate on OK, or when live + * (it's normal to have EOS on advancing in live when we hit the + * end of the manifest) */ + if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) { + GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change"); + if (gst_adaptive_demux2_stream_select_bitrate (demux, stream, + gst_adaptive_demux2_stream_update_current_bitrate (stream))) { + GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH"); + stream->need_header = TRUE; + ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH; + } + } + + stream->last_ret = ret; + return stream->last_ret; +} + +/* TRACKS_LOCK held */ +static GstAdaptiveDemuxTrack * +gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream, + GstStreamType stream_type) +{ + GList *iter; + + for (iter = stream->tracks; iter; iter = iter->next) { + GstAdaptiveDemuxTrack *track = iter->data; + + if (track->type == stream_type) + return track; + } + + return NULL; +} + +/* TRACKS lock held */ +static void +gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream * stream) +{ + guint i; + + GST_DEBUG_OBJECT (stream, "Updating track information from collection"); + + for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection); + i++) { + GstStream *gst_stream = + gst_stream_collection_get_stream (stream->stream_collection, i); + GstStreamType stream_type = gst_stream_get_stream_type (gst_stream); + GstAdaptiveDemuxTrack *track; + + if (stream_type == GST_STREAM_TYPE_UNKNOWN) + continue; + track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type); + if (!track) { + GST_DEBUG_OBJECT (stream, + "We don't have an existing track to handle stream %" GST_PTR_FORMAT, + gst_stream); + continue; + } + + if (track->upstream_stream_id) + g_free (track->upstream_stream_id); + track->upstream_stream_id = + g_strdup (gst_stream_get_stream_id (gst_stream)); + } + +} + +static gboolean +tags_have_language_info (GstTagList * tags) +{ + const gchar *language = NULL; + + if (tags == NULL) + return FALSE; + + if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0, + &language)) + return TRUE; + if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0, + &language)) + return TRUE; + + return FALSE; +} + +static gboolean +can_handle_collection (GstAdaptiveDemux2Stream * stream, + GstStreamCollection * collection) +{ + guint i; + guint nb_audio, nb_video, nb_text; + gboolean have_audio_languages = TRUE; + gboolean have_text_languages = TRUE; + + nb_audio = nb_video = nb_text = 0; + + for (i = 0; i < gst_stream_collection_get_size (collection); i++) { + GstStream *gst_stream = gst_stream_collection_get_stream (collection, i); + GstTagList *tags = gst_stream_get_tags (gst_stream); + + GST_DEBUG_OBJECT (stream, + "Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream); + switch (gst_stream_get_stream_type (gst_stream)) { + case GST_STREAM_TYPE_AUDIO: + have_audio_languages &= tags_have_language_info (tags); + nb_audio++; + break; + case GST_STREAM_TYPE_VIDEO: + nb_video++; + break; + case GST_STREAM_TYPE_TEXT: + have_text_languages &= tags_have_language_info (tags); + nb_text++; + break; + default: + break; + } + } + + /* Check that we either have at most 1 of each track type, or that + * we have language tags for each to tell which is which */ + if (nb_video > 1 || + (nb_audio > 1 && !have_audio_languages) || + (nb_text > 1 && !have_text_languages)) { + GST_WARNING + ("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)", + nb_audio, nb_video, nb_text); + return FALSE; + } + + return TRUE; +} + +/* Called from the demuxer when it receives a GstStreamCollection on the bus + * for this stream. */ +/* TRACKS lock held */ +gboolean +gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream * stream, + GstStreamCollection * collection, gboolean * had_pending_tracks) +{ + g_assert (had_pending_tracks != NULL); + + /* Check whether the collection is "sane" or not. + * + * In the context of adaptive streaming, we can only handle multiplexed + * content where the output sub-streams can be matched reliably to the various + * tracks. That is, only a single stream of each type, or if there are + * multiple audio/subtitle tracks, they can be differentiated by language + * (and possibly in the future by codec). + */ + if (!can_handle_collection (stream, collection)) { + return FALSE; + } + + /* Store the collection on the stream */ + gst_object_replace ((GstObject **) & stream->stream_collection, + (GstObject *) collection); + + /* If stream is marked as having pending_tracks, ask the subclass to + * handle that and create the tracks now */ + if (stream->pending_tracks) { + GstAdaptiveDemux2StreamClass *klass = + GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream); + g_assert (klass->create_tracks); + klass->create_tracks (stream); + stream->pending_tracks = FALSE; + *had_pending_tracks = TRUE; + } else { + g_assert (stream->tracks); + + /* Now we should have assigned tracks, match them to the + * collection and update the pending upstream stream_id + * for each of them based on the collection information. */ + gst_adaptive_demux2_stream_update_track_ids (stream); + } + + return TRUE; +} + +static guint64 +_update_average_bitrate (GstAdaptiveDemux2Stream * stream, guint64 new_bitrate) +{ + gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS; + + stream->moving_bitrate -= stream->fragment_bitrates[index]; + stream->fragment_bitrates[index] = new_bitrate; + stream->moving_bitrate += new_bitrate; + + stream->moving_index += 1; + + if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS) + return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS; + return stream->moving_bitrate / stream->moving_index; +} + +guint64 +gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream * + stream) +{ + guint64 average_bitrate; + guint64 fragment_bitrate; + guint connection_speed, min_bitrate, max_bitrate, target_download_rate; + + fragment_bitrate = stream->last_bitrate; + GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps", + fragment_bitrate); + + average_bitrate = _update_average_bitrate (stream, fragment_bitrate); + + GST_INFO_OBJECT (stream, + "last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate); + GST_INFO_OBJECT (stream, + "Last %u fragments average bitrate is %" G_GUINT64_FORMAT, + NUM_LOOKBACK_FRAGMENTS, average_bitrate); + + /* Conservative approach, make sure we don't upgrade too fast */ + stream->current_download_rate = MIN (average_bitrate, fragment_bitrate); + + /* For the video stream, update the demuxer reported download + * rate. FIXME: Move all bandwidth estimation to the + * download helper and make it the demuxer's responsibility + * to select the right set of things to download within + * that bandwidth */ + GstAdaptiveDemux *demux = stream->demux; + GST_OBJECT_LOCK (demux); + + /* If this is stream containing our video, update the overall demuxer + * reported bitrate and notify, to give the application a + * chance to choose a new connection-bitrate */ + if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) { + demux->current_download_rate = stream->current_download_rate; + GST_OBJECT_UNLOCK (demux); + g_object_notify (G_OBJECT (demux), "current-bandwidth"); + GST_OBJECT_LOCK (demux); + } + + connection_speed = demux->connection_speed; + min_bitrate = demux->min_bitrate; + max_bitrate = demux->max_bitrate; + GST_OBJECT_UNLOCK (demux); + + if (connection_speed) { + GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it", + connection_speed / 1000); + return connection_speed; + } + + /* No explicit connection_speed, so choose the new variant to use as a + * fraction of the measured download rate */ + target_download_rate = + CLAMP (stream->current_download_rate, 0, + G_MAXUINT) * demux->bandwidth_target_ratio; + + GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u", + demux->bandwidth_target_ratio, target_download_rate); + +#if 0 + /* Debugging code, modulate the bitrate every few fragments */ + { + static guint ctr = 0; + if (ctr % 3 == 0) { + GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging"); + target_download_rate /= 2; + } + ctr++; + } +#endif + + if (min_bitrate > 0 && target_download_rate < min_bitrate) { + target_download_rate = min_bitrate; + GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s", + min_bitrate); + } + + if (max_bitrate > 0 && target_download_rate > max_bitrate) { + target_download_rate = max_bitrate; + GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s", + max_bitrate); + } + + GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps", + target_download_rate); + + return target_download_rate; +} + +void +gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f) +{ + g_free (f->uri); + f->uri = NULL; + f->range_start = 0; + f->range_end = -1; + + g_free (f->header_uri); + f->header_uri = NULL; + f->header_range_start = 0; + f->header_range_end = -1; + + g_free (f->index_uri); + f->index_uri = NULL; + f->index_range_start = 0; + f->index_range_end = -1; + + f->stream_time = GST_CLOCK_STIME_NONE; + f->duration = GST_CLOCK_TIME_NONE; + f->finished = FALSE; +} diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.h new file mode 100644 index 0000000000..7049b10796 --- /dev/null +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.h @@ -0,0 +1,348 @@ +/* GStreamer + * + * Copyright (C) 2014 Samsung Electronics. All rights reserved. + * Author: Thiago Santos + * + * Copyright (C) 2021-2022 Centricular Ltd + * Author: Edward Hervey + * Author: Jan Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _GST_ADAPTIVE_DEMUX_STREAM_H_ +#define _GST_ADAPTIVE_DEMUX_STREAM_H_ + +#include +#include "gstadaptivedemux-types.h" +#include "downloadrequest.h" + +G_BEGIN_DECLS + +#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \ + (gst_adaptive_demux2_stream_get_type()) +#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream)) +#define GST_ADAPTIVE_DEMUX2_STREAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2StreamClass)) +#define GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2StreamClass)) +#define GST_IS_ADAPTIVE_DEMUX2_STREAM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM)) +#define GST_IS_ADAPTIVE_DEMUX2_STREAM_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX2_STREAM)) +#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj) + + +#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header) + +typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState; + +typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment; + +struct _GstAdaptiveDemux2StreamFragment +{ + /* The period-local stream time for the given fragment. */ + GstClockTimeDiff stream_time; + GstClockTime duration; + + gchar *uri; + gint64 range_start; + gint64 range_end; + + /* when chunked downloading is used, may be be updated need_another_chunk() */ + gint chunk_size; + + /* when headers are needed */ + gchar *header_uri; + gint64 header_range_start; + gint64 header_range_end; + + /* when index is needed */ + gchar *index_uri; + gint64 index_range_start; + gint64 index_range_end; + + gboolean finished; +}; + +enum _GstAdaptiveDemux2StreamState { + GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */ + GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */ + GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS, + GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED +}; + +struct _GstAdaptiveDemux2StreamClass +{ + GstObjectClass parent_class; + + /** + * update_fragment_info: + * @stream: #GstAdaptiveDemux2Stream + * + * Requests the stream to set the information about the current fragment to its + * current fragment struct + * + * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS + * if there is no fragment. + */ + GstFlowReturn (*update_fragment_info) (GstAdaptiveDemux2Stream * stream); + + /** + * finish_fragment: + * @stream: #GstAdaptiveDemux2Stream + * + * Notifies the subclass that a fragment download was finished. + * It can be used to cleanup internal state after a fragment and + * also push any pending data before moving to the next fragment. + */ + GstFlowReturn (*finish_fragment) (GstAdaptiveDemux2Stream * stream); + + /** + * data_received: + * @stream: #GstAdaptiveDemux2Stream + * @buffer: #GstBuffer + * + * Notifies the subclass that a fragment chunk was downloaded. The subclass + * can look at the data and modify/push data as desired. + * + * Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error. + */ + GstFlowReturn (*data_received) (GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); + + gboolean (*has_next_fragment) (GstAdaptiveDemux2Stream * stream); + GstFlowReturn (*advance_fragment) (GstAdaptiveDemux2Stream * stream); + + /** + * can_start: + * @stream: a #GstAdaptiveDemux2Stream + * + * Called before starting a @stream. sub-classes can return %FALSE if more + * information is required before it can be started. Sub-classes will have to + * call gst_adaptive_demux2_stream_start() when the stream should be started. + */ + gboolean (*can_start) (GstAdaptiveDemux2Stream *stream); + + /** + * create_tracks: + * @stream: A #GstAdaptiveDemux2Stream + * + * Called whenever the base class collected a @collection on a @stream which has + * pending tracks to be created. Subclasses should override this if they + * create streams without tracks. + * + * * create the various tracks by analyzing the @stream stream_collection + * * Set the track upstream_stream_id to the corresponding stream_id from the collection + */ + void (*create_tracks) (GstAdaptiveDemux2Stream *stream); + + /** + * need_another_chunk: + * @stream: #GstAdaptiveDemux2Stream + * + * If chunked downloading is used (chunk_size != 0) this is called once a + * chunk is finished to decide whether more has to be downloaded or not. + * May update chunk_size to a different value + */ + gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream); + + /** + * select_bitrate: + * @stream: #GstAdaptiveDemux2Stream + * @bitrate: the bitrate to select (in bytes per second) + * + * The stream should try to select the bitrate that is the greater, but not + * greater than the requested bitrate. If it needs a codec change it should + * create the new stream using gst_adaptive_demux2_stream_new(). If it only + * needs a caps change it should set the new caps using + * gst_adaptive_demux2_stream_set_caps(). + * + * Returns: %TRUE if the stream changed bitrate, %FALSE otherwise + */ + gboolean (*select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate); + + /** + * get_fragment_waiting_time: + * @stream: #GstAdaptiveDemux2Stream + * + * For live streams, requests how much time should be waited before starting + * to download the fragment. This is useful to avoid downloading a fragment that + * isn't available yet. + * + * Returns: The waiting time in as a #GstClockTime + */ + GstClockTime (*get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream); + + /** + * start_fragment: + * @stream: #GstAdaptiveDemux2Stream + * + * Notifies the subclass that the given stream is starting the download + * of a new fragment. Can be used to reset/init internal state that is + * needed before each fragment, like decryption engines. + * + * Returns: %TRUE if successful. + */ + gboolean (*start_fragment) (GstAdaptiveDemux2Stream * stream); + + /** + * get_presentation_offset: + * @stream: #GstAdaptiveDemux2Stream + * + * Gets the delay to apply to @stream. + * + * Return: a #GstClockTime representing the (positive) time offset to apply to + * @stream. + */ + GstClockTime (*get_presentation_offset) (GstAdaptiveDemux2Stream *stream); +}; + +struct _GstAdaptiveDemux2Stream +{ + GstObject object; + + /* FIXME : transition to gstobject->parent */ + GstAdaptiveDemux *demux; + + /* The period to which the stream belongs, set when adding the stream to the + * demuxer */ + GstAdaptiveDemuxPeriod *period; + + /* The tracks this stream targets */ + GList *tracks; + + /* The internal parsebin, forward data to track */ + GstElement *parsebin; + GstPad *parsebin_sink; + + gulong pad_added_id, pad_removed_id; + + GstSegment parse_segment; + + /* TRUE if the current stream GstSegment should be sent downstream */ + gboolean send_segment; + /* TRUE if the stream GstSegment requires recalculation (from demuxer + segment) */ + gboolean compute_segment; + /* first_and_live applies to compute_segment */ + gboolean first_and_live; + + /* When restarting, what is the target position (in demux segment) to + * begin at */ + GstClockTime start_position; + + /* Track the current position (in demux segment) of the current fragment */ + GstClockTime current_position; + + GstCaps *pending_caps; + GstTagList *pending_tags; + + GList *pending_events; + + GstFlowReturn last_ret; + GError *last_error; + + gboolean discont; + + /* download tooling */ + gboolean need_header; + gboolean need_index; + + gboolean downloading_header; + gboolean downloading_index; + + /* persistent, reused download request for fragment data */ + DownloadRequest *download_request; + + GstAdaptiveDemux2StreamState state; + guint pending_cb_id; + gboolean download_active; + /* The (global output) time at which this stream should be woken + * to download more input */ + GstClockTimeDiff next_input_wakeup_time; + + guint last_status_code; + + gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */ + gboolean download_finished; + + gboolean starting_fragment; + gboolean first_fragment_buffer; + gint64 download_start_time; + gint64 download_total_bytes; + gint64 download_end_offset; + guint64 current_download_rate; + + /* bitrate of the previous fragment (pre-queue2) */ + guint64 last_bitrate; + + /* Total last download time, from request to completion */ + GstClockTime last_download_time; + + /* Average for the last fragments */ + guint64 moving_bitrate; + guint moving_index; + guint64 *fragment_bitrates; + + GstAdaptiveDemux2StreamFragment fragment; + + guint download_error_count; + + /* Last collection provided by parsebin */ + GstStreamCollection *stream_collection; + + /* OR'd set of stream types in this stream */ + GstStreamType stream_type; + + /* The buffering threshold recommended by the subclass */ + GstClockTime recommended_buffering_threshold; +}; + +GType gst_adaptive_demux2_stream_get_type (void); + +void gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream); + +void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream, + GstEvent * event); + +gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream); +gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream); + +void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream, + GstCaps * caps); + +void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream, + GstTagList * tags); + +GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream, + GstBuffer * buffer); + +GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stream * stream, + GstClockTime duration); + +gboolean gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream *stream, + GstStreamCollection *collection, gboolean *had_pending_tracks); + +void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f); + +G_END_DECLS + +#endif diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-types.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-types.h new file mode 100644 index 0000000000..8c0fda361a --- /dev/null +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-types.h @@ -0,0 +1,55 @@ +/* GStreamer + * + * Copyright (C) 2014 Samsung Electronics. All rights reserved. + * Author: Thiago Santos + * + * Copyright (C) 2021-2022 Centricular Ltd + * Author: Edward Hervey + * Author: Jan Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_ADAPTIVE_DEMUX_TYPES_H_ +#define _GST_ADAPTIVE_DEMUX_TYPES_H_ + +G_BEGIN_DECLS + +typedef struct _GstAdaptiveDemux2StreamClass GstAdaptiveDemux2StreamClass; +typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream; +typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack; +typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod; +typedef struct _GstAdaptiveDemux GstAdaptiveDemux; +typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass; + +#define GST_TYPE_ADAPTIVE_DEMUX \ + (gst_adaptive_demux_ng_get_type()) +#define GST_ADAPTIVE_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux)) +#define GST_ADAPTIVE_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) +#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) +#define GST_IS_ADAPTIVE_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX)) +#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX)) +#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj) + +G_END_DECLS + +#endif + diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c index 261bf06f68..07b8ffd0f5 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c @@ -229,8 +229,6 @@ static void gst_adaptive_demux_reset (GstAdaptiveDemux * demux); static gboolean gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux, gboolean first_and_live); -static gboolean gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux * - demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate); static GstFlowReturn gst_adaptive_demux_update_manifest_default (GstAdaptiveDemux * demux); @@ -242,19 +240,6 @@ static void gst_adaptive_demux_start_manifest_update_task (GstAdaptiveDemux * static void gst_adaptive_demux_start_tasks (GstAdaptiveDemux * demux); static void gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux, gboolean stop_updates); -static GstFlowReturn -gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); -static GstFlowReturn -gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static GstFlowReturn -gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstClockTime duration); - -static void -gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); static gboolean gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux @@ -532,19 +517,15 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass) gst_element_class_add_static_pad_template (gstelement_class, &gst_adaptive_demux_subtitlesrc_template); - gstelement_class->change_state = gst_adaptive_demux_change_state; gstelement_class->query = gst_adaptive_demux_query; gstelement_class->send_event = gst_adaptive_demux_send_event; gstbin_class->handle_message = gst_adaptive_demux_handle_message; - klass->data_received = gst_adaptive_demux2_stream_data_received_default; - klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default; klass->update_manifest = gst_adaptive_demux_update_manifest_default; klass->requires_periodical_playlist_update = gst_adaptive_demux_requires_periodical_playlist_update_default; - klass->stream_update_tracks = gst_adaptive_demux2_stream_update_tracks; gst_type_mark_as_plugin_api (GST_TYPE_ADAPTIVE_DEMUX, 0); } @@ -1468,123 +1449,6 @@ find_stream_for_element_locked (GstAdaptiveDemux * demux, GstObject * o) return NULL; } -/* TRACKS_LOCK held */ -static GstAdaptiveDemuxTrack * -gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream, - GstStreamType stream_type) -{ - GList *iter; - - for (iter = stream->tracks; iter; iter = iter->next) { - GstAdaptiveDemuxTrack *track = iter->data; - - if (track->type == stream_type) - return track; - } - - return NULL; -} - -/* MANIFEST and TRACKS lock held */ -static void -gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - guint i; - - GST_DEBUG_OBJECT (stream, "Updating track information from collection"); - - for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection); - i++) { - GstStream *gst_stream = - gst_stream_collection_get_stream (stream->stream_collection, i); - GstStreamType stream_type = gst_stream_get_stream_type (gst_stream); - GstAdaptiveDemuxTrack *track; - - if (stream_type == GST_STREAM_TYPE_UNKNOWN) - continue; - track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type); - if (!track) { - GST_DEBUG_OBJECT (stream, - "We don't have an existing track to handle stream %" GST_PTR_FORMAT, - gst_stream); - continue; - } - - if (track->upstream_stream_id) - g_free (track->upstream_stream_id); - track->upstream_stream_id = - g_strdup (gst_stream_get_stream_id (gst_stream)); - } - -} - -static gboolean -tags_have_language_info (GstTagList * tags) -{ - const gchar *language = NULL; - - if (tags == NULL) - return FALSE; - - if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0, - &language)) - return TRUE; - if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0, - &language)) - return TRUE; - - return FALSE; -} - -static gboolean -can_handle_collection (GstAdaptiveDemux2Stream * stream, - GstStreamCollection * collection) -{ - guint i; - guint nb_audio, nb_video, nb_text; - gboolean have_audio_languages = TRUE; - gboolean have_text_languages = TRUE; - - nb_audio = nb_video = nb_text = 0; - - for (i = 0; i < gst_stream_collection_get_size (collection); i++) { - GstStream *gst_stream = gst_stream_collection_get_stream (collection, i); - GstTagList *tags = gst_stream_get_tags (gst_stream); - - GST_DEBUG_OBJECT (stream, - "Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream); - switch (gst_stream_get_stream_type (gst_stream)) { - case GST_STREAM_TYPE_AUDIO: - have_audio_languages &= tags_have_language_info (tags); - nb_audio++; - break; - case GST_STREAM_TYPE_VIDEO: - nb_video++; - break; - case GST_STREAM_TYPE_TEXT: - have_text_languages &= tags_have_language_info (tags); - nb_text++; - break; - default: - break; - } - } - - /* Check that we either have at most 1 of each track type, or that - * we have language tags for each to tell which is which */ - if (nb_video > 1 || - (nb_audio > 1 && !have_audio_languages) || - (nb_text > 1 && !have_text_languages)) { - GST_WARNING - ("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)", - nb_audio, nb_video, nb_text); - return FALSE; - } - - return TRUE; -} - static void gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux, GstMessage * msg) @@ -1606,60 +1470,38 @@ gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux, if (!collection) goto beach; - /* Check whether the collection is "sane" or not. - * - * In the context of adaptive streaming, we can only handle multiplexed - * content that provides at most one stream of valid types (audio, video, - * text). Without this we cannot reliably match the output of this multiplex - * to the various tracks. - * - * FIXME : In the future and *IF* we encounter such streams, we could envision - * supporting multiple streams of the same type if, and only if, they have - * tags that allow differentiating them (ex: languages). - */ - if (!can_handle_collection (stream, collection)) { + TRACKS_LOCK (demux); + + if (!gst_adaptive_demux2_stream_handle_collection (stream, collection, + &pending_tracks_activated)) { + TRACKS_UNLOCK (demux); + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (_("Stream format can't be handled")), ("The streams provided by the multiplex are ambiguous")); goto beach; } - /* Store the collection on the stream */ - gst_object_replace ((GstObject **) & stream->stream_collection, - (GstObject *) collection); - - /* IF there are pending tracks, ask the subclass to handle that */ - if (stream->pending_tracks) { - GstAdaptiveDemuxClass *demux_class = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - g_assert (demux_class->stream_update_tracks); - demux_class->stream_update_tracks (demux, stream); - TRACKS_LOCK (demux); - stream->pending_tracks = FALSE; - pending_tracks_activated = TRUE; + if (pending_tracks_activated) { + /* If pending tracks were handled, then update the demuxer collection */ if (gst_adaptive_demux_update_collection (demux, demux->input_period) && - demux->input_period == demux->output_period) + demux->input_period == demux->output_period) { gst_adaptive_demux_post_collection (demux); - } else { - g_assert (stream->tracks); - TRACKS_LOCK (demux); - /* If we already have assigned tracks, update the pending upstream stream_id - * for each of them based on the collection information. */ - gst_adaptive_demux2_stream_update_tracks (demux, stream); - } + } - /* If we discovered pending tracks and we no longer have any, we can ensure - * selected tracks are started */ - if (pending_tracks_activated - && !gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) { - GList *iter = demux->input_period->streams; - for (; iter; iter = iter->next) { - GstAdaptiveDemux2Stream *new_stream = iter->data; + /* If we discovered pending tracks and we no longer have any, we can ensure + * selected tracks are started */ + if (!gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) { + GList *iter = demux->input_period->streams; + for (; iter; iter = iter->next) { + GstAdaptiveDemux2Stream *new_stream = iter->data; - /* The stream that posted this collection was already started. If a - * different stream is now selected, start it */ - if (stream != new_stream - && gst_adaptive_demux2_stream_is_selected_locked (new_stream)) - gst_adaptive_demux2_stream_start (new_stream); + /* The stream that posted this collection was already started. If a + * different stream is now selected, start it */ + if (stream != new_stream + && gst_adaptive_demux2_stream_is_selected_locked (new_stream)) + gst_adaptive_demux2_stream_start (new_stream); + } } } TRACKS_UNLOCK (demux); @@ -1741,21 +1583,6 @@ gst_adaptive_demux_handle_message (GstBin * bin, GstMessage * msg) GST_BIN_CLASS (parent_class)->handle_message (bin, msg); } -/* must be called with manifest_lock taken */ -GstClockTime -gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - GstAdaptiveDemuxClass *klass; - - klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - - if (klass->get_presentation_offset == NULL) - return 0; - - return klass->get_presentation_offset (demux, stream); -} - /* must be called with manifest_lock taken */ GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux) @@ -1813,7 +1640,7 @@ gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux, /* TODO we only need the first timestamp, maybe create a simple function to * get the current PTS of a fragment ? */ GST_DEBUG_OBJECT (stream, "Calling update_fragment_info"); - gst_adaptive_demux2_stream_update_fragment_info (demux, stream); + gst_adaptive_demux2_stream_update_fragment_info (stream); GST_DEBUG_OBJECT (stream, "Got stream time %" GST_STIME_FORMAT, @@ -3008,130 +2835,6 @@ gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream, stream->pending_events = g_list_append (stream->pending_events, event); } -static guint64 -_update_average_bitrate (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, guint64 new_bitrate) -{ - gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS; - - stream->moving_bitrate -= stream->fragment_bitrates[index]; - stream->fragment_bitrates[index] = new_bitrate; - stream->moving_bitrate += new_bitrate; - - stream->moving_index += 1; - - if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS) - return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS; - return stream->moving_bitrate / stream->moving_index; -} - -static guint64 -gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - guint64 average_bitrate; - guint64 fragment_bitrate; - guint connection_speed, min_bitrate, max_bitrate, target_download_rate; - - fragment_bitrate = stream->last_bitrate; - GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps", - fragment_bitrate); - - average_bitrate = _update_average_bitrate (demux, stream, fragment_bitrate); - - GST_INFO_OBJECT (stream, - "last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate); - GST_INFO_OBJECT (stream, - "Last %u fragments average bitrate is %" G_GUINT64_FORMAT, - NUM_LOOKBACK_FRAGMENTS, average_bitrate); - - /* Conservative approach, make sure we don't upgrade too fast */ - GST_OBJECT_LOCK (demux); - stream->current_download_rate = MIN (average_bitrate, fragment_bitrate); - - /* If this is the/a video stream update the overall demuxer - * reported bitrate and notify, to give the application a - * chance to choose a new connection-bitrate */ - if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) { - demux->current_download_rate = stream->current_download_rate; - GST_OBJECT_UNLOCK (demux); - g_object_notify (G_OBJECT (demux), "current-bandwidth"); - GST_OBJECT_LOCK (demux); - } - - connection_speed = demux->connection_speed; - min_bitrate = demux->min_bitrate; - max_bitrate = demux->max_bitrate; - GST_OBJECT_UNLOCK (demux); - - if (connection_speed) { - GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it", - connection_speed / 1000); - return connection_speed; - } - - /* No explicit connection_speed, so choose the new variant to use as a - * fraction of the measured download rate */ - target_download_rate = - CLAMP (stream->current_download_rate, 0, - G_MAXUINT) * demux->bandwidth_target_ratio; - - GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u", - demux->bandwidth_target_ratio, target_download_rate); - -#if 0 - /* Debugging code, modulate the bitrate every few fragments */ - { - static guint ctr = 0; - if (ctr % 3 == 0) { - GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging"); - target_download_rate /= 2; - } - ctr++; - } -#endif - - if (min_bitrate > 0 && target_download_rate < min_bitrate) { - target_download_rate = min_bitrate; - GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s", - min_bitrate); - } - - if (max_bitrate > 0 && target_download_rate > max_bitrate) { - target_download_rate = max_bitrate; - GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s", - max_bitrate); - } - - GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps", - target_download_rate); - - return target_download_rate; -} - -/* must be called with manifest_lock taken */ -static GstFlowReturn -gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - /* No need to advance, this isn't a real fragment */ - if (G_UNLIKELY (stream->downloading_header || stream->downloading_index)) - return GST_FLOW_OK; - - return gst_adaptive_demux2_stream_advance_fragment (demux, stream, - stream->fragment.duration); -} - -/* must be called with manifest_lock taken. - * Can temporarily release manifest_lock - */ -static GstFlowReturn -gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer) -{ - return gst_adaptive_demux2_stream_push_buffer (stream, buffer); -} - static gboolean gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux * demux) @@ -3877,181 +3580,6 @@ gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux, return GST_FLOW_ERROR; } -/* must be called from the scheduler */ -gboolean -gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - gboolean ret = TRUE; - - if (klass->stream_has_next_fragment) - ret = klass->stream_has_next_fragment (stream); - - return ret; -} - -/* must be called from the scheduler */ -/* Called from: the ::finish_fragment() handlers when an *actual* fragment is - * done - * - * @duration: Is the duration of the advancement starting from - * stream->current_position which might not be the fragment duration after a - * seek. - */ -GstFlowReturn -gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstClockTime duration) -{ - if (stream->last_ret != GST_FLOW_OK) - return stream->last_ret; - - stream->last_ret = - gst_adaptive_demux2_stream_advance_fragment_unlocked (demux, stream, - duration); - - return stream->last_ret; -} - -/* must be called with manifest_lock taken */ -static GstFlowReturn -gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstClockTime duration) -{ - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - GstFlowReturn ret; - - g_return_val_if_fail (klass->stream_advance_fragment != NULL, GST_FLOW_ERROR); - - GST_LOG_OBJECT (stream, - "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, - GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration)); - - stream->download_error_count = 0; - g_clear_error (&stream->last_error); - -#if 0 - /* FIXME - url has no indication of byte ranges for subsegments */ - /* FIXME: Reenable statistics sending? */ - gst_element_post_message (GST_ELEMENT_CAST (demux), - gst_message_new_element (GST_OBJECT_CAST (demux), - gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME, - "manifest-uri", G_TYPE_STRING, - demux->manifest_uri, "uri", G_TYPE_STRING, - stream->fragment.uri, "fragment-start-time", - GST_TYPE_CLOCK_TIME, stream->download_start_time, - "fragment-stop-time", GST_TYPE_CLOCK_TIME, - gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64, - stream->download_total_bytes, "fragment-download-time", - GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL))); -#endif - - /* Don't update to the end of the segment if in reverse playback */ - GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux); - if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) { - stream->parse_segment.position += duration; - stream->current_position += duration; - - GST_DEBUG_OBJECT (stream, - "stream position now %" GST_TIME_FORMAT, - GST_TIME_ARGS (stream->current_position)); - } - GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux); - - /* When advancing with a non 1.0 rate on live streams, we need to check - * the live seeking range again to make sure we can still advance to - * that position */ - if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) { - if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream)) - ret = GST_FLOW_EOS; - else - ret = klass->stream_advance_fragment (stream); - } else if (gst_adaptive_demux_is_live (demux) - || gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) { - ret = klass->stream_advance_fragment (stream); - } else { - ret = GST_FLOW_EOS; - } - - stream->download_start_time = - GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux)); - - /* Always check if we need to switch bitrate on OK, or when live - * (it's normal to have EOS on advancing in live when we hit the - * end of the manifest) */ - if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) { - GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change"); - if (gst_adaptive_demux2_stream_select_bitrate (demux, stream, - gst_adaptive_demux2_stream_update_current_bitrate (demux, - stream))) { - GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH"); - stream->need_header = TRUE; - ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH; - } - } - - return ret; -} - -/* must be called with manifest_lock taken */ -static gboolean -gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux * - demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate) -{ - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - - if (klass->stream_select_bitrate) - return klass->stream_select_bitrate (stream, bitrate); - return FALSE; -} - -/* must be called with manifest_lock taken */ -GstFlowReturn -gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) -{ - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - GstFlowReturn ret; - - g_return_val_if_fail (klass->stream_update_fragment_info != NULL, - GST_FLOW_ERROR); - - /* Make sure the sub-class will update bitrate, or else - * we will later */ - stream->fragment.finished = FALSE; - - GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT, - GST_TIME_ARGS (stream->current_position)); - - ret = klass->stream_update_fragment_info (stream); - - GST_LOG_OBJECT (stream, "ret:%s uri:%s", - gst_flow_get_name (ret), stream->fragment.uri); - if (ret == GST_FLOW_OK) { - GST_LOG_OBJECT (stream, - "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, - GST_STIME_ARGS (stream->fragment.stream_time), - GST_TIME_ARGS (stream->fragment.duration)); - GST_LOG_OBJECT (stream, - "range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT, - stream->fragment.range_start, stream->fragment.range_end); - } - - return ret; -} - -/* must be called with manifest_lock taken */ -GstClockTime -gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux * - demux, GstAdaptiveDemux2Stream * stream) -{ - GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux); - - if (klass->stream_get_fragment_waiting_time) - return klass->stream_get_fragment_waiting_time (stream); - return 0; -} - static void handle_manifest_download_complete (DownloadRequest * request, DownloadRequestState state, GstAdaptiveDemux * demux) @@ -4160,29 +3688,6 @@ gst_adaptive_demux_update_manifest (GstAdaptiveDemux * demux) return ret; } -void -gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f) -{ - g_free (f->uri); - f->uri = NULL; - f->range_start = 0; - f->range_end = -1; - - g_free (f->header_uri); - f->header_uri = NULL; - f->header_range_start = 0; - f->header_range_end = -1; - - g_free (f->index_uri); - f->index_uri = NULL; - f->index_range_start = 0; - f->index_range_end = -1; - - f->stream_time = GST_CLOCK_STIME_NONE; - f->duration = GST_CLOCK_TIME_NONE; - f->finished = FALSE; -} - /* must be called with manifest_lock taken */ gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux) diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h index 0c6470f8f1..883b562998 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h @@ -34,32 +34,11 @@ #include "gstadaptivedemuxutils.h" +#include "gstadaptivedemux-types.h" +#include "gstadaptivedemux-stream.h" + G_BEGIN_DECLS -#define GST_TYPE_ADAPTIVE_DEMUX \ - (gst_adaptive_demux_ng_get_type()) -#define GST_ADAPTIVE_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux)) -#define GST_ADAPTIVE_DEMUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) -#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) -#define GST_IS_ADAPTIVE_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX)) -#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX)) -#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj) - -#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \ - (gst_adaptive_demux2_stream_get_type()) -#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream)) -#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj) - -typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream; -typedef GstObjectClass GstAdaptiveDemux2StreamClass; - - /** * GST_ADAPTIVE_DEMUX_SINK_NAME: * @@ -77,8 +56,6 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass; #define GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS(obj) ((((GstAdaptiveDemux*)(obj))->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) == GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) -#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header) - /** * GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME: * @@ -101,48 +78,15 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass; #define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1 /* Current fragment download should be aborted and restarted. The parent class - * will call ::update_fragment_info() again to get the updated information. + * will call ::update_fragment_info() on the stream again to get the updated information. */ #define GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_2 /* The live stream has lost synchronization and the demuxer needs to be resetted */ #define GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_2 + 1 -typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState; - -typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment; -typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack; -typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod; -typedef struct _GstAdaptiveDemux GstAdaptiveDemux; -typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass; typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate; -struct _GstAdaptiveDemux2StreamFragment -{ - /* The period-local stream time for the given fragment. */ - GstClockTimeDiff stream_time; - GstClockTime duration; - - gchar *uri; - gint64 range_start; - gint64 range_end; - - /* when chunked downloading is used, may be be updated need_another_chunk() */ - gint chunk_size; - - /* when headers are needed */ - gchar *header_uri; - gint64 header_range_start; - gint64 header_range_end; - - /* when index is needed */ - gchar *index_uri; - gint64 index_range_start; - gint64 index_range_end; - - gboolean finished; -}; - struct _GstAdaptiveDemuxTrack { gint ref_count; @@ -249,119 +193,6 @@ struct _GstAdaptiveDemuxTrack gboolean output_discont; }; -enum _GstAdaptiveDemux2StreamState { - GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */ - GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */ - GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS, - GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED -}; - -struct _GstAdaptiveDemux2Stream -{ - GstObject object; - - /* FIXME : transition to gstobject->parent */ - GstAdaptiveDemux *demux; - - /* The period to which the stream belongs, set when adding the stream to the - * demuxer */ - GstAdaptiveDemuxPeriod *period; - - /* The tracks this stream targets */ - GList *tracks; - - /* The internal parsebin, forward data to track */ - GstElement *parsebin; - GstPad *parsebin_sink; - - gulong pad_added_id, pad_removed_id; - - GstSegment parse_segment; - - /* TRUE if the current stream GstSegment should be sent downstream */ - gboolean send_segment; - /* TRUE if the stream GstSegment requires recalculation (from demuxer - segment) */ - gboolean compute_segment; - /* first_and_live applies to compute_segment */ - gboolean first_and_live; - - /* When restarting, what is the target position (in demux segment) to - * begin at */ - GstClockTime start_position; - - /* Track the current position (in demux segment) of the current fragment */ - GstClockTime current_position; - - GstCaps *pending_caps; - GstTagList *pending_tags; - - GList *pending_events; - - GstFlowReturn last_ret; - GError *last_error; - - gboolean discont; - - /* download tooling */ - gboolean need_header; - gboolean need_index; - - gboolean downloading_header; - gboolean downloading_index; - - /* persistent, reused download request for fragment data */ - DownloadRequest *download_request; - - GstAdaptiveDemux2StreamState state; - guint pending_cb_id; - gboolean download_active; - /* The (global output) time at which this stream should be woken - * to download more input */ - GstClockTimeDiff next_input_wakeup_time; - - guint last_status_code; - - gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */ - gboolean download_finished; - - gboolean starting_fragment; - gboolean first_fragment_buffer; - gint64 download_start_time; - gint64 download_total_bytes; - gint64 download_end_offset; - guint64 current_download_rate; - - /* bitrate of the previous fragment (pre-queue2) */ - guint64 last_bitrate; - - /* Total last download time, from request to completion */ - GstClockTime last_download_time; - - /* Average for the last fragments */ - guint64 moving_bitrate; - guint moving_index; - guint64 *fragment_bitrates; - - GstAdaptiveDemux2StreamFragment fragment; - - guint download_error_count; - - /* Last collection provided by parsebin */ - GstStreamCollection *stream_collection; - - /* OR'd set of stream types in this stream */ - GstStreamType stream_type; - - /* The buffering threshold recommended by the subclass */ - GstClockTime recommended_buffering_threshold; -}; - /** * GstAdaptiveDemuxPeriod: * @@ -574,116 +405,6 @@ struct _GstAdaptiveDemuxClass GstSeekFlags flags, GstClockTimeDiff target_ts, GstClockTimeDiff * final_ts); - gboolean (*stream_has_next_fragment) (GstAdaptiveDemux2Stream * stream); - GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemux2Stream * stream); - - /** - * stream_can_start: - * @demux: The #GstAdaptiveDemux - * @stream: a #GstAdaptiveDemux2Stream - * - * Called before starting a @stream. sub-classes can return %FALSE if more - * information is required before it can be started. Sub-classes will have to - * call gst_adaptive_demux2_stream_start() when the stream should be started. - */ - gboolean (*stream_can_start) (GstAdaptiveDemux *demux, - GstAdaptiveDemux2Stream *stream); - - /** - * stream_update_tracks: - * @demux: The #GstAdaptiveDemux - * @stream: A #GstAdaptiveDemux2Stream - * - * Called whenever the base class collected a @collection on a @stream which has - * pending tracks to be created. Subclasses should override this if they - * create streams without tracks. - * - * * create the various tracks by analyzing the @stream stream_collection - * * Set the track upstream_stream_id to the corresponding stream_id from the collection - */ - void (*stream_update_tracks) (GstAdaptiveDemux *demux, - GstAdaptiveDemux2Stream *stream); - /** - * need_another_chunk: - * @stream: #GstAdaptiveDemux2Stream - * - * If chunked downloading is used (chunk_size != 0) this is called once a - * chunk is finished to decide whether more has to be downloaded or not. - * May update chunk_size to a different value - */ - gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream); - - /** - * stream_update_fragment_info: - * @stream: #GstAdaptiveDemux2Stream - * - * Requests the stream to set the information about the current fragment to its - * current fragment struct - * - * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS - * if there is no fragment. - */ - GstFlowReturn (*stream_update_fragment_info) (GstAdaptiveDemux2Stream * stream); - /** - * stream_select_bitrate: - * @stream: #GstAdaptiveDemux2Stream - * @bitrate: the bitrate to select (in bytes per second) - * - * The stream should try to select the bitrate that is the greater, but not - * greater than the requested bitrate. If it needs a codec change it should - * create the new stream using gst_adaptive_demux2_stream_new(). If it only - * needs a caps change it should set the new caps using - * gst_adaptive_demux2_stream_set_caps(). - * - * Returns: %TRUE if the stream changed bitrate, %FALSE otherwise - */ - gboolean (*stream_select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate); - /** - * stream_get_fragment_waiting_time: - * @stream: #GstAdaptiveDemux2Stream - * - * For live streams, requests how much time should be waited before starting - * to download the fragment. This is useful to avoid downloading a fragment that - * isn't available yet. - * - * Returns: The waiting time in as a #GstClockTime - */ - GstClockTime (*stream_get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream); - - /** - * start_fragment: - * @demux: #GstAdaptiveDemux - * @stream: #GstAdaptiveDemux2Stream - * - * Notifies the subclass that the given stream is starting the download - * of a new fragment. Can be used to reset/init internal state that is - * needed before each fragment, like decryption engines. - * - * Returns: %TRUE if successful. - */ - gboolean (*start_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream); - /** - * finish_fragment: - * @demux: #GstAdaptiveDemux - * @stream: #GstAdaptiveDemux2Stream - * - * Notifies the subclass that a fragment download was finished. - * It can be used to cleanup internal state after a fragment and - * also push any pending data before moving to the next fragment. - */ - GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream); - /** - * data_received: - * @demux: #GstAdaptiveDemux - * @stream: #GstAdaptiveDemux2Stream - * @buffer: #GstBuffer - * - * Notifies the subclass that a fragment chunk was downloaded. The subclass - * can look at the data and modify/push data as desired. - * - * Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error. - */ - GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); /** * get_live_seek_range: @@ -697,18 +418,6 @@ struct _GstAdaptiveDemuxClass */ gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop); - /** - * get_presentation_offset: - * @demux: #GstAdaptiveDemux - * @stream: #GstAdaptiveDemux2Stream - * - * Gets the delay to apply to @stream. - * - * Return: a #GstClockTime representing the (positive) time offset to apply to - * @stream. - */ - GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); - /** * get_period_start_time: * @demux: #GstAdaptiveDemux @@ -739,8 +448,6 @@ struct _GstAdaptiveDemuxClass GType gst_adaptive_demux_ng_get_type (void); -GType gst_adaptive_demux2_stream_get_type (void); - gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); @@ -757,34 +464,9 @@ GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *trac void gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track); -void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream, - GstCaps * caps); - -void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream, - GstTagList * tags); - -void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f); - -GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream, - GstBuffer * buffer); - -GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, - GstClockTime duration); - gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux); -void -gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream); - -void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream, - GstEvent * event); - -gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream); -gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream); - GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux); - GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux); gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux); diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c index 045b209e99..43bcfd5973 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c @@ -116,36 +116,32 @@ gst_hls_update_time_mappings (GstHLSDemux * demux, static void gst_hls_prune_time_mappings (GstHLSDemux * demux); static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek); + static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts); + static gboolean -gst_hls_demux_start_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static GstFlowReturn gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static GstFlowReturn gst_hls_demux_data_received (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); +gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream); +static GstFlowReturn +gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream); +static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream + * stream, GstBuffer * buffer); static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream); -static GstFlowReturn gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream * +static GstFlowReturn +gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream); +static GstFlowReturn +gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream); +static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream); -static GstFlowReturn gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream - * stream); -static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static void gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream); -static gboolean gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream, - guint64 bitrate); -static void gst_hls_demux_reset (GstAdaptiveDemux * demux); -static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, - gint64 * start, gint64 * stop); -static GstClockTime gst_hls_demux_get_presentation_offset (GstAdaptiveDemux * - demux, GstAdaptiveDemux2Stream * stream); -static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux, - GstHLSVariantStream * variant); +static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * + stream); +static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * + stream, guint64 bitrate); +static GstClockTime +gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream); static void gst_hls_demux_stream_finalize (GObject * object); @@ -161,8 +157,31 @@ static void gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class = + GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass); gobject_class->finalize = gst_hls_demux_stream_finalize; + + adaptivedemux2stream_class->update_fragment_info = + gst_hls_demux_stream_update_fragment_info; + adaptivedemux2stream_class->has_next_fragment = + gst_hls_demux_stream_has_next_fragment; + adaptivedemux2stream_class->advance_fragment = + gst_hls_demux_stream_advance_fragment; + adaptivedemux2stream_class->select_bitrate = + gst_hls_demux_stream_select_bitrate; + adaptivedemux2stream_class->can_start = gst_hls_demux_stream_can_start; + adaptivedemux2stream_class->create_tracks = + gst_hls_demux_stream_create_tracks; + + adaptivedemux2stream_class->start_fragment = + gst_hls_demux_stream_start_fragment; + adaptivedemux2stream_class->finish_fragment = + gst_hls_demux_stream_finish_fragment; + adaptivedemux2stream_class->data_received = + gst_hls_demux_stream_data_received; + adaptivedemux2stream_class->get_presentation_offset = + gst_hls_demux_stream_get_presentation_offset; } static void @@ -181,6 +200,12 @@ typedef struct _GstHLSDemux2Class GstHLSDemux2Class; G_DEFINE_TYPE_WITH_CODE (GstHLSDemux2, gst_hls_demux2, GST_TYPE_ADAPTIVE_DEMUX, hls2_element_init ()); +static void gst_hls_demux_reset (GstAdaptiveDemux * demux); +static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, + gint64 * start, gint64 * stop); +static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux, + GstHLSVariantStream * variant); + static void gst_hls_demux_finalize (GObject * obj) { @@ -263,8 +288,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass) adaptivedemux_class->is_live = gst_hls_demux_is_live; adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range; - adaptivedemux_class->get_presentation_offset = - gst_hls_demux_get_presentation_offset; adaptivedemux_class->get_duration = gst_hls_demux_get_duration; adaptivedemux_class->get_manifest_update_interval = gst_hls_demux_get_manifest_update_interval; @@ -273,19 +296,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass) adaptivedemux_class->reset = gst_hls_demux_reset; adaptivedemux_class->seek = gst_hls_demux_seek; adaptivedemux_class->stream_seek = gst_hls_demux_stream_seek; - adaptivedemux_class->stream_has_next_fragment = - gst_hls_demux_stream_has_next_fragment; - adaptivedemux_class->stream_advance_fragment = gst_hls_demux_advance_fragment; - adaptivedemux_class->stream_update_fragment_info = - gst_hls_demux_update_fragment_info; - adaptivedemux_class->stream_select_bitrate = gst_hls_demux_select_bitrate; - adaptivedemux_class->stream_can_start = gst_hls_demux_stream_can_start; - adaptivedemux_class->stream_update_tracks = - gst_hls_demux_stream_update_tracks; - - adaptivedemux_class->start_fragment = gst_hls_demux_start_fragment; - adaptivedemux_class->finish_fragment = gst_hls_demux_finish_fragment; - adaptivedemux_class->data_received = gst_hls_demux_data_received; } static void @@ -617,16 +627,15 @@ get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype) } static void -gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream) { - GstHLSDemux *hlsdemux = (GstHLSDemux *) demux; + GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux; GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream; guint i; GstStreamType uriless_types = 0; GstCaps *variant_caps = NULL; - GST_DEBUG_OBJECT (demux, "Update tracks of variant stream"); + GST_DEBUG_OBJECT (stream, "Update tracks of variant stream"); if (hlsdemux->master->have_codecs) { variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master); @@ -662,7 +671,7 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux, if (embedded_media) { GstTagList *tags = gst_stream_get_tags (gst_stream); - GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream", + GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream", embedded_media->name); track = new_track_for_rendition (hlsdemux, embedded_media, manifest_caps, @@ -673,10 +682,10 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux, g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type), i); - GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream", + GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream", stream_id); track = - gst_adaptive_demux_track_new (demux, stream_type, + gst_adaptive_demux_track_new (stream->demux, stream_type, flags, stream_id, manifest_caps, NULL); g_free (stream_id); } @@ -1037,11 +1046,10 @@ out: } static gboolean -gst_hls_demux_start_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream) { GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); - GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); + GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux); const GstHLSKey *key; GstHLSMediaPlaylist *m3u8; @@ -1069,14 +1077,14 @@ gst_hls_demux_start_fragment (GstAdaptiveDemux * demux, key_failed: { - GST_ELEMENT_ERROR (demux, STREAM, DECRYPT_NOKEY, + GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY, ("Couldn't retrieve key for decryption"), (NULL)); - GST_WARNING_OBJECT (demux, "Failed to decrypt data"); + GST_WARNING_OBJECT (hlsdemux, "Failed to decrypt data"); return FALSE; } decrypt_start_failed: { - GST_ELEMENT_ERROR (demux, STREAM, DECRYPT, ("Failed to start decrypt"), + GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT, ("Failed to start decrypt"), ("Couldn't set key and IV or plugin was built without crypto library")); return FALSE; } @@ -1480,11 +1488,11 @@ out_resync: } static GstFlowReturn -gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer, gboolean at_eos) +gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream, + GstBuffer * buffer, gboolean at_eos) { GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function - GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); + GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux); GstFlowReturn ret = GST_FLOW_OK; GstBuffer *pending_header_data = NULL; @@ -1596,8 +1604,7 @@ out: } static GstFlowReturn -gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream) { GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function GstFlowReturn ret = GST_FLOW_OK; @@ -1626,7 +1633,7 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, } ret = - gst_hls_demux_handle_buffer (demux, stream, + gst_hls_demux_stream_handle_buffer (stream, hls_stream->pending_decrypted_buffer, TRUE); hls_stream->pending_decrypted_buffer = NULL; } @@ -1636,14 +1643,14 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, GstBuffer *buf = hls_stream->pending_typefind_buffer; hls_stream->pending_typefind_buffer = NULL; - gst_hls_demux_handle_buffer (demux, stream, buf, TRUE); + gst_hls_demux_stream_handle_buffer (stream, buf, TRUE); } if (hls_stream->pending_segment_data) { GstBuffer *buf = hls_stream->pending_segment_data; hls_stream->pending_segment_data = NULL; - ret = gst_hls_demux_handle_buffer (demux, stream, buf, TRUE); + ret = gst_hls_demux_stream_handle_buffer (stream, buf, TRUE); } } } @@ -1664,18 +1671,18 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, * before advancing. Note that we don't have any period so we can set the * stream_time as-is on the stream current position */ stream->current_position = hls_stream->current_segment->stream_time; - return gst_adaptive_demux2_stream_advance_fragment (demux, stream, + return gst_adaptive_demux2_stream_advance_fragment (stream, hls_stream->current_segment->duration); } return ret; } static GstFlowReturn -gst_hls_demux_data_received (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream, GstBuffer * buffer) +gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream, + GstBuffer * buffer) { GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); - GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); + GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux); if (hls_stream->current_segment == NULL) return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC; @@ -1707,7 +1714,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux, decrypted_buffer = gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err); if (err) { - GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"), + GST_ELEMENT_ERROR (hlsdemux, STREAM, DECODE, ("Failed to decrypt buffer"), ("decryption failed %s", err->message)); g_error_free (err); return GST_FLOW_ERROR; @@ -1720,7 +1727,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux, return GST_FLOW_OK; } - return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE); + return gst_hls_demux_stream_handle_buffer (stream, buffer, FALSE); } static void @@ -1794,7 +1801,7 @@ gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream) } static GstFlowReturn -gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream) { GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream); GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux; @@ -2330,7 +2337,7 @@ gst_hls_demux_stream_update_variant_playlist (GstHLSDemux * demux, } static GstFlowReturn -gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream) { GstFlowReturn ret = GST_FLOW_OK; GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream); @@ -2456,14 +2463,13 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream) } static gboolean -gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream) { - GstHLSDemux *hlsdemux = (GstHLSDemux *) demux; + GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux; GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream; GList *tmp; - GST_DEBUG_OBJECT (demux, "is_variant:%d mappings:%p", hls_stream->is_variant, + GST_DEBUG_OBJECT (stream, "is_variant:%d mappings:%p", hls_stream->is_variant, hlsdemux->mappings); /* Variant streams can always start straight away */ @@ -2556,7 +2562,8 @@ gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux, } static gboolean -gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream, guint64 bitrate) +gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream, + guint64 bitrate) { GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux); GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux); @@ -2964,10 +2971,9 @@ gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * demux) } static GstClockTime -gst_hls_demux_get_presentation_offset (GstAdaptiveDemux * demux, - GstAdaptiveDemux2Stream * stream) +gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream) { - GstHLSDemux *hlsdemux = (GstHLSDemux *) demux; + GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux; GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream; GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,