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:
Jan Schmidt 2022-08-21 04:31:53 +10:00
parent 9f89b8e3ef
commit 2fe641353d
8 changed files with 1139 additions and 1023 deletions

View file

@ -351,18 +351,12 @@ static void gst_dash_demux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_dash_demux_dispose (GObject * obj);
/* GstAdaptiveDemux */
static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux);
static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux);
static void gst_dash_demux_reset (GstAdaptiveDemux * ademux);
static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux,
GstBuffer * buf);
static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
/* GstAdaptiveDemuxStream */
static GstFlowReturn
gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream *
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
GstClockTimeDiff * final_ts);
static GstClockTime
gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
stream);
static gboolean gst_dash_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
* stream);
static GstFlowReturn
@ -371,25 +365,35 @@ static gboolean
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemux2Stream * stream);
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
stream, guint64 bitrate);
static GstClockTime
gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
stream);
static GstFlowReturn
gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer);
static gboolean gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream *
stream);
static GstFlowReturn
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream);
static gboolean
gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream);
/* GstAdaptiveDemux */
static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux);
static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux);
static void gst_dash_demux_reset (GstAdaptiveDemux * ademux);
static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux,
GstBuffer * buf);
static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream *
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
GstClockTimeDiff * final_ts);
static gint64 gst_dash_demux_get_manifest_update_interval (GstAdaptiveDemux *
demux);
static GstFlowReturn gst_dash_demux_update_manifest_data (GstAdaptiveDemux *
demux, GstBuffer * buf);
static GstClockTime
gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
stream);
static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux);
static GstFlowReturn gst_dash_demux_data_received (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
static gboolean
gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static GstFlowReturn
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static gboolean gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream *
stream);
/* GstDashDemux2 */
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux2 * demux);
@ -464,8 +468,32 @@ static void
gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
gobject_class->finalize = gst_dash_demux_stream_finalize;
adaptivedemux2stream_class->update_fragment_info =
gst_dash_demux_stream_update_fragment_info;
adaptivedemux2stream_class->has_next_fragment =
gst_dash_demux_stream_has_next_fragment;
adaptivedemux2stream_class->advance_fragment =
gst_dash_demux_stream_advance_fragment;
adaptivedemux2stream_class->get_fragment_waiting_time =
gst_dash_demux_stream_get_fragment_waiting_time;
adaptivedemux2stream_class->select_bitrate =
gst_dash_demux_stream_select_bitrate;
adaptivedemux2stream_class->get_presentation_offset =
gst_dash_demux_stream_get_presentation_offset;
adaptivedemux2stream_class->start_fragment =
gst_dash_demux_stream_fragment_start;
adaptivedemux2stream_class->finish_fragment =
gst_dash_demux_stream_fragment_finished;
adaptivedemux2stream_class->data_received =
gst_dash_demux_stream_data_received;
adaptivedemux2stream_class->need_another_chunk =
gst_dash_demux_stream_need_another_chunk;
}
@ -547,11 +575,10 @@ gst_dash_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
}
static GstClockTime
gst_dash_demux_get_presentation_offset (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
{
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
return gst_mpd_client2_get_stream_presentation_offset (dashdemux->client,
dashstream->index);
@ -628,30 +655,12 @@ gst_dash_demux2_class_init (GstDashDemux2Class * klass)
gstadaptivedemux_class->has_next_period = gst_dash_demux_has_next_period;
gstadaptivedemux_class->advance_period = gst_dash_demux_advance_period;
gstadaptivedemux_class->stream_has_next_fragment =
gst_dash_demux_stream_has_next_fragment;
gstadaptivedemux_class->stream_advance_fragment =
gst_dash_demux_stream_advance_fragment;
gstadaptivedemux_class->stream_get_fragment_waiting_time =
gst_dash_demux_stream_get_fragment_waiting_time;
gstadaptivedemux_class->stream_seek = gst_dash_demux_stream_seek;
gstadaptivedemux_class->stream_select_bitrate =
gst_dash_demux_stream_select_bitrate;
gstadaptivedemux_class->stream_update_fragment_info =
gst_dash_demux_stream_update_fragment_info;
gstadaptivedemux_class->get_live_seek_range =
gst_dash_demux_get_live_seek_range;
gstadaptivedemux_class->get_presentation_offset =
gst_dash_demux_get_presentation_offset;
gstadaptivedemux_class->get_period_start_time =
gst_dash_demux_get_period_start_time;
gstadaptivedemux_class->start_fragment = gst_dash_demux_stream_fragment_start;
gstadaptivedemux_class->finish_fragment =
gst_dash_demux_stream_fragment_finished;
gstadaptivedemux_class->data_received = gst_dash_demux_data_received;
gstadaptivedemux_class->need_another_chunk =
gst_dash_demux_need_another_chunk;
}
static void
@ -1827,10 +1836,10 @@ gst_dash_demux_stream_get_target_time (GstDashDemux2 * dashdemux,
GstClockTime deadline;
GstClockTime upstream_earliest_time;
GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
gdouble play_rate = gst_adaptive_demux_play_rate (stream->demux);
gdouble play_rate = gst_adaptive_demux_play_rate (demux);
GstClockTime period_start = gst_dash_demux_get_period_start_time (demux);
GstClockTime pts_offset =
gst_dash_demux_get_presentation_offset (demux, stream);
gst_dash_demux_stream_get_presentation_offset (stream);
g_assert (min_skip > 0);
@ -2706,10 +2715,9 @@ _gst_buffer_split (GstBuffer * buffer, gint offset, gsize size)
}
static gboolean
gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream * stream)
{
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
GST_LOG_OBJECT (stream, "Actual position %" GST_TIME_FORMAT,
@ -2725,7 +2733,7 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
* buffer. We need offsets to be consistent between moof and mdat
*/
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
&& GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux)
&& GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux)
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
stream->discont = TRUE;
@ -2733,11 +2741,10 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
}
static GstFlowReturn
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream)
{
GstClockTime consumed_duration;
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
/* We need to mark every first buffer of a key unit as discont,
@ -2747,7 +2754,7 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
* buffer. We need offsets to be consistent between moof and mdat
*/
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
&& GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux)
&& GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux)
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
stream->discont = TRUE;
@ -2774,20 +2781,20 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
consumed_duration =
(stream->fragment.stream_time + stream->fragment.duration) -
stream->current_position;
GST_LOG_OBJECT (demux, "Consumed duration after seeking: %"
GST_LOG_OBJECT (stream, "Consumed duration after seeking: %"
GST_TIMEP_FORMAT, &consumed_duration);
} else {
consumed_duration = stream->fragment.duration;
}
return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
return gst_adaptive_demux2_stream_advance_fragment (stream,
consumed_duration);
}
static gboolean
gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream * stream)
gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream)
{
GstDashDemux2 *dashdemux = (GstDashDemux2 *) stream->demux;
GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstAdaptiveDemux *demux = stream->demux;
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
gboolean playing_forward = (demux->segment.rate > 0.0);
@ -3371,9 +3378,9 @@ gst_dash_demux_find_sync_samples (GstAdaptiveDemux * demux,
static GstFlowReturn
gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_dash_demux_stream_handle_isobmff (GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemux *demux = stream->demux;
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *buffer;
@ -3527,22 +3534,22 @@ gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
if (sidx_advance) {
ret =
gst_adaptive_demux2_stream_advance_fragment (demux, stream,
gst_adaptive_demux2_stream_advance_fragment (stream,
SIDX_CURRENT_ENTRY (dash_stream)->duration);
if (ret != GST_FLOW_OK)
return ret;
/* If we still have data available, recurse and use it up if possible */
if (gst_adapter_available (dash_stream->adapter) > 0)
return gst_dash_demux_handle_isobmff (demux, stream);
return gst_dash_demux_stream_handle_isobmff (stream);
}
return ret;
}
static GstFlowReturn
gst_dash_demux_data_received (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer)
{
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
GstFlowReturn ret = GST_FLOW_OK;
@ -3574,7 +3581,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
if (dash_stream->is_isobmff || stream->downloading_index) {
/* SIDX index is also ISOBMMF */
ret = gst_dash_demux_handle_isobmff (demux, stream);
ret = gst_dash_demux_stream_handle_isobmff (stream);
} else if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
gsize available;
@ -3622,7 +3629,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
if (has_next) {
GstFlowReturn new_ret;
new_ret =
gst_adaptive_demux2_stream_advance_fragment (demux, stream,
gst_adaptive_demux2_stream_advance_fragment (stream,
SIDX_CURRENT_ENTRY (dash_stream)->duration);
/* only overwrite if it was OK before */

View file

@ -25,9 +25,16 @@
#ifndef _GST_ADAPTIVE_DEMUX_PRIVATE_H_
#define _GST_ADAPTIVE_DEMUX_PRIVATE_H_
#include <gst/gst.h>
#include <gst/base/gstadapter.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 MAX_DOWNLOAD_ERROR_COUNT 3
@ -166,10 +173,8 @@ GstFlowReturn gst_adaptive_demux_update_manifest (GstAdaptiveDemux *demux);
void gst_adaptive_demux2_stream_wants_manifest_update (GstAdaptiveDemux * demux);
void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err);
GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux);
gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
@ -177,10 +182,7 @@ gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
void gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream);
void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream);
GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags,
GstClockTimeDiff ts, GstClockTimeDiff * final_ts);
@ -193,6 +195,7 @@ gboolean gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream
gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux);
void gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux);
GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
void gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream);
gboolean gst_adaptive_demux_handle_lost_sync (GstAdaptiveDemux * demux);
@ -240,4 +243,6 @@ GstFlowReturn gst_adaptive_demux_period_combine_stream_flows (GstAdap
gboolean gst_adaptive_demux_period_has_pending_tracks (GstAdaptiveDemuxPeriod * period);
void gst_adaptive_demux_period_check_input_wakeup_locked (GstAdaptiveDemuxPeriod * period, GstClockTimeDiff current_output_position);
G_END_DECLS
#endif

View file

@ -27,7 +27,7 @@
#include "config.h"
#endif
#include "gstadaptivedemux.h"
#include "gstadaptivedemux-stream.h"
#include "gstadaptivedemux-private.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_error (GstAdaptiveDemux2Stream * stream);
static GstFlowReturn
gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream *
stream, GstBuffer * buffer);
static GstFlowReturn
gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream *
stream);
guint64
gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
stream);
static void gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream
* stream);
#define gst_adaptive_demux2_stream_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE (GstAdaptiveDemux2Stream, gst_adaptive_demux2_stream,
@ -49,6 +61,9 @@ gst_adaptive_demux2_stream_class_init (GstAdaptiveDemux2StreamClass * klass)
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gst_adaptive_demux2_stream_finalize;
klass->data_received = gst_adaptive_demux2_stream_data_received_default;
klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default;
}
static GType tsdemux_type = 0;
@ -255,10 +270,10 @@ schedule_another_chunk (GstAdaptiveDemux2Stream * stream)
}
static void
drain_inactive_tracks (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
drain_inactive_tracks (GstAdaptiveDemux2Stream * stream)
{
GList *iter;
GstAdaptiveDemux *demux = stream->demux;
TRACKS_LOCK (demux);
for (iter = stream->tracks; iter; iter = iter->next) {
@ -278,8 +293,8 @@ static void
gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
stream, GstFlowReturn ret, GError * err)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
GstAdaptiveDemux *demux = stream->demux;
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GST_DEBUG_OBJECT (stream,
"%s download finish: %d %s - err: %p", uritype (stream), ret,
@ -313,12 +328,12 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) {
/* We lost sync, seek back to live and return */
GST_WARNING_OBJECT (stream, "Lost sync when downloading");
gst_adaptive_demux_handle_lost_sync (demux);
gst_adaptive_demux_handle_lost_sync (stream->demux);
return;
} else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) {
/* The sub-class wants to stop the fragment immediately */
stream->fragment.finished = TRUE;
ret = klass->finish_fragment (demux, stream);
ret = klass->finish_fragment (stream);
GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
gst_flow_get_name (ret));
@ -331,7 +346,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|| !klass->need_another_chunk (stream)
|| stream->fragment.chunk_size == 0) {
stream->fragment.finished = TRUE;
ret = klass->finish_fragment (stream->demux, stream);
ret = klass->finish_fragment (stream);
GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
gst_flow_get_name (ret));
@ -343,7 +358,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
/* For HLS, we might be enqueueing data into tracks that aren't
* selected. Drain those ones out */
drain_inactive_tracks (stream->demux, stream);
drain_inactive_tracks (stream);
/* Now that we've called finish_fragment we can clear these flags the
* sub-class might have checked */
@ -387,7 +402,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
GST_LOG_OBJECT (stream, "Scheduling next_download() call");
stream->pending_cb_id =
gst_adaptive_demux_loop_call (demux->priv->scheduler_task,
gst_adaptive_demux_loop_call (stream->demux->priv->scheduler_task,
(GSourceFunc) gst_adaptive_demux2_stream_next_download,
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
}
@ -414,13 +429,16 @@ gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream * stream,
}
static void
gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, gboolean first_and_live)
gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux2Stream * stream,
gboolean first_and_live)
{
GstAdaptiveDemux *demux = stream->demux;
GstClockTime period_start = gst_adaptive_demux_get_period_start_time (demux);
GstClockTime offset =
gst_adaptive_demux2_stream_get_presentation_offset (demux, stream);
gst_adaptive_demux2_stream_get_presentation_offset (stream);
/* FIXME: Add a helper function to retrieve the demuxer segment
* using the SEGMENT_LOCK */
stream->parse_segment = demux->segment;
/* The demuxer segment is just built from seek events, but for each stream
@ -478,8 +496,9 @@ gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux,
* the segment time and base as calculated by the second case would be
* equivalent.
*/
GST_DEBUG_OBJECT (demux, "Using demux segment %" GST_SEGMENT_FORMAT,
&demux->segment);
GST_DEBUG_OBJECT (stream, "Using demux segment %" GST_SEGMENT_FORMAT,
&stream->parse_segment);
GST_DEBUG_OBJECT (demux,
"period_start: %" GST_TIME_FORMAT " offset: %" GST_TIME_FORMAT,
GST_TIME_ARGS (period_start), GST_TIME_ARGS (offset));
@ -542,7 +561,7 @@ update_buffer_pts_and_demux_position_locked (GstAdaptiveDemux * demux,
if (GST_CLOCK_STIME_IS_VALID (pos)) {
GstClockTime offset =
gst_adaptive_demux2_stream_get_presentation_offset (demux, stream);
gst_adaptive_demux2_stream_get_presentation_offset (stream);
pos += offset;
@ -574,8 +593,7 @@ gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
GList *pending_events = NULL;
if (stream->compute_segment) {
gst_adaptive_demux2_stream_prepare_segment (demux, stream,
stream->first_and_live);
gst_adaptive_demux2_stream_prepare_segment (stream, stream->first_and_live);
stream->compute_segment = FALSE;
stream->first_and_live = FALSE;
}
@ -711,7 +729,8 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer)
{
GstAdaptiveDemux *demux = stream->demux;
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GstFlowReturn ret = GST_FLOW_OK;
/* do not make any changes if the stream is cancelled */
@ -728,7 +747,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
* including the *actual* fragment ! */
if (stream->starting_fragment) {
stream->starting_fragment = FALSE;
if (klass->start_fragment != NULL && !klass->start_fragment (demux, stream))
if (klass->start_fragment != NULL && !klass->start_fragment (stream))
return GST_FLOW_ERROR;
}
@ -738,7 +757,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
"Received %s buffer of size %" G_GSIZE_FORMAT, uritype (stream),
gst_buffer_get_size (buffer));
ret = klass->data_received (demux, stream, buffer);
ret = klass->data_received (stream, buffer);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (stream, "data_received returned %s",
@ -759,7 +778,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
GST_DEBUG_OBJECT (stream, "Pushing EOS to parser");
/* TODO push this on all pads */
gst_event_set_seqnum (eos, stream->demux->priv->segment_seqnum);
gst_event_set_seqnum (eos, demux->priv->segment_seqnum);
gst_pad_send_event (stream->parsebin_sink, eos);
ret = GST_FLOW_ERROR;
@ -1177,7 +1196,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|| last_status_code / 100 == 5)) {
/* 4xx/5xx */
/* if current position is before available start, switch to next */
if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream))
if (!gst_adaptive_demux2_stream_has_next_fragment (stream))
goto flushing;
if (live) {
@ -1195,7 +1214,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream, "update_fragment_info ret: %s",
gst_flow_get_name (ret));
@ -1205,8 +1224,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
} else if (demux->segment.position > range_stop) {
/* wait a bit to be in range, we don't have any locks at that point */
GstClockTime wait_time =
gst_adaptive_demux2_stream_get_fragment_waiting_time (demux,
stream);
gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
if (wait_time > 0) {
GST_DEBUG_OBJECT (stream,
"Download waiting for %" GST_TIME_FORMAT,
@ -1230,7 +1248,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
gst_adaptive_demux2_stream_handle_playlist_eos (stream);
return;
}
} else if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) {
} else if (!gst_adaptive_demux2_stream_has_next_fragment (stream)) {
/* If this is the last fragment, consider failures EOS and not actual
* errors. Due to rounding errors in the durations, the last fragment
* might not actually exist */
@ -1421,7 +1439,8 @@ static GstFlowReturn
gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemux *demux = stream->demux;
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
gchar *url = NULL;
/* FIXME : */
@ -1760,7 +1779,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE:
/* Get information about the fragment to download */
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream,
"Fragment info update result: %d %s", ret, gst_flow_get_name (ret));
@ -1795,7 +1814,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
/* wait for live fragments to be available */
if (live) {
GstClockTime wait_time =
gst_adaptive_demux2_stream_get_fragment_waiting_time (demux, stream);
gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
if (wait_time > 0) {
GST_DEBUG_OBJECT (stream,
"Download waiting for %" GST_TIME_FORMAT,
@ -1967,12 +1986,12 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream)
static gboolean
gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemux *demux = stream->demux;
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
if (!klass->stream_can_start)
if (!klass->can_start)
return TRUE;
return klass->stream_can_start (demux, stream);
return klass->can_start (stream);
}
/**
@ -2096,3 +2115,492 @@ gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream * stream)
return ret;
}
/* Called from the scheduler task */
GstClockTime
gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
stream)
{
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
if (klass->get_presentation_offset == NULL)
return 0;
return klass->get_presentation_offset (stream);
}
GstFlowReturn
gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream *
stream)
{
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GstFlowReturn ret;
g_return_val_if_fail (klass->update_fragment_info != NULL, GST_FLOW_ERROR);
/* Make sure the sub-class will update bitrate, or else
* we will later */
stream->fragment.finished = FALSE;
GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->current_position));
ret = klass->update_fragment_info (stream);
GST_LOG_OBJECT (stream, "ret:%s uri:%s",
gst_flow_get_name (ret), stream->fragment.uri);
if (ret == GST_FLOW_OK) {
GST_LOG_OBJECT (stream,
"stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
GST_STIME_ARGS (stream->fragment.stream_time),
GST_TIME_ARGS (stream->fragment.duration));
GST_LOG_OBJECT (stream,
"range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT,
stream->fragment.range_start, stream->fragment.range_end);
}
return ret;
}
static GstFlowReturn
gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream *
stream, GstBuffer * buffer)
{
return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
}
static GstFlowReturn
gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream *
stream)
{
/* No need to advance, this isn't a real fragment */
if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
return GST_FLOW_OK;
return gst_adaptive_demux2_stream_advance_fragment (stream,
stream->fragment.duration);
}
/* must be called from the scheduler */
gboolean
gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
gboolean ret = TRUE;
if (klass->has_next_fragment)
ret = klass->has_next_fragment (stream);
return ret;
}
static gboolean
gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate)
{
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
if (klass->select_bitrate)
return klass->select_bitrate (stream, bitrate);
return FALSE;
}
GstClockTime
gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
stream)
{
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
if (klass->get_fragment_waiting_time)
return klass->get_fragment_waiting_time (stream);
return 0;
}
/* must be called from the scheduler */
/* Called from: the ::finish_fragment() handlers when an *actual* fragment is
* done
*
* @duration: Is the duration of the advancement starting from
* stream->current_position which might not be the fragment duration after a
* seek.
*/
GstFlowReturn
gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stream * stream,
GstClockTime duration)
{
if (stream->last_ret != GST_FLOW_OK)
return stream->last_ret;
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GstAdaptiveDemux *demux = stream->demux;
GstFlowReturn ret = GST_FLOW_OK;
g_assert (klass->advance_fragment != NULL);
GST_LOG_OBJECT (stream,
"stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration));
stream->download_error_count = 0;
g_clear_error (&stream->last_error);
#if 0
/* FIXME - url has no indication of byte ranges for subsegments */
/* FIXME: Reenable statistics sending? */
gst_element_post_message (GST_ELEMENT_CAST (demux),
gst_message_new_element (GST_OBJECT_CAST (demux),
gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
"manifest-uri", G_TYPE_STRING,
demux->manifest_uri, "uri", G_TYPE_STRING,
stream->fragment.uri, "fragment-start-time",
GST_TYPE_CLOCK_TIME, stream->download_start_time,
"fragment-stop-time", GST_TYPE_CLOCK_TIME,
gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64,
stream->download_total_bytes, "fragment-download-time",
GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL)));
#endif
/* Don't update to the end of the segment if in reverse playback */
GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux);
if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) {
stream->parse_segment.position += duration;
stream->current_position += duration;
GST_DEBUG_OBJECT (stream,
"stream position now %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->current_position));
}
GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
/* When advancing with a non 1.0 rate on live streams, we need to check
* the live seeking range again to make sure we can still advance to
* that position */
if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) {
if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream))
ret = GST_FLOW_EOS;
else
ret = klass->advance_fragment (stream);
} else if (gst_adaptive_demux_is_live (demux)
|| gst_adaptive_demux2_stream_has_next_fragment (stream)) {
ret = klass->advance_fragment (stream);
} else {
ret = GST_FLOW_EOS;
}
stream->download_start_time =
GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux));
/* Always check if we need to switch bitrate on OK, or when live
* (it's normal to have EOS on advancing in live when we hit the
* end of the manifest) */
if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) {
GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change");
if (gst_adaptive_demux2_stream_select_bitrate (demux, stream,
gst_adaptive_demux2_stream_update_current_bitrate (stream))) {
GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH");
stream->need_header = TRUE;
ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH;
}
}
stream->last_ret = ret;
return stream->last_ret;
}
/* TRACKS_LOCK held */
static GstAdaptiveDemuxTrack *
gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream,
GstStreamType stream_type)
{
GList *iter;
for (iter = stream->tracks; iter; iter = iter->next) {
GstAdaptiveDemuxTrack *track = iter->data;
if (track->type == stream_type)
return track;
}
return NULL;
}
/* TRACKS lock held */
static void
gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream * stream)
{
guint i;
GST_DEBUG_OBJECT (stream, "Updating track information from collection");
for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
i++) {
GstStream *gst_stream =
gst_stream_collection_get_stream (stream->stream_collection, i);
GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
GstAdaptiveDemuxTrack *track;
if (stream_type == GST_STREAM_TYPE_UNKNOWN)
continue;
track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type);
if (!track) {
GST_DEBUG_OBJECT (stream,
"We don't have an existing track to handle stream %" GST_PTR_FORMAT,
gst_stream);
continue;
}
if (track->upstream_stream_id)
g_free (track->upstream_stream_id);
track->upstream_stream_id =
g_strdup (gst_stream_get_stream_id (gst_stream));
}
}
static gboolean
tags_have_language_info (GstTagList * tags)
{
const gchar *language = NULL;
if (tags == NULL)
return FALSE;
if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0,
&language))
return TRUE;
if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0,
&language))
return TRUE;
return FALSE;
}
static gboolean
can_handle_collection (GstAdaptiveDemux2Stream * stream,
GstStreamCollection * collection)
{
guint i;
guint nb_audio, nb_video, nb_text;
gboolean have_audio_languages = TRUE;
gboolean have_text_languages = TRUE;
nb_audio = nb_video = nb_text = 0;
for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
GstStream *gst_stream = gst_stream_collection_get_stream (collection, i);
GstTagList *tags = gst_stream_get_tags (gst_stream);
GST_DEBUG_OBJECT (stream,
"Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream);
switch (gst_stream_get_stream_type (gst_stream)) {
case GST_STREAM_TYPE_AUDIO:
have_audio_languages &= tags_have_language_info (tags);
nb_audio++;
break;
case GST_STREAM_TYPE_VIDEO:
nb_video++;
break;
case GST_STREAM_TYPE_TEXT:
have_text_languages &= tags_have_language_info (tags);
nb_text++;
break;
default:
break;
}
}
/* Check that we either have at most 1 of each track type, or that
* we have language tags for each to tell which is which */
if (nb_video > 1 ||
(nb_audio > 1 && !have_audio_languages) ||
(nb_text > 1 && !have_text_languages)) {
GST_WARNING
("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)",
nb_audio, nb_video, nb_text);
return FALSE;
}
return TRUE;
}
/* Called from the demuxer when it receives a GstStreamCollection on the bus
* for this stream. */
/* TRACKS lock held */
gboolean
gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream * stream,
GstStreamCollection * collection, gboolean * had_pending_tracks)
{
g_assert (had_pending_tracks != NULL);
/* Check whether the collection is "sane" or not.
*
* In the context of adaptive streaming, we can only handle multiplexed
* content where the output sub-streams can be matched reliably to the various
* tracks. That is, only a single stream of each type, or if there are
* multiple audio/subtitle tracks, they can be differentiated by language
* (and possibly in the future by codec).
*/
if (!can_handle_collection (stream, collection)) {
return FALSE;
}
/* Store the collection on the stream */
gst_object_replace ((GstObject **) & stream->stream_collection,
(GstObject *) collection);
/* If stream is marked as having pending_tracks, ask the subclass to
* handle that and create the tracks now */
if (stream->pending_tracks) {
GstAdaptiveDemux2StreamClass *klass =
GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
g_assert (klass->create_tracks);
klass->create_tracks (stream);
stream->pending_tracks = FALSE;
*had_pending_tracks = TRUE;
} else {
g_assert (stream->tracks);
/* Now we should have assigned tracks, match them to the
* collection and update the pending upstream stream_id
* for each of them based on the collection information. */
gst_adaptive_demux2_stream_update_track_ids (stream);
}
return TRUE;
}
static guint64
_update_average_bitrate (GstAdaptiveDemux2Stream * stream, guint64 new_bitrate)
{
gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS;
stream->moving_bitrate -= stream->fragment_bitrates[index];
stream->fragment_bitrates[index] = new_bitrate;
stream->moving_bitrate += new_bitrate;
stream->moving_index += 1;
if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS)
return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS;
return stream->moving_bitrate / stream->moving_index;
}
guint64
gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
stream)
{
guint64 average_bitrate;
guint64 fragment_bitrate;
guint connection_speed, min_bitrate, max_bitrate, target_download_rate;
fragment_bitrate = stream->last_bitrate;
GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps",
fragment_bitrate);
average_bitrate = _update_average_bitrate (stream, fragment_bitrate);
GST_INFO_OBJECT (stream,
"last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate);
GST_INFO_OBJECT (stream,
"Last %u fragments average bitrate is %" G_GUINT64_FORMAT,
NUM_LOOKBACK_FRAGMENTS, average_bitrate);
/* Conservative approach, make sure we don't upgrade too fast */
stream->current_download_rate = MIN (average_bitrate, fragment_bitrate);
/* For the video stream, update the demuxer reported download
* rate. FIXME: Move all bandwidth estimation to the
* download helper and make it the demuxer's responsibility
* to select the right set of things to download within
* that bandwidth */
GstAdaptiveDemux *demux = stream->demux;
GST_OBJECT_LOCK (demux);
/* If this is stream containing our video, update the overall demuxer
* reported bitrate and notify, to give the application a
* chance to choose a new connection-bitrate */
if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) {
demux->current_download_rate = stream->current_download_rate;
GST_OBJECT_UNLOCK (demux);
g_object_notify (G_OBJECT (demux), "current-bandwidth");
GST_OBJECT_LOCK (demux);
}
connection_speed = demux->connection_speed;
min_bitrate = demux->min_bitrate;
max_bitrate = demux->max_bitrate;
GST_OBJECT_UNLOCK (demux);
if (connection_speed) {
GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it",
connection_speed / 1000);
return connection_speed;
}
/* No explicit connection_speed, so choose the new variant to use as a
* fraction of the measured download rate */
target_download_rate =
CLAMP (stream->current_download_rate, 0,
G_MAXUINT) * demux->bandwidth_target_ratio;
GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u",
demux->bandwidth_target_ratio, target_download_rate);
#if 0
/* Debugging code, modulate the bitrate every few fragments */
{
static guint ctr = 0;
if (ctr % 3 == 0) {
GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging");
target_download_rate /= 2;
}
ctr++;
}
#endif
if (min_bitrate > 0 && target_download_rate < min_bitrate) {
target_download_rate = min_bitrate;
GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s",
min_bitrate);
}
if (max_bitrate > 0 && target_download_rate > max_bitrate) {
target_download_rate = max_bitrate;
GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s",
max_bitrate);
}
GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps",
target_download_rate);
return target_download_rate;
}
void
gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f)
{
g_free (f->uri);
f->uri = NULL;
f->range_start = 0;
f->range_end = -1;
g_free (f->header_uri);
f->header_uri = NULL;
f->header_range_start = 0;
f->header_range_end = -1;
g_free (f->index_uri);
f->index_uri = NULL;
f->index_range_start = 0;
f->index_range_end = -1;
f->stream_time = GST_CLOCK_STIME_NONE;
f->duration = GST_CLOCK_TIME_NONE;
f->finished = FALSE;
}

