mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
This commit is contained in:
parent
9f89b8e3ef
commit
2fe641353d
8 changed files with 1139 additions and 1023 deletions
|
@ -351,18 +351,12 @@ static void gst_dash_demux_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
static void gst_dash_demux_dispose (GObject * obj);
|
static void gst_dash_demux_dispose (GObject * obj);
|
||||||
|
|
||||||
/* GstAdaptiveDemux */
|
/* GstAdaptiveDemuxStream */
|
||||||
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
|
static GstFlowReturn
|
||||||
gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
|
gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
|
||||||
static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream *
|
static GstClockTime
|
||||||
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
|
gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
|
||||||
GstClockTimeDiff * final_ts);
|
stream);
|
||||||
static gboolean gst_dash_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
|
static gboolean gst_dash_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
|
||||||
* stream);
|
* stream);
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
@ -371,25 +365,35 @@ static gboolean
|
||||||
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemux2Stream * stream);
|
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemux2Stream * stream);
|
||||||
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
|
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
|
||||||
stream, guint64 bitrate);
|
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 *
|
static gint64 gst_dash_demux_get_manifest_update_interval (GstAdaptiveDemux *
|
||||||
demux);
|
demux);
|
||||||
static GstFlowReturn gst_dash_demux_update_manifest_data (GstAdaptiveDemux *
|
static GstFlowReturn gst_dash_demux_update_manifest_data (GstAdaptiveDemux *
|
||||||
demux, GstBuffer * buf);
|
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 void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
|
||||||
static gboolean gst_dash_demux_has_next_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 */
|
/* GstDashDemux2 */
|
||||||
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux2 * demux);
|
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux2 * demux);
|
||||||
|
@ -464,8 +468,32 @@ static void
|
||||||
gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass)
|
gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
|
||||||
|
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
|
||||||
|
|
||||||
gobject_class->finalize = gst_dash_demux_stream_finalize;
|
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
|
static GstClockTime
|
||||||
gst_dash_demux_get_presentation_offset (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) 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,
|
return gst_mpd_client2_get_stream_presentation_offset (dashdemux->client,
|
||||||
dashstream->index);
|
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->has_next_period = gst_dash_demux_has_next_period;
|
||||||
gstadaptivedemux_class->advance_period = gst_dash_demux_advance_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_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 =
|
gstadaptivedemux_class->get_live_seek_range =
|
||||||
gst_dash_demux_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 =
|
gstadaptivedemux_class->get_period_start_time =
|
||||||
gst_dash_demux_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
|
static void
|
||||||
|
@ -1827,10 +1836,10 @@ gst_dash_demux_stream_get_target_time (GstDashDemux2 * dashdemux,
|
||||||
GstClockTime deadline;
|
GstClockTime deadline;
|
||||||
GstClockTime upstream_earliest_time;
|
GstClockTime upstream_earliest_time;
|
||||||
GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
|
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 period_start = gst_dash_demux_get_period_start_time (demux);
|
||||||
GstClockTime pts_offset =
|
GstClockTime pts_offset =
|
||||||
gst_dash_demux_get_presentation_offset (demux, stream);
|
gst_dash_demux_stream_get_presentation_offset (stream);
|
||||||
|
|
||||||
g_assert (min_skip > 0);
|
g_assert (min_skip > 0);
|
||||||
|
|
||||||
|
@ -2706,10 +2715,9 @@ _gst_buffer_split (GstBuffer * buffer, gint offset, gsize size)
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
|
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
|
||||||
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
||||||
|
|
||||||
GST_LOG_OBJECT (stream, "Actual position %" GST_TIME_FORMAT,
|
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
|
* buffer. We need offsets to be consistent between moof and mdat
|
||||||
*/
|
*/
|
||||||
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
|
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)
|
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
|
||||||
stream->discont = TRUE;
|
stream->discont = TRUE;
|
||||||
|
|
||||||
|
@ -2733,11 +2741,10 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstClockTime consumed_duration;
|
GstClockTime consumed_duration;
|
||||||
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
|
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
|
||||||
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
||||||
|
|
||||||
/* We need to mark every first buffer of a key unit as discont,
|
/* 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
|
* buffer. We need offsets to be consistent between moof and mdat
|
||||||
*/
|
*/
|
||||||
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
|
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)
|
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
|
||||||
stream->discont = TRUE;
|
stream->discont = TRUE;
|
||||||
|
|
||||||
|
@ -2774,20 +2781,20 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
||||||
consumed_duration =
|
consumed_duration =
|
||||||
(stream->fragment.stream_time + stream->fragment.duration) -
|
(stream->fragment.stream_time + stream->fragment.duration) -
|
||||||
stream->current_position;
|
stream->current_position;
|
||||||
GST_LOG_OBJECT (demux, "Consumed duration after seeking: %"
|
GST_LOG_OBJECT (stream, "Consumed duration after seeking: %"
|
||||||
GST_TIMEP_FORMAT, &consumed_duration);
|
GST_TIMEP_FORMAT, &consumed_duration);
|
||||||
} else {
|
} else {
|
||||||
consumed_duration = stream->fragment.duration;
|
consumed_duration = stream->fragment.duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
|
return gst_adaptive_demux2_stream_advance_fragment (stream,
|
||||||
consumed_duration);
|
consumed_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
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;
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
|
||||||
gboolean playing_forward = (demux->segment.rate > 0.0);
|
gboolean playing_forward = (demux->segment.rate > 0.0);
|
||||||
|
@ -3371,9 +3378,9 @@ gst_dash_demux_find_sync_samples (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_handle_isobmff (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
|
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
GstBuffer *buffer;
|
GstBuffer *buffer;
|
||||||
|
@ -3527,22 +3534,22 @@ gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
if (sidx_advance) {
|
if (sidx_advance) {
|
||||||
ret =
|
ret =
|
||||||
gst_adaptive_demux2_stream_advance_fragment (demux, stream,
|
gst_adaptive_demux2_stream_advance_fragment (stream,
|
||||||
SIDX_CURRENT_ENTRY (dash_stream)->duration);
|
SIDX_CURRENT_ENTRY (dash_stream)->duration);
|
||||||
if (ret != GST_FLOW_OK)
|
if (ret != GST_FLOW_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* If we still have data available, recurse and use it up if possible */
|
/* If we still have data available, recurse and use it up if possible */
|
||||||
if (gst_adapter_available (dash_stream->adapter) > 0)
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
|
||||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
|
GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
|
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
@ -3574,7 +3581,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
if (dash_stream->is_isobmff || stream->downloading_index) {
|
if (dash_stream->is_isobmff || stream->downloading_index) {
|
||||||
/* SIDX index is also ISOBMMF */
|
/* 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) {
|
} else if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
|
||||||
gsize available;
|
gsize available;
|
||||||
|
|
||||||
|
@ -3622,7 +3629,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
if (has_next) {
|
if (has_next) {
|
||||||
GstFlowReturn new_ret;
|
GstFlowReturn new_ret;
|
||||||
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);
|
SIDX_CURRENT_ENTRY (dash_stream)->duration);
|
||||||
|
|
||||||
/* only overwrite if it was OK before */
|
/* only overwrite if it was OK before */
|
||||||
|
|
|
@ -25,9 +25,16 @@
|
||||||
#ifndef _GST_ADAPTIVE_DEMUX_PRIVATE_H_
|
#ifndef _GST_ADAPTIVE_DEMUX_PRIVATE_H_
|
||||||
#define _GST_ADAPTIVE_DEMUX_PRIVATE_H_
|
#define _GST_ADAPTIVE_DEMUX_PRIVATE_H_
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstadapter.h>
|
#include <gst/base/gstadapter.h>
|
||||||
#include <gst/base/gstflowcombiner.h>
|
#include <gst/base/gstflowcombiner.h>
|
||||||
|
|
||||||
|
#include "gstadaptivedemux-types.h"
|
||||||
|
#include "gstadaptivedemux.h"
|
||||||
|
#include "gstadaptivedemuxutils.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define NUM_LOOKBACK_FRAGMENTS 3
|
#define NUM_LOOKBACK_FRAGMENTS 3
|
||||||
#define MAX_DOWNLOAD_ERROR_COUNT 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_wants_manifest_update (GstAdaptiveDemux * demux);
|
||||||
|
|
||||||
void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err);
|
void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err);
|
||||||
GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
|
GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * stream);
|
||||||
demux, GstAdaptiveDemux2Stream * stream);
|
GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
|
||||||
GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
|
|
||||||
GstAdaptiveDemux2Stream * stream);
|
|
||||||
GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux);
|
GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux);
|
||||||
|
|
||||||
gboolean gst_adaptive_demux_is_live (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_manifest_update (GstAdaptiveDemux2Stream * stream);
|
||||||
void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
|
void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
|
||||||
|
|
||||||
gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
|
gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream);
|
||||||
GstAdaptiveDemux2Stream * stream);
|
|
||||||
GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
|
|
||||||
GstAdaptiveDemux2Stream * stream);
|
|
||||||
GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
|
GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags,
|
GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags,
|
||||||
GstClockTimeDiff ts, GstClockTimeDiff * final_ts);
|
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);
|
gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux);
|
||||||
void gst_adaptive_demux_advance_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);
|
void gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream);
|
||||||
|
|
||||||
gboolean gst_adaptive_demux_handle_lost_sync (GstAdaptiveDemux * demux);
|
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);
|
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);
|
void gst_adaptive_demux_period_check_input_wakeup_locked (GstAdaptiveDemuxPeriod * period, GstClockTimeDiff current_output_position);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gstadaptivedemux.h"
|
#include "gstadaptivedemux-stream.h"
|
||||||
#include "gstadaptivedemux-private.h"
|
#include "gstadaptivedemux-private.h"
|
||||||
|
|
||||||
#include <glib/gi18n-lib.h>
|
#include <glib/gi18n-lib.h>
|
||||||
|
@ -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_finalize (GObject * object);
|
||||||
static void gst_adaptive_demux2_stream_error (GstAdaptiveDemux2Stream * stream);
|
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
|
#define gst_adaptive_demux2_stream_parent_class parent_class
|
||||||
G_DEFINE_ABSTRACT_TYPE (GstAdaptiveDemux2Stream, gst_adaptive_demux2_stream,
|
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;
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
|
||||||
gobject_class->finalize = gst_adaptive_demux2_stream_finalize;
|
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;
|
static GType tsdemux_type = 0;
|
||||||
|
@ -255,10 +270,10 @@ schedule_another_chunk (GstAdaptiveDemux2Stream * stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
drain_inactive_tracks (GstAdaptiveDemux * demux,
|
drain_inactive_tracks (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GList *iter;
|
GList *iter;
|
||||||
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
|
|
||||||
TRACKS_LOCK (demux);
|
TRACKS_LOCK (demux);
|
||||||
for (iter = stream->tracks; iter; iter = iter->next) {
|
for (iter = stream->tracks; iter; iter = iter->next) {
|
||||||
|
@ -278,8 +293,8 @@ static void
|
||||||
gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
||||||
stream, GstFlowReturn ret, GError * err)
|
stream, GstFlowReturn ret, GError * err)
|
||||||
{
|
{
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
|
GstAdaptiveDemux2StreamClass *klass =
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (stream,
|
GST_DEBUG_OBJECT (stream,
|
||||||
"%s download finish: %d %s - err: %p", uritype (stream), ret,
|
"%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) {
|
if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) {
|
||||||
/* We lost sync, seek back to live and return */
|
/* We lost sync, seek back to live and return */
|
||||||
GST_WARNING_OBJECT (stream, "Lost sync when downloading");
|
GST_WARNING_OBJECT (stream, "Lost sync when downloading");
|
||||||
gst_adaptive_demux_handle_lost_sync (demux);
|
gst_adaptive_demux_handle_lost_sync (stream->demux);
|
||||||
return;
|
return;
|
||||||
} else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) {
|
} else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) {
|
||||||
/* The sub-class wants to stop the fragment immediately */
|
/* The sub-class wants to stop the fragment immediately */
|
||||||
stream->fragment.finished = TRUE;
|
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_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
|
||||||
gst_flow_get_name (ret));
|
gst_flow_get_name (ret));
|
||||||
|
@ -331,7 +346,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
||||||
|| !klass->need_another_chunk (stream)
|
|| !klass->need_another_chunk (stream)
|
||||||
|| stream->fragment.chunk_size == 0) {
|
|| stream->fragment.chunk_size == 0) {
|
||||||
stream->fragment.finished = TRUE;
|
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_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
|
||||||
gst_flow_get_name (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
|
/* For HLS, we might be enqueueing data into tracks that aren't
|
||||||
* selected. Drain those ones out */
|
* 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
|
/* Now that we've called finish_fragment we can clear these flags the
|
||||||
* sub-class might have checked */
|
* sub-class might have checked */
|
||||||
|
@ -387,7 +402,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
||||||
|
|
||||||
GST_LOG_OBJECT (stream, "Scheduling next_download() call");
|
GST_LOG_OBJECT (stream, "Scheduling next_download() call");
|
||||||
stream->pending_cb_id =
|
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,
|
(GSourceFunc) gst_adaptive_demux2_stream_next_download,
|
||||||
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
|
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
|
||||||
}
|
}
|
||||||
|
@ -414,13 +429,16 @@ gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream * stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux,
|
gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux2Stream * stream,
|
||||||
GstAdaptiveDemux2Stream * stream, gboolean first_and_live)
|
gboolean first_and_live)
|
||||||
{
|
{
|
||||||
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
GstClockTime period_start = gst_adaptive_demux_get_period_start_time (demux);
|
GstClockTime period_start = gst_adaptive_demux_get_period_start_time (demux);
|
||||||
GstClockTime offset =
|
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;
|
stream->parse_segment = demux->segment;
|
||||||
|
|
||||||
/* The demuxer segment is just built from seek events, but for each stream
|
/* 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
|
* the segment time and base as calculated by the second case would be
|
||||||
* equivalent.
|
* equivalent.
|
||||||
*/
|
*/
|
||||||
GST_DEBUG_OBJECT (demux, "Using demux segment %" GST_SEGMENT_FORMAT,
|
GST_DEBUG_OBJECT (stream, "Using demux segment %" GST_SEGMENT_FORMAT,
|
||||||
&demux->segment);
|
&stream->parse_segment);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (demux,
|
GST_DEBUG_OBJECT (demux,
|
||||||
"period_start: %" GST_TIME_FORMAT " offset: %" GST_TIME_FORMAT,
|
"period_start: %" GST_TIME_FORMAT " offset: %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (period_start), GST_TIME_ARGS (offset));
|
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)) {
|
if (GST_CLOCK_STIME_IS_VALID (pos)) {
|
||||||
GstClockTime offset =
|
GstClockTime offset =
|
||||||
gst_adaptive_demux2_stream_get_presentation_offset (demux, stream);
|
gst_adaptive_demux2_stream_get_presentation_offset (stream);
|
||||||
|
|
||||||
pos += offset;
|
pos += offset;
|
||||||
|
|
||||||
|
@ -574,8 +593,7 @@ gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
|
||||||
GList *pending_events = NULL;
|
GList *pending_events = NULL;
|
||||||
|
|
||||||
if (stream->compute_segment) {
|
if (stream->compute_segment) {
|
||||||
gst_adaptive_demux2_stream_prepare_segment (demux, stream,
|
gst_adaptive_demux2_stream_prepare_segment (stream, stream->first_and_live);
|
||||||
stream->first_and_live);
|
|
||||||
stream->compute_segment = FALSE;
|
stream->compute_segment = FALSE;
|
||||||
stream->first_and_live = FALSE;
|
stream->first_and_live = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -711,7 +729,8 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
|
||||||
GstBuffer * buffer)
|
GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
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;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
/* do not make any changes if the stream is cancelled */
|
/* 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 ! */
|
* including the *actual* fragment ! */
|
||||||
if (stream->starting_fragment) {
|
if (stream->starting_fragment) {
|
||||||
stream->starting_fragment = FALSE;
|
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;
|
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),
|
"Received %s buffer of size %" G_GSIZE_FORMAT, uritype (stream),
|
||||||
gst_buffer_get_size (buffer));
|
gst_buffer_get_size (buffer));
|
||||||
|
|
||||||
ret = klass->data_received (demux, stream, buffer);
|
ret = klass->data_received (stream, buffer);
|
||||||
|
|
||||||
if (ret != GST_FLOW_OK) {
|
if (ret != GST_FLOW_OK) {
|
||||||
GST_DEBUG_OBJECT (stream, "data_received returned %s",
|
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");
|
GST_DEBUG_OBJECT (stream, "Pushing EOS to parser");
|
||||||
|
|
||||||
/* TODO push this on all pads */
|
/* 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);
|
gst_pad_send_event (stream->parsebin_sink, eos);
|
||||||
ret = GST_FLOW_ERROR;
|
ret = GST_FLOW_ERROR;
|
||||||
|
|
||||||
|
@ -1177,7 +1196,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
||||||
|| last_status_code / 100 == 5)) {
|
|| last_status_code / 100 == 5)) {
|
||||||
/* 4xx/5xx */
|
/* 4xx/5xx */
|
||||||
/* if current position is before available start, switch to next */
|
/* 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;
|
goto flushing;
|
||||||
|
|
||||||
if (live) {
|
if (live) {
|
||||||
|
@ -1195,7 +1214,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
|
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_DEBUG_OBJECT (stream, "update_fragment_info ret: %s",
|
||||||
gst_flow_get_name (ret));
|
gst_flow_get_name (ret));
|
||||||
|
|
||||||
|
@ -1205,8 +1224,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
||||||
} else if (demux->segment.position > range_stop) {
|
} else if (demux->segment.position > range_stop) {
|
||||||
/* wait a bit to be in range, we don't have any locks at that point */
|
/* wait a bit to be in range, we don't have any locks at that point */
|
||||||
GstClockTime wait_time =
|
GstClockTime wait_time =
|
||||||
gst_adaptive_demux2_stream_get_fragment_waiting_time (demux,
|
gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
|
||||||
stream);
|
|
||||||
if (wait_time > 0) {
|
if (wait_time > 0) {
|
||||||
GST_DEBUG_OBJECT (stream,
|
GST_DEBUG_OBJECT (stream,
|
||||||
"Download waiting for %" GST_TIME_FORMAT,
|
"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);
|
gst_adaptive_demux2_stream_handle_playlist_eos (stream);
|
||||||
return;
|
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
|
/* If this is the last fragment, consider failures EOS and not actual
|
||||||
* errors. Due to rounding errors in the durations, the last fragment
|
* errors. Due to rounding errors in the durations, the last fragment
|
||||||
* might not actually exist */
|
* might not actually exist */
|
||||||
|
@ -1421,7 +1439,8 @@ static GstFlowReturn
|
||||||
gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
|
gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
|
||||||
{
|
{
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
GstAdaptiveDemux2StreamClass *klass =
|
||||||
|
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
|
||||||
gchar *url = NULL;
|
gchar *url = NULL;
|
||||||
|
|
||||||
/* FIXME : */
|
/* FIXME : */
|
||||||
|
@ -1760,7 +1779,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
|
||||||
case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE:
|
case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE:
|
||||||
/* Get information about the fragment to download */
|
/* Get information about the fragment to download */
|
||||||
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
|
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,
|
GST_DEBUG_OBJECT (stream,
|
||||||
"Fragment info update result: %d %s", ret, gst_flow_get_name (ret));
|
"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 */
|
/* wait for live fragments to be available */
|
||||||
if (live) {
|
if (live) {
|
||||||
GstClockTime wait_time =
|
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) {
|
if (wait_time > 0) {
|
||||||
GST_DEBUG_OBJECT (stream,
|
GST_DEBUG_OBJECT (stream,
|
||||||
"Download waiting for %" GST_TIME_FORMAT,
|
"Download waiting for %" GST_TIME_FORMAT,
|
||||||
|
@ -1967,12 +1986,12 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream)
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream)
|
gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream)
|
||||||
{
|
{
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
GstAdaptiveDemux2StreamClass *klass =
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
|
||||||
|
|
||||||
if (!klass->stream_can_start)
|
if (!klass->can_start)
|
||||||
return TRUE;
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,348 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||||
|
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021-2022 Centricular Ltd
|
||||||
|
* Author: Edward Hervey <edward@centricular.com>
|
||||||
|
* Author: Jan Schmidt <jan@centricular.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
#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
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||||
|
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021-2022 Centricular Ltd
|
||||||
|
* Author: Edward Hervey <edward@centricular.com>
|
||||||
|
* Author: Jan Schmidt <jan@centricular.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
|
@ -229,8 +229,6 @@ static void gst_adaptive_demux_reset (GstAdaptiveDemux * demux);
|
||||||
static gboolean gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
|
static gboolean gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
|
||||||
gboolean first_and_live);
|
gboolean first_and_live);
|
||||||
|
|
||||||
static gboolean gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
|
|
||||||
demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate);
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_adaptive_demux_update_manifest_default (GstAdaptiveDemux * demux);
|
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_start_tasks (GstAdaptiveDemux * demux);
|
||||||
static void gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux,
|
static void gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux,
|
||||||
gboolean stop_updates);
|
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
|
static gboolean
|
||||||
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
|
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_element_class_add_static_pad_template (gstelement_class,
|
||||||
&gst_adaptive_demux_subtitlesrc_template);
|
&gst_adaptive_demux_subtitlesrc_template);
|
||||||
|
|
||||||
|
|
||||||
gstelement_class->change_state = gst_adaptive_demux_change_state;
|
gstelement_class->change_state = gst_adaptive_demux_change_state;
|
||||||
gstelement_class->query = gst_adaptive_demux_query;
|
gstelement_class->query = gst_adaptive_demux_query;
|
||||||
gstelement_class->send_event = gst_adaptive_demux_send_event;
|
gstelement_class->send_event = gst_adaptive_demux_send_event;
|
||||||
|
|
||||||
gstbin_class->handle_message = gst_adaptive_demux_handle_message;
|
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->update_manifest = gst_adaptive_demux_update_manifest_default;
|
||||||
klass->requires_periodical_playlist_update =
|
klass->requires_periodical_playlist_update =
|
||||||
gst_adaptive_demux_requires_periodical_playlist_update_default;
|
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);
|
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;
|
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
|
static void
|
||||||
gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
|
gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
|
||||||
GstMessage * msg)
|
GstMessage * msg)
|
||||||
|
@ -1606,60 +1470,38 @@ gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
|
||||||
if (!collection)
|
if (!collection)
|
||||||
goto beach;
|
goto beach;
|
||||||
|
|
||||||
/* Check whether the collection is "sane" or not.
|
TRACKS_LOCK (demux);
|
||||||
*
|
|
||||||
* In the context of adaptive streaming, we can only handle multiplexed
|
if (!gst_adaptive_demux2_stream_handle_collection (stream, collection,
|
||||||
* content that provides at most one stream of valid types (audio, video,
|
&pending_tracks_activated)) {
|
||||||
* text). Without this we cannot reliably match the output of this multiplex
|
TRACKS_UNLOCK (demux);
|
||||||
* 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)) {
|
|
||||||
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
|
||||||
(_("Stream format can't be handled")),
|
(_("Stream format can't be handled")),
|
||||||
("The streams provided by the multiplex are ambiguous"));
|
("The streams provided by the multiplex are ambiguous"));
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store the collection on the stream */
|
if (pending_tracks_activated) {
|
||||||
gst_object_replace ((GstObject **) & stream->stream_collection,
|
/* If pending tracks were handled, then update the demuxer 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 (gst_adaptive_demux_update_collection (demux, demux->input_period) &&
|
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);
|
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
|
/* If we discovered pending tracks and we no longer have any, we can ensure
|
||||||
* selected tracks are started */
|
* selected tracks are started */
|
||||||
if (pending_tracks_activated
|
if (!gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
|
||||||
&& !gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
|
GList *iter = demux->input_period->streams;
|
||||||
GList *iter = demux->input_period->streams;
|
for (; iter; iter = iter->next) {
|
||||||
for (; iter; iter = iter->next) {
|
GstAdaptiveDemux2Stream *new_stream = iter->data;
|
||||||
GstAdaptiveDemux2Stream *new_stream = iter->data;
|
|
||||||
|
|
||||||
/* The stream that posted this collection was already started. If a
|
/* The stream that posted this collection was already started. If a
|
||||||
* different stream is now selected, start it */
|
* different stream is now selected, start it */
|
||||||
if (stream != new_stream
|
if (stream != new_stream
|
||||||
&& gst_adaptive_demux2_stream_is_selected_locked (new_stream))
|
&& gst_adaptive_demux2_stream_is_selected_locked (new_stream))
|
||||||
gst_adaptive_demux2_stream_start (new_stream);
|
gst_adaptive_demux2_stream_start (new_stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TRACKS_UNLOCK (demux);
|
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);
|
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 */
|
/* must be called with manifest_lock taken */
|
||||||
GstClockTime
|
GstClockTime
|
||||||
gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux)
|
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
|
/* TODO we only need the first timestamp, maybe create a simple function to
|
||||||
* get the current PTS of a fragment ? */
|
* get the current PTS of a fragment ? */
|
||||||
GST_DEBUG_OBJECT (stream, "Calling update_fragment_info");
|
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,
|
GST_DEBUG_OBJECT (stream,
|
||||||
"Got stream time %" GST_STIME_FORMAT,
|
"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);
|
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
|
static gboolean
|
||||||
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
|
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
|
||||||
* demux)
|
* demux)
|
||||||
|
@ -3877,181 +3580,6 @@ gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
|
||||||
return GST_FLOW_ERROR;
|
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
|
static void
|
||||||
handle_manifest_download_complete (DownloadRequest * request,
|
handle_manifest_download_complete (DownloadRequest * request,
|
||||||
DownloadRequestState state, GstAdaptiveDemux * demux)
|
DownloadRequestState state, GstAdaptiveDemux * demux)
|
||||||
|
@ -4160,29 +3688,6 @@ gst_adaptive_demux_update_manifest (GstAdaptiveDemux * demux)
|
||||||
return ret;
|
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 */
|
/* must be called with manifest_lock taken */
|
||||||
gboolean
|
gboolean
|
||||||
gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)
|
gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)
|
||||||
|
|
|
@ -34,32 +34,11 @@
|
||||||
|
|
||||||
#include "gstadaptivedemuxutils.h"
|
#include "gstadaptivedemuxutils.h"
|
||||||
|
|
||||||
|
#include "gstadaptivedemux-types.h"
|
||||||
|
#include "gstadaptivedemux-stream.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
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:
|
* 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_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:
|
* 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
|
#define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1
|
||||||
|
|
||||||
/* Current fragment download should be aborted and restarted. The parent class
|
/* 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
|
#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 */
|
/* 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
|
#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;
|
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
|
struct _GstAdaptiveDemuxTrack
|
||||||
{
|
{
|
||||||
gint ref_count;
|
gint ref_count;
|
||||||
|
@ -249,119 +193,6 @@ struct _GstAdaptiveDemuxTrack
|
||||||
gboolean output_discont;
|
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:
|
* GstAdaptiveDemuxPeriod:
|
||||||
*
|
*
|
||||||
|
@ -574,116 +405,6 @@ struct _GstAdaptiveDemuxClass
|
||||||
GstSeekFlags flags,
|
GstSeekFlags flags,
|
||||||
GstClockTimeDiff target_ts,
|
GstClockTimeDiff target_ts,
|
||||||
GstClockTimeDiff * final_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:
|
* get_live_seek_range:
|
||||||
|
@ -697,18 +418,6 @@ struct _GstAdaptiveDemuxClass
|
||||||
*/
|
*/
|
||||||
gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop);
|
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:
|
* get_period_start_time:
|
||||||
* @demux: #GstAdaptiveDemux
|
* @demux: #GstAdaptiveDemux
|
||||||
|
@ -739,8 +448,6 @@ struct _GstAdaptiveDemuxClass
|
||||||
|
|
||||||
GType gst_adaptive_demux_ng_get_type (void);
|
GType gst_adaptive_demux_ng_get_type (void);
|
||||||
|
|
||||||
GType gst_adaptive_demux2_stream_get_type (void);
|
|
||||||
|
|
||||||
gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux,
|
gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux,
|
||||||
GstAdaptiveDemux2Stream *stream);
|
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_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);
|
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);
|
GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux);
|
||||||
|
|
||||||
GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux);
|
GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux);
|
||||||
|
|
||||||
gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux);
|
gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux);
|
||||||
|
|
|
@ -116,36 +116,32 @@ gst_hls_update_time_mappings (GstHLSDemux * demux,
|
||||||
static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
|
static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
|
||||||
|
|
||||||
static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
|
static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
|
||||||
|
|
||||||
static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream *
|
static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream *
|
||||||
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
|
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
|
||||||
GstClockTimeDiff * final_ts);
|
GstClockTimeDiff * final_ts);
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
|
||||||
GstAdaptiveDemux2Stream * stream);
|
static GstFlowReturn
|
||||||
static GstFlowReturn gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream);
|
||||||
GstAdaptiveDemux2Stream * stream);
|
static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream
|
||||||
static GstFlowReturn gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
* stream, GstBuffer * buffer);
|
||||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
|
|
||||||
|
|
||||||
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
|
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
|
||||||
* stream);
|
* 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);
|
stream);
|
||||||
static GstFlowReturn gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream
|
static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream *
|
||||||
* stream);
|
stream);
|
||||||
static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
|
static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
|
||||||
GstAdaptiveDemux2Stream * stream);
|
stream, guint64 bitrate);
|
||||||
static void gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
|
static GstClockTime
|
||||||
GstAdaptiveDemux2Stream * stream);
|
gst_hls_demux_stream_get_presentation_offset (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_finalize (GObject * object);
|
static void gst_hls_demux_stream_finalize (GObject * object);
|
||||||
|
|
||||||
|
@ -161,8 +157,31 @@ static void
|
||||||
gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
|
gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
|
||||||
|
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
|
||||||
|
|
||||||
gobject_class->finalize = gst_hls_demux_stream_finalize;
|
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
|
static void
|
||||||
|
@ -181,6 +200,12 @@ typedef struct _GstHLSDemux2Class GstHLSDemux2Class;
|
||||||
G_DEFINE_TYPE_WITH_CODE (GstHLSDemux2, gst_hls_demux2, GST_TYPE_ADAPTIVE_DEMUX,
|
G_DEFINE_TYPE_WITH_CODE (GstHLSDemux2, gst_hls_demux2, GST_TYPE_ADAPTIVE_DEMUX,
|
||||||
hls2_element_init ());
|
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
|
static void
|
||||||
gst_hls_demux_finalize (GObject * obj)
|
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->is_live = gst_hls_demux_is_live;
|
||||||
adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range;
|
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_duration = gst_hls_demux_get_duration;
|
||||||
adaptivedemux_class->get_manifest_update_interval =
|
adaptivedemux_class->get_manifest_update_interval =
|
||||||
gst_hls_demux_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->reset = gst_hls_demux_reset;
|
||||||
adaptivedemux_class->seek = gst_hls_demux_seek;
|
adaptivedemux_class->seek = gst_hls_demux_seek;
|
||||||
adaptivedemux_class->stream_seek = gst_hls_demux_stream_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
|
static void
|
||||||
|
@ -617,16 +627,15 @@ get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
|
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
|
||||||
GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
|
GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
|
||||||
guint i;
|
guint i;
|
||||||
GstStreamType uriless_types = 0;
|
GstStreamType uriless_types = 0;
|
||||||
GstCaps *variant_caps = NULL;
|
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) {
|
if (hlsdemux->master->have_codecs) {
|
||||||
variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master);
|
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) {
|
if (embedded_media) {
|
||||||
GstTagList *tags = gst_stream_get_tags (gst_stream);
|
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);
|
embedded_media->name);
|
||||||
track =
|
track =
|
||||||
new_track_for_rendition (hlsdemux, embedded_media, manifest_caps,
|
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),
|
g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type),
|
||||||
i);
|
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);
|
stream_id);
|
||||||
track =
|
track =
|
||||||
gst_adaptive_demux_track_new (demux, stream_type,
|
gst_adaptive_demux_track_new (stream->demux, stream_type,
|
||||||
flags, stream_id, manifest_caps, NULL);
|
flags, stream_id, manifest_caps, NULL);
|
||||||
g_free (stream_id);
|
g_free (stream_id);
|
||||||
}
|
}
|
||||||
|
@ -1037,11 +1046,10 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (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;
|
const GstHLSKey *key;
|
||||||
GstHLSMediaPlaylist *m3u8;
|
GstHLSMediaPlaylist *m3u8;
|
||||||
|
|
||||||
|
@ -1069,14 +1077,14 @@ gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
key_failed:
|
key_failed:
|
||||||
{
|
{
|
||||||
GST_ELEMENT_ERROR (demux, STREAM, DECRYPT_NOKEY,
|
GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY,
|
||||||
("Couldn't retrieve key for decryption"), (NULL));
|
("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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
decrypt_start_failed:
|
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"));
|
("Couldn't set key and IV or plugin was built without crypto library"));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -1480,11 +1488,11 @@ out_resync:
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream,
|
||||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer, gboolean at_eos)
|
GstBuffer * buffer, gboolean at_eos)
|
||||||
{
|
{
|
||||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
|
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;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
GstBuffer *pending_header_data = NULL;
|
GstBuffer *pending_header_data = NULL;
|
||||||
|
|
||||||
|
@ -1596,8 +1604,7 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
|
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
@ -1626,7 +1633,7 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret =
|
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, TRUE);
|
||||||
hls_stream->pending_decrypted_buffer = NULL;
|
hls_stream->pending_decrypted_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1636,14 +1643,14 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
GstBuffer *buf = hls_stream->pending_typefind_buffer;
|
GstBuffer *buf = hls_stream->pending_typefind_buffer;
|
||||||
hls_stream->pending_typefind_buffer = NULL;
|
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) {
|
if (hls_stream->pending_segment_data) {
|
||||||
GstBuffer *buf = hls_stream->pending_segment_data;
|
GstBuffer *buf = hls_stream->pending_segment_data;
|
||||||
hls_stream->pending_segment_data = NULL;
|
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
|
* 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_time as-is on the stream current position */
|
||||||
stream->current_position = hls_stream->current_segment->stream_time;
|
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);
|
hls_stream->current_segment->duration);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
|
||||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
|
GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (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);
|
||||||
|
|
||||||
if (hls_stream->current_segment == NULL)
|
if (hls_stream->current_segment == NULL)
|
||||||
return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
|
return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
|
||||||
|
@ -1707,7 +1714,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
decrypted_buffer =
|
decrypted_buffer =
|
||||||
gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
|
gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
|
||||||
if (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));
|
("decryption failed %s", err->message));
|
||||||
g_error_free (err);
|
g_error_free (err);
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
|
@ -1720,7 +1727,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
return GST_FLOW_OK;
|
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
|
static void
|
||||||
|
@ -1794,7 +1801,7 @@ gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
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);
|
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
|
||||||
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
|
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
|
||||||
|
@ -2330,7 +2337,7 @@ gst_hls_demux_stream_update_variant_playlist (GstHLSDemux * demux,
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream)
|
gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
|
||||||
{
|
{
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
|
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
|
||||||
|
@ -2456,14 +2463,13 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
|
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
|
||||||
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
|
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
|
||||||
GList *tmp;
|
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);
|
hlsdemux->mappings);
|
||||||
|
|
||||||
/* Variant streams can always start straight away */
|
/* Variant streams can always start straight away */
|
||||||
|
@ -2556,7 +2562,8 @@ gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
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);
|
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
|
||||||
GstHLSDemux *hlsdemux = GST_HLS_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
|
static GstClockTime
|
||||||
gst_hls_demux_get_presentation_offset (GstAdaptiveDemux * demux,
|
gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
|
||||||
GstAdaptiveDemux2Stream * stream)
|
|
||||||
{
|
{
|
||||||
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
|
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
|
||||||
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
|
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,
|
||||||
|
|
Loading…
Reference in a new issue