View file

@ -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

View file

@ -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

View file

@ -229,8 +229,6 @@ static void gst_adaptive_demux_reset (GstAdaptiveDemux * demux);
static gboolean gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
gboolean first_and_live);
static gboolean gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate);
static GstFlowReturn
gst_adaptive_demux_update_manifest_default (GstAdaptiveDemux * demux);
@ -242,19 +240,6 @@ static void gst_adaptive_demux_start_manifest_update_task (GstAdaptiveDemux *
static void gst_adaptive_demux_start_tasks (GstAdaptiveDemux * demux);
static void gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux,
gboolean stop_updates);
static GstFlowReturn
gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
static GstFlowReturn
gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static GstFlowReturn
gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstClockTime duration);
static void
gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static gboolean
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
@ -532,19 +517,15 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
gst_element_class_add_static_pad_template (gstelement_class,
&gst_adaptive_demux_subtitlesrc_template);
gstelement_class->change_state = gst_adaptive_demux_change_state;
gstelement_class->query = gst_adaptive_demux_query;
gstelement_class->send_event = gst_adaptive_demux_send_event;
gstbin_class->handle_message = gst_adaptive_demux_handle_message;
klass->data_received = gst_adaptive_demux2_stream_data_received_default;
klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default;
klass->update_manifest = gst_adaptive_demux_update_manifest_default;
klass->requires_periodical_playlist_update =
gst_adaptive_demux_requires_periodical_playlist_update_default;
klass->stream_update_tracks = gst_adaptive_demux2_stream_update_tracks;
gst_type_mark_as_plugin_api (GST_TYPE_ADAPTIVE_DEMUX, 0);
}
@ -1468,123 +1449,6 @@ find_stream_for_element_locked (GstAdaptiveDemux * demux, GstObject * o)
return NULL;
}
/* TRACKS_LOCK held */
static GstAdaptiveDemuxTrack *
gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream,
GstStreamType stream_type)
{
GList *iter;
for (iter = stream->tracks; iter; iter = iter->next) {
GstAdaptiveDemuxTrack *track = iter->data;
if (track->type == stream_type)
return track;
}
return NULL;
}
/* MANIFEST and TRACKS lock held */
static void
gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
guint i;
GST_DEBUG_OBJECT (stream, "Updating track information from collection");
for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
i++) {
GstStream *gst_stream =
gst_stream_collection_get_stream (stream->stream_collection, i);
GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
GstAdaptiveDemuxTrack *track;
if (stream_type == GST_STREAM_TYPE_UNKNOWN)
continue;
track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type);
if (!track) {
GST_DEBUG_OBJECT (stream,
"We don't have an existing track to handle stream %" GST_PTR_FORMAT,
gst_stream);
continue;
}
if (track->upstream_stream_id)
g_free (track->upstream_stream_id);
track->upstream_stream_id =
g_strdup (gst_stream_get_stream_id (gst_stream));
}
}
static gboolean
tags_have_language_info (GstTagList * tags)
{
const gchar *language = NULL;
if (tags == NULL)
return FALSE;
if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0,
&language))
return TRUE;
if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0,
&language))
return TRUE;
return FALSE;
}
static gboolean
can_handle_collection (GstAdaptiveDemux2Stream * stream,
GstStreamCollection * collection)
{
guint i;
guint nb_audio, nb_video, nb_text;
gboolean have_audio_languages = TRUE;
gboolean have_text_languages = TRUE;
nb_audio = nb_video = nb_text = 0;
for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
GstStream *gst_stream = gst_stream_collection_get_stream (collection, i);
GstTagList *tags = gst_stream_get_tags (gst_stream);
GST_DEBUG_OBJECT (stream,
"Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream);
switch (gst_stream_get_stream_type (gst_stream)) {
case GST_STREAM_TYPE_AUDIO:
have_audio_languages &= tags_have_language_info (tags);
nb_audio++;
break;
case GST_STREAM_TYPE_VIDEO:
nb_video++;
break;
case GST_STREAM_TYPE_TEXT:
have_text_languages &= tags_have_language_info (tags);
nb_text++;
break;
default:
break;
}
}
/* Check that we either have at most 1 of each track type, or that
* we have language tags for each to tell which is which */
if (nb_video > 1 ||
(nb_audio > 1 && !have_audio_languages) ||
(nb_text > 1 && !have_text_languages)) {
GST_WARNING
("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)",
nb_audio, nb_video, nb_text);
return FALSE;
}
return TRUE;
}
static void
gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
GstMessage * msg)
@ -1606,60 +1470,38 @@ gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
if (!collection)
goto beach;
/* Check whether the collection is "sane" or not.
*
* In the context of adaptive streaming, we can only handle multiplexed
* content that provides at most one stream of valid types (audio, video,
* text). Without this we cannot reliably match the output of this multiplex
* to the various tracks.
*
* FIXME : In the future and *IF* we encounter such streams, we could envision
* supporting multiple streams of the same type if, and only if, they have
* tags that allow differentiating them (ex: languages).
*/
if (!can_handle_collection (stream, collection)) {
TRACKS_LOCK (demux);
if (!gst_adaptive_demux2_stream_handle_collection (stream, collection,
&pending_tracks_activated)) {
TRACKS_UNLOCK (demux);
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
(_("Stream format can't be handled")),
("The streams provided by the multiplex are ambiguous"));
goto beach;
}
/* Store the collection on the stream */
gst_object_replace ((GstObject **) & stream->stream_collection,
(GstObject *) collection);
/* IF there are pending tracks, ask the subclass to handle that */
if (stream->pending_tracks) {
GstAdaptiveDemuxClass *demux_class = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
g_assert (demux_class->stream_update_tracks);
demux_class->stream_update_tracks (demux, stream);
TRACKS_LOCK (demux);
stream->pending_tracks = FALSE;
pending_tracks_activated = TRUE;
if (pending_tracks_activated) {
/* If pending tracks were handled, then update the demuxer collection */
if (gst_adaptive_demux_update_collection (demux, demux->input_period) &&
demux->input_period == demux->output_period)
demux->input_period == demux->output_period) {
gst_adaptive_demux_post_collection (demux);
} else {
g_assert (stream->tracks);
TRACKS_LOCK (demux);
/* If we already have assigned tracks, update the pending upstream stream_id
* for each of them based on the collection information. */
gst_adaptive_demux2_stream_update_tracks (demux, stream);
}
}
/* If we discovered pending tracks and we no longer have any, we can ensure
* selected tracks are started */
if (pending_tracks_activated
&& !gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
GList *iter = demux->input_period->streams;
for (; iter; iter = iter->next) {
GstAdaptiveDemux2Stream *new_stream = iter->data;
/* If we discovered pending tracks and we no longer have any, we can ensure
* selected tracks are started */
if (!gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
GList *iter = demux->input_period->streams;
for (; iter; iter = iter->next) {
GstAdaptiveDemux2Stream *new_stream = iter->data;
/* The stream that posted this collection was already started. If a
* different stream is now selected, start it */
if (stream != new_stream
&& gst_adaptive_demux2_stream_is_selected_locked (new_stream))
gst_adaptive_demux2_stream_start (new_stream);
/* The stream that posted this collection was already started. If a
* different stream is now selected, start it */
if (stream != new_stream
&& gst_adaptive_demux2_stream_is_selected_locked (new_stream))
gst_adaptive_demux2_stream_start (new_stream);
}
}
}
TRACKS_UNLOCK (demux);
@ -1741,21 +1583,6 @@ gst_adaptive_demux_handle_message (GstBin * bin, GstMessage * msg)
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
}
/* must be called with manifest_lock taken */
GstClockTime
gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemuxClass *klass;
klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
if (klass->get_presentation_offset == NULL)
return 0;
return klass->get_presentation_offset (demux, stream);
}
/* must be called with manifest_lock taken */
GstClockTime
gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux)
@ -1813,7 +1640,7 @@ gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
/* TODO we only need the first timestamp, maybe create a simple function to
* get the current PTS of a fragment ? */
GST_DEBUG_OBJECT (stream, "Calling update_fragment_info");
gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream,
"Got stream time %" GST_STIME_FORMAT,
@ -3008,130 +2835,6 @@ gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
stream->pending_events = g_list_append (stream->pending_events, event);
}
static guint64
_update_average_bitrate (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, guint64 new_bitrate)
{
gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS;
stream->moving_bitrate -= stream->fragment_bitrates[index];
stream->fragment_bitrates[index] = new_bitrate;
stream->moving_bitrate += new_bitrate;
stream->moving_index += 1;
if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS)
return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS;
return stream->moving_bitrate / stream->moving_index;
}
static guint64
gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
guint64 average_bitrate;
guint64 fragment_bitrate;
guint connection_speed, min_bitrate, max_bitrate, target_download_rate;
fragment_bitrate = stream->last_bitrate;
GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps",
fragment_bitrate);
average_bitrate = _update_average_bitrate (demux, stream, fragment_bitrate);
GST_INFO_OBJECT (stream,
"last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate);
GST_INFO_OBJECT (stream,
"Last %u fragments average bitrate is %" G_GUINT64_FORMAT,
NUM_LOOKBACK_FRAGMENTS, average_bitrate);
/* Conservative approach, make sure we don't upgrade too fast */
GST_OBJECT_LOCK (demux);
stream->current_download_rate = MIN (average_bitrate, fragment_bitrate);
/* If this is the/a video stream update the overall demuxer
* reported bitrate and notify, to give the application a
* chance to choose a new connection-bitrate */
if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) {
demux->current_download_rate = stream->current_download_rate;
GST_OBJECT_UNLOCK (demux);
g_object_notify (G_OBJECT (demux), "current-bandwidth");
GST_OBJECT_LOCK (demux);
}
connection_speed = demux->connection_speed;
min_bitrate = demux->min_bitrate;
max_bitrate = demux->max_bitrate;
GST_OBJECT_UNLOCK (demux);
if (connection_speed) {
GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it",
connection_speed / 1000);
return connection_speed;
}
/* No explicit connection_speed, so choose the new variant to use as a
* fraction of the measured download rate */
target_download_rate =
CLAMP (stream->current_download_rate, 0,
G_MAXUINT) * demux->bandwidth_target_ratio;
GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u",
demux->bandwidth_target_ratio, target_download_rate);
#if 0
/* Debugging code, modulate the bitrate every few fragments */
{
static guint ctr = 0;
if (ctr % 3 == 0) {
GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging");
target_download_rate /= 2;
}
ctr++;
}
#endif
if (min_bitrate > 0 && target_download_rate < min_bitrate) {
target_download_rate = min_bitrate;
GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s",
min_bitrate);
}
if (max_bitrate > 0 && target_download_rate > max_bitrate) {
target_download_rate = max_bitrate;
GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s",
max_bitrate);
}
GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps",
target_download_rate);
return target_download_rate;
}
/* must be called with manifest_lock taken */
static GstFlowReturn
gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
/* No need to advance, this isn't a real fragment */
if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
return GST_FLOW_OK;
return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
stream->fragment.duration);
}
/* must be called with manifest_lock taken.
* Can temporarily release manifest_lock
*/
static GstFlowReturn
gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
{
return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
}
static gboolean
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
* demux)
@ -3877,181 +3580,6 @@ gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
return GST_FLOW_ERROR;
}
/* must be called from the scheduler */
gboolean
gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
gboolean ret = TRUE;
if (klass->stream_has_next_fragment)
ret = klass->stream_has_next_fragment (stream);
return ret;
}
/* must be called from the scheduler */
/* Called from: the ::finish_fragment() handlers when an *actual* fragment is
* done
*
* @duration: Is the duration of the advancement starting from
* stream->current_position which might not be the fragment duration after a
* seek.
*/
GstFlowReturn
gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstClockTime duration)
{
if (stream->last_ret != GST_FLOW_OK)
return stream->last_ret;
stream->last_ret =
gst_adaptive_demux2_stream_advance_fragment_unlocked (demux, stream,
duration);
return stream->last_ret;
}
/* must be called with manifest_lock taken */
static GstFlowReturn
gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstClockTime duration)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
GstFlowReturn ret;
g_return_val_if_fail (klass->stream_advance_fragment != NULL, GST_FLOW_ERROR);
GST_LOG_OBJECT (stream,
"stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration));
stream->download_error_count = 0;
g_clear_error (&stream->last_error);
#if 0
/* FIXME - url has no indication of byte ranges for subsegments */
/* FIXME: Reenable statistics sending? */
gst_element_post_message (GST_ELEMENT_CAST (demux),
gst_message_new_element (GST_OBJECT_CAST (demux),
gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
"manifest-uri", G_TYPE_STRING,
demux->manifest_uri, "uri", G_TYPE_STRING,
stream->fragment.uri, "fragment-start-time",
GST_TYPE_CLOCK_TIME, stream->download_start_time,
"fragment-stop-time", GST_TYPE_CLOCK_TIME,
gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64,
stream->download_total_bytes, "fragment-download-time",
GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL)));
#endif
/* Don't update to the end of the segment if in reverse playback */
GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux);
if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) {
stream->parse_segment.position += duration;
stream->current_position += duration;
GST_DEBUG_OBJECT (stream,
"stream position now %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->current_position));
}
GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
/* When advancing with a non 1.0 rate on live streams, we need to check
* the live seeking range again to make sure we can still advance to
* that position */
if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) {
if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream))
ret = GST_FLOW_EOS;
else
ret = klass->stream_advance_fragment (stream);
} else if (gst_adaptive_demux_is_live (demux)
|| gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) {
ret = klass->stream_advance_fragment (stream);
} else {
ret = GST_FLOW_EOS;
}
stream->download_start_time =
GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux));
/* Always check if we need to switch bitrate on OK, or when live
* (it's normal to have EOS on advancing in live when we hit the
* end of the manifest) */
if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) {
GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change");
if (gst_adaptive_demux2_stream_select_bitrate (demux, stream,
gst_adaptive_demux2_stream_update_current_bitrate (demux,
stream))) {
GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH");
stream->need_header = TRUE;
ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH;
}
}
return ret;
}
/* must be called with manifest_lock taken */
static gboolean
gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
if (klass->stream_select_bitrate)
return klass->stream_select_bitrate (stream, bitrate);
return FALSE;
}
/* must be called with manifest_lock taken */
GstFlowReturn
gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
GstFlowReturn ret;
g_return_val_if_fail (klass->stream_update_fragment_info != NULL,
GST_FLOW_ERROR);
/* Make sure the sub-class will update bitrate, or else
* we will later */
stream->fragment.finished = FALSE;
GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->current_position));
ret = klass->stream_update_fragment_info (stream);
GST_LOG_OBJECT (stream, "ret:%s uri:%s",
gst_flow_get_name (ret), stream->fragment.uri);
if (ret == GST_FLOW_OK) {
GST_LOG_OBJECT (stream,
"stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
GST_STIME_ARGS (stream->fragment.stream_time),
GST_TIME_ARGS (stream->fragment.duration));
GST_LOG_OBJECT (stream,
"range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT,
stream->fragment.range_start, stream->fragment.range_end);
}
return ret;
}
/* must be called with manifest_lock taken */
GstClockTime
gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
if (klass->stream_get_fragment_waiting_time)
return klass->stream_get_fragment_waiting_time (stream);
return 0;
}
static void
handle_manifest_download_complete (DownloadRequest * request,
DownloadRequestState state, GstAdaptiveDemux * demux)
@ -4160,29 +3688,6 @@ gst_adaptive_demux_update_manifest (GstAdaptiveDemux * demux)
return ret;
}
void
gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f)
{
g_free (f->uri);
f->uri = NULL;
f->range_start = 0;
f->range_end = -1;
g_free (f->header_uri);
f->header_uri = NULL;
f->header_range_start = 0;
f->header_range_end = -1;
g_free (f->index_uri);
f->index_uri = NULL;
f->index_range_start = 0;
f->index_range_end = -1;
f->stream_time = GST_CLOCK_STIME_NONE;
f->duration = GST_CLOCK_TIME_NONE;
f->finished = FALSE;
}
/* must be called with manifest_lock taken */
gboolean
gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)

View file

@ -34,32 +34,11 @@
#include "gstadaptivedemuxutils.h"
#include "gstadaptivedemux-types.h"
#include "gstadaptivedemux-stream.h"
G_BEGIN_DECLS
#define GST_TYPE_ADAPTIVE_DEMUX \
(gst_adaptive_demux_ng_get_type())
#define GST_ADAPTIVE_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux))
#define GST_ADAPTIVE_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
#define GST_IS_ADAPTIVE_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX))
#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX))
#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj)
#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \
(gst_adaptive_demux2_stream_get_type())
#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream))
#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj)
typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream;
typedef GstObjectClass GstAdaptiveDemux2StreamClass;
/**
* GST_ADAPTIVE_DEMUX_SINK_NAME:
*
@ -77,8 +56,6 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass;
#define GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS(obj) ((((GstAdaptiveDemux*)(obj))->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) == GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)
#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header)
/**
* GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME:
*
@ -101,48 +78,15 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass;
#define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1
/* Current fragment download should be aborted and restarted. The parent class
* will call ::update_fragment_info() again to get the updated information.
* will call ::update_fragment_info() on the stream again to get the updated information.
*/
#define GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_2
/* The live stream has lost synchronization and the demuxer needs to be resetted */
#define GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_2 + 1
typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState;
typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment;
typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack;
typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod;
typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass;
typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate;
struct _GstAdaptiveDemux2StreamFragment
{
/* The period-local stream time for the given fragment. */
GstClockTimeDiff stream_time;
GstClockTime duration;
gchar *uri;
gint64 range_start;
gint64 range_end;
/* when chunked downloading is used, may be be updated need_another_chunk() */
gint chunk_size;
/* when headers are needed */
gchar *header_uri;
gint64 header_range_start;
gint64 header_range_end;
/* when index is needed */
gchar *index_uri;
gint64 index_range_start;
gint64 index_range_end;
gboolean finished;
};
struct _GstAdaptiveDemuxTrack
{
gint ref_count;
@ -249,119 +193,6 @@ struct _GstAdaptiveDemuxTrack
gboolean output_discont;
};
enum _GstAdaptiveDemux2StreamState {
GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */
GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */
GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS,
GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED
};
struct _GstAdaptiveDemux2Stream
{
GstObject object;
/* FIXME : transition to gstobject->parent */
GstAdaptiveDemux *demux;
/* The period to which the stream belongs, set when adding the stream to the
* demuxer */
GstAdaptiveDemuxPeriod *period;
/* The tracks this stream targets */
GList *tracks;
/* The internal parsebin, forward data to track */
GstElement *parsebin;
GstPad *parsebin_sink;
gulong pad_added_id, pad_removed_id;
GstSegment parse_segment;
/* TRUE if the current stream GstSegment should be sent downstream */
gboolean send_segment;
/* TRUE if the stream GstSegment requires recalculation (from demuxer
segment) */
gboolean compute_segment;
/* first_and_live applies to compute_segment */
gboolean first_and_live;
/* When restarting, what is the target position (in demux segment) to
* begin at */
GstClockTime start_position;
/* Track the current position (in demux segment) of the current fragment */
GstClockTime current_position;
GstCaps *pending_caps;
GstTagList *pending_tags;
GList *pending_events;
GstFlowReturn last_ret;
GError *last_error;
gboolean discont;
/* download tooling */
gboolean need_header;
gboolean need_index;
gboolean downloading_header;
gboolean downloading_index;
/* persistent, reused download request for fragment data */
DownloadRequest *download_request;
GstAdaptiveDemux2StreamState state;
guint pending_cb_id;
gboolean download_active;
/* The (global output) time at which this stream should be woken
* to download more input */
GstClockTimeDiff next_input_wakeup_time;
guint last_status_code;
gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */
gboolean download_finished;
gboolean starting_fragment;
gboolean first_fragment_buffer;
gint64 download_start_time;
gint64 download_total_bytes;
gint64 download_end_offset;
guint64 current_download_rate;
/* bitrate of the previous fragment (pre-queue2) */
guint64 last_bitrate;
/* Total last download time, from request to completion */
GstClockTime last_download_time;
/* Average for the last fragments */
guint64 moving_bitrate;
guint moving_index;
guint64 *fragment_bitrates;
GstAdaptiveDemux2StreamFragment fragment;
guint download_error_count;
/* Last collection provided by parsebin */
GstStreamCollection *stream_collection;
/* OR'd set of stream types in this stream */
GstStreamType stream_type;
/* The buffering threshold recommended by the subclass */
GstClockTime recommended_buffering_threshold;
};
/**
* GstAdaptiveDemuxPeriod:
*
@ -574,116 +405,6 @@ struct _GstAdaptiveDemuxClass
GstSeekFlags flags,
GstClockTimeDiff target_ts,
GstClockTimeDiff * final_ts);
gboolean (*stream_has_next_fragment) (GstAdaptiveDemux2Stream * stream);
GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemux2Stream * stream);
/**
* stream_can_start:
* @demux: The #GstAdaptiveDemux
* @stream: a #GstAdaptiveDemux2Stream
*
* Called before starting a @stream. sub-classes can return %FALSE if more
* information is required before it can be started. Sub-classes will have to
* call gst_adaptive_demux2_stream_start() when the stream should be started.
*/
gboolean (*stream_can_start) (GstAdaptiveDemux *demux,
GstAdaptiveDemux2Stream *stream);
/**
* stream_update_tracks:
* @demux: The #GstAdaptiveDemux
* @stream: A #GstAdaptiveDemux2Stream
*
* Called whenever the base class collected a @collection on a @stream which has
* pending tracks to be created. Subclasses should override this if they
* create streams without tracks.
*
* * create the various tracks by analyzing the @stream stream_collection
* * Set the track upstream_stream_id to the corresponding stream_id from the collection
*/
void (*stream_update_tracks) (GstAdaptiveDemux *demux,
GstAdaptiveDemux2Stream *stream);
/**
* need_another_chunk:
* @stream: #GstAdaptiveDemux2Stream
*
* If chunked downloading is used (chunk_size != 0) this is called once a
* chunk is finished to decide whether more has to be downloaded or not.
* May update chunk_size to a different value
*/
gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream);
/**
* stream_update_fragment_info:
* @stream: #GstAdaptiveDemux2Stream
*
* Requests the stream to set the information about the current fragment to its
* current fragment struct
*
* Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS
* if there is no fragment.
*/
GstFlowReturn (*stream_update_fragment_info) (GstAdaptiveDemux2Stream * stream);
/**
* stream_select_bitrate:
* @stream: #GstAdaptiveDemux2Stream
* @bitrate: the bitrate to select (in bytes per second)
*
* The stream should try to select the bitrate that is the greater, but not
* greater than the requested bitrate. If it needs a codec change it should
* create the new stream using gst_adaptive_demux2_stream_new(). If it only
* needs a caps change it should set the new caps using
* gst_adaptive_demux2_stream_set_caps().
*
* Returns: %TRUE if the stream changed bitrate, %FALSE otherwise
*/
gboolean (*stream_select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate);
/**
* stream_get_fragment_waiting_time:
* @stream: #GstAdaptiveDemux2Stream
*
* For live streams, requests how much time should be waited before starting
* to download the fragment. This is useful to avoid downloading a fragment that
* isn't available yet.
*
* Returns: The waiting time in as a #GstClockTime
*/
GstClockTime (*stream_get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream);
/**
* start_fragment:
* @demux: #GstAdaptiveDemux
* @stream: #GstAdaptiveDemux2Stream
*
* Notifies the subclass that the given stream is starting the download
* of a new fragment. Can be used to reset/init internal state that is
* needed before each fragment, like decryption engines.
*
* Returns: %TRUE if successful.
*/
gboolean (*start_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
/**
* finish_fragment:
* @demux: #GstAdaptiveDemux
* @stream: #GstAdaptiveDemux2Stream
*
* Notifies the subclass that a fragment download was finished.
* It can be used to cleanup internal state after a fragment and
* also push any pending data before moving to the next fragment.
*/
GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
/**
* data_received:
* @demux: #GstAdaptiveDemux
* @stream: #GstAdaptiveDemux2Stream
* @buffer: #GstBuffer
*
* Notifies the subclass that a fragment chunk was downloaded. The subclass
* can look at the data and modify/push data as desired.
*
* Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
*/
GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
/**
* get_live_seek_range:
@ -697,18 +418,6 @@ struct _GstAdaptiveDemuxClass
*/
gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop);
/**
* get_presentation_offset:
* @demux: #GstAdaptiveDemux
* @stream: #GstAdaptiveDemux2Stream
*
* Gets the delay to apply to @stream.
*
* Return: a #GstClockTime representing the (positive) time offset to apply to
* @stream.
*/
GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream);
/**
* get_period_start_time:
* @demux: #GstAdaptiveDemux
@ -739,8 +448,6 @@ struct _GstAdaptiveDemuxClass
GType gst_adaptive_demux_ng_get_type (void);
GType gst_adaptive_demux2_stream_get_type (void);
gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux,
GstAdaptiveDemux2Stream *stream);
@ -757,34 +464,9 @@ GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *trac
void gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track);
void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream,
GstCaps * caps);
void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream,
GstTagList * tags);
void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f);
GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer);
GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream,
GstClockTime duration);
gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux);
void
gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream);
void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
GstEvent * event);
gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream);
gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux);
GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux);
gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux);

View file

@ -116,36 +116,32 @@ gst_hls_update_time_mappings (GstHLSDemux * demux,
static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream *
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
GstClockTimeDiff * final_ts);
static gboolean
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static GstFlowReturn gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static GstFlowReturn gst_hls_demux_data_received (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
static GstFlowReturn
gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream);
static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream
* stream, GstBuffer * buffer);
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
* stream);
static GstFlowReturn gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream *
static GstFlowReturn
gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream);
static GstFlowReturn
gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream *
stream);
static GstFlowReturn gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream
* stream);
static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static void gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream);
static gboolean gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream,
guint64 bitrate);
static void gst_hls_demux_reset (GstAdaptiveDemux * demux);
static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux,
gint64 * start, gint64 * stop);
static GstClockTime gst_hls_demux_get_presentation_offset (GstAdaptiveDemux *
demux, GstAdaptiveDemux2Stream * stream);
static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
GstHLSVariantStream * variant);
static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream *
stream);
static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
stream, guint64 bitrate);
static GstClockTime
gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
static void gst_hls_demux_stream_finalize (GObject * object);
@ -161,8 +157,31 @@ static void
gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
gobject_class->finalize = gst_hls_demux_stream_finalize;
adaptivedemux2stream_class->update_fragment_info =
gst_hls_demux_stream_update_fragment_info;
adaptivedemux2stream_class->has_next_fragment =
gst_hls_demux_stream_has_next_fragment;
adaptivedemux2stream_class->advance_fragment =
gst_hls_demux_stream_advance_fragment;
adaptivedemux2stream_class->select_bitrate =
gst_hls_demux_stream_select_bitrate;
adaptivedemux2stream_class->can_start = gst_hls_demux_stream_can_start;
adaptivedemux2stream_class->create_tracks =
gst_hls_demux_stream_create_tracks;
adaptivedemux2stream_class->start_fragment =
gst_hls_demux_stream_start_fragment;
adaptivedemux2stream_class->finish_fragment =
gst_hls_demux_stream_finish_fragment;
adaptivedemux2stream_class->data_received =
gst_hls_demux_stream_data_received;
adaptivedemux2stream_class->get_presentation_offset =
gst_hls_demux_stream_get_presentation_offset;
}
static void
@ -181,6 +200,12 @@ typedef struct _GstHLSDemux2Class GstHLSDemux2Class;
G_DEFINE_TYPE_WITH_CODE (GstHLSDemux2, gst_hls_demux2, GST_TYPE_ADAPTIVE_DEMUX,
hls2_element_init ());
static void gst_hls_demux_reset (GstAdaptiveDemux * demux);
static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux,
gint64 * start, gint64 * stop);
static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
GstHLSVariantStream * variant);
static void
gst_hls_demux_finalize (GObject * obj)
{
@ -263,8 +288,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
adaptivedemux_class->is_live = gst_hls_demux_is_live;
adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range;
adaptivedemux_class->get_presentation_offset =
gst_hls_demux_get_presentation_offset;
adaptivedemux_class->get_duration = gst_hls_demux_get_duration;
adaptivedemux_class->get_manifest_update_interval =
gst_hls_demux_get_manifest_update_interval;
@ -273,19 +296,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
adaptivedemux_class->reset = gst_hls_demux_reset;
adaptivedemux_class->seek = gst_hls_demux_seek;
adaptivedemux_class->stream_seek = gst_hls_demux_stream_seek;
adaptivedemux_class->stream_has_next_fragment =
gst_hls_demux_stream_has_next_fragment;
adaptivedemux_class->stream_advance_fragment = gst_hls_demux_advance_fragment;
adaptivedemux_class->stream_update_fragment_info =
gst_hls_demux_update_fragment_info;
adaptivedemux_class->stream_select_bitrate = gst_hls_demux_select_bitrate;
adaptivedemux_class->stream_can_start = gst_hls_demux_stream_can_start;
adaptivedemux_class->stream_update_tracks =
gst_hls_demux_stream_update_tracks;
adaptivedemux_class->start_fragment = gst_hls_demux_start_fragment;
adaptivedemux_class->finish_fragment = gst_hls_demux_finish_fragment;
adaptivedemux_class->data_received = gst_hls_demux_data_received;
}
static void
@ -617,16 +627,15 @@ get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype)
}
static void
gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
guint i;
GstStreamType uriless_types = 0;
GstCaps *variant_caps = NULL;
GST_DEBUG_OBJECT (demux, "Update tracks of variant stream");
GST_DEBUG_OBJECT (stream, "Update tracks of variant stream");
if (hlsdemux->master->have_codecs) {
variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master);
@ -662,7 +671,7 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
if (embedded_media) {
GstTagList *tags = gst_stream_get_tags (gst_stream);
GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream",
GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
embedded_media->name);
track =
new_track_for_rendition (hlsdemux, embedded_media, manifest_caps,
@ -673,10 +682,10 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type),
i);
GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream",
GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
stream_id);
track =
gst_adaptive_demux_track_new (demux, stream_type,
gst_adaptive_demux_track_new (stream->demux, stream_type,
flags, stream_id, manifest_caps, NULL);
g_free (stream_id);
}
@ -1037,11 +1046,10 @@ out:
}
static gboolean
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
const GstHLSKey *key;
GstHLSMediaPlaylist *m3u8;
@ -1069,14 +1077,14 @@ gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
key_failed:
{
GST_ELEMENT_ERROR (demux, STREAM, DECRYPT_NOKEY,
GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY,
("Couldn't retrieve key for decryption"), (NULL));
GST_WARNING_OBJECT (demux, "Failed to decrypt data");
GST_WARNING_OBJECT (hlsdemux, "Failed to decrypt data");
return FALSE;
}
decrypt_start_failed:
{
GST_ELEMENT_ERROR (demux, STREAM, DECRYPT, ("Failed to start decrypt"),
GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT, ("Failed to start decrypt"),
("Couldn't set key and IV or plugin was built without crypto library"));
return FALSE;
}
@ -1480,11 +1488,11 @@ out_resync:
}
static GstFlowReturn
gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer, gboolean at_eos)
gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer, gboolean at_eos)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *pending_header_data = NULL;
@ -1596,8 +1604,7 @@ out:
}
static GstFlowReturn
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
GstFlowReturn ret = GST_FLOW_OK;
@ -1626,7 +1633,7 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
}
ret =
gst_hls_demux_handle_buffer (demux, stream,
gst_hls_demux_stream_handle_buffer (stream,
hls_stream->pending_decrypted_buffer, TRUE);
hls_stream->pending_decrypted_buffer = NULL;
}
@ -1636,14 +1643,14 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
GstBuffer *buf = hls_stream->pending_typefind_buffer;
hls_stream->pending_typefind_buffer = NULL;
gst_hls_demux_handle_buffer (demux, stream, buf, TRUE);
gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
}
if (hls_stream->pending_segment_data) {
GstBuffer *buf = hls_stream->pending_segment_data;
hls_stream->pending_segment_data = NULL;
ret = gst_hls_demux_handle_buffer (demux, stream, buf, TRUE);
ret = gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
}
}
}
@ -1664,18 +1671,18 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
* before advancing. Note that we don't have any period so we can set the
* stream_time as-is on the stream current position */
stream->current_position = hls_stream->current_segment->stream_time;
return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
return gst_adaptive_demux2_stream_advance_fragment (stream,
hls_stream->current_segment->duration);
}
return ret;
}
static GstFlowReturn
gst_hls_demux_data_received (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
GstBuffer * buffer)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
if (hls_stream->current_segment == NULL)
return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
@ -1707,7 +1714,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
decrypted_buffer =
gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
if (err) {
GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
GST_ELEMENT_ERROR (hlsdemux, STREAM, DECODE, ("Failed to decrypt buffer"),
("decryption failed %s", err->message));
g_error_free (err);
return GST_FLOW_ERROR;
@ -1720,7 +1727,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
return GST_FLOW_OK;
}
return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE);
return gst_hls_demux_stream_handle_buffer (stream, buffer, FALSE);
}
static void
@ -1794,7 +1801,7 @@ gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
}
static GstFlowReturn
gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
@ -2330,7 +2337,7 @@ gst_hls_demux_stream_update_variant_playlist (GstHLSDemux * demux,
}
static GstFlowReturn
gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
{
GstFlowReturn ret = GST_FLOW_OK;
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
@ -2456,14 +2463,13 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream)
}
static gboolean
gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
GList *tmp;
GST_DEBUG_OBJECT (demux, "is_variant:%d mappings:%p", hls_stream->is_variant,
GST_DEBUG_OBJECT (stream, "is_variant:%d mappings:%p", hls_stream->is_variant,
hlsdemux->mappings);
/* Variant streams can always start straight away */
@ -2556,7 +2562,8 @@ gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
}
static gboolean
gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream, guint64 bitrate)
gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
guint64 bitrate)
{
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
@ -2964,10 +2971,9 @@ gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
}
static GstClockTime
gst_hls_demux_get_presentation_offset (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream)
gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,