{
PROP_0,
- PROP_MIN_BUFFERING_TIME,
PROP_MAX_BUFFERING_TIME,
PROP_BANDWIDTH_USAGE,
PROP_MAX_BITRATE,
};
/* Default values for properties */
-#define DEFAULT_MIN_BUFFERING_TIME 5 /* in seconds */
#define DEFAULT_MAX_BUFFERING_TIME 30 /* in seconds */
#define DEFAULT_BANDWIDTH_USAGE 0.8 /* 0 to 1 */
#define DEFAULT_MAX_BITRATE 24000000 /* in bit/s */
#define DEFAULT_FAILED_COUNT 3
+#define DOWNLOAD_RATE_HISTORY_MAX 3
+#define DOWNLOAD_RATE_TIME_MAX 3 * GST_SECOND
+
+/* Custom internal event to signal end of period */
+#define GST_EVENT_DASH_EOP GST_EVENT_MAKE_TYPE(81, GST_EVENT_TYPE_DOWNSTREAM | GST_EVENT_TYPE_SERIALIZED)
+static GstEvent *
+gst_event_new_dash_eop (void)
+{
+ return gst_event_new_custom (GST_EVENT_DASH_EOP, NULL);
+}
/* GObject */
static void gst_dash_demux_stream_loop (GstDashDemux * demux);
static void gst_dash_demux_download_loop (GstDashDemux * demux);
static void gst_dash_demux_stop (GstDashDemux * demux);
-static void gst_dash_demux_pause_stream_task (GstDashDemux * demux);
static void gst_dash_demux_resume_stream_task (GstDashDemux * demux);
static void gst_dash_demux_resume_download_task (GstDashDemux * demux);
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux);
-static gboolean gst_dash_demux_select_representations (GstDashDemux * demux,
- guint64 current_bitrate);
-static gboolean gst_dash_demux_get_next_fragment_set (GstDashDemux * demux);
+static gboolean gst_dash_demux_select_representations (GstDashDemux * demux);
+static GstCaps *gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream);
+static gboolean gst_dash_demux_get_next_fragment (GstDashDemux * demux, GstActiveStream **fragment_stream, GstClockTime *selected_ts);
+static void gst_dash_demux_clear_streams(GstDashDemux * demux);
static void gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose);
static GstClockTime gst_dash_demux_get_buffering_time (GstDashDemux * demux);
-static float gst_dash_demux_get_buffering_ratio (GstDashDemux * demux);
-static GstBuffer *gst_dash_demux_merge_buffer_list (GstFragment * fragment);
static void
_do_init (GType type)
}
gst_object_unref (demux->download_task);
g_static_rec_mutex_free (&demux->download_lock);
- g_mutex_free(demux->download_timed_lock);
demux->download_task = NULL;
}
+ g_cond_clear (&demux->download_cond);
+ g_mutex_clear (&demux->download_mutex);
+
if (demux->downloader != NULL) {
g_object_unref (demux->downloader);
demux->downloader = NULL;
gst_dash_demux_reset (demux, TRUE);
- g_queue_free (demux->queue);
-
G_OBJECT_CLASS (parent_class)->dispose (obj);
}
gobject_class->get_property = gst_dash_demux_get_property;
gobject_class->dispose = gst_dash_demux_dispose;
- g_object_class_install_property (gobject_class, PROP_MIN_BUFFERING_TIME,
- g_param_spec_uint ("min-buffering-time", "Minimum buffering time",
- "Minimum number of seconds of buffer accumulated before playback",
- 1, G_MAXUINT, DEFAULT_MIN_BUFFERING_TIME,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
g_object_class_install_property (gobject_class, PROP_MAX_BUFFERING_TIME,
g_param_spec_uint ("max-buffering-time", "Maximum buffering time",
"Maximum number of seconds of buffer accumulated during playback",
GST_DEBUG_FUNCPTR (gst_dash_demux_change_state);
}
+static gboolean
+_check_queue_full (GstDataQueue * q, guint visible, guint bytes, guint64 time,
+ GstDashDemux *demux)
+{
+ return time > demux->max_buffering_time;
+}
+
+static void
+_data_queue_item_destroy (GstDataQueueItem * item)
+{
+ gst_mini_object_unref (item->object);
+ g_free (item);
+}
+
+static void
+gst_dash_demux_stream_push_event (GstDashDemuxStream * stream,
+ GstEvent *event)
+{
+ GstDataQueueItem *item = g_new0 (GstDataQueueItem, 1);
+
+ item->object = GST_MINI_OBJECT_CAST (event);
+ item->destroy = (GDestroyNotify) _data_queue_item_destroy;
+
+ gst_data_queue_push (stream->queue, item);
+}
+
+static void
+gst_dash_demux_stream_push_data (GstDashDemuxStream * stream,
+ GstBuffer * buffer)
+{
+ GstDataQueueItem *item = g_new0 (GstDataQueueItem, 1);
+
+ item->object = GST_MINI_OBJECT_CAST (buffer);
+ item->duration = GST_BUFFER_DURATION (buffer);
+ item->visible = TRUE;
+ item->size = GST_BUFFER_SIZE (buffer);
+
+ item->destroy = (GDestroyNotify) _data_queue_item_destroy;
+
+ gst_data_queue_push (stream->queue, item);
+}
+
static void
gst_dash_demux_init (GstDashDemux * demux, GstDashDemuxClass * klass)
{
demux->downloader = gst_uri_downloader_new ();
/* Properties */
- demux->min_buffering_time = DEFAULT_MIN_BUFFERING_TIME * GST_SECOND;
demux->max_buffering_time = DEFAULT_MAX_BUFFERING_TIME * GST_SECOND;
demux->bandwidth_usage = DEFAULT_BANDWIDTH_USAGE;
demux->max_bitrate = DEFAULT_MAX_BITRATE;
- demux->queue = g_queue_new ();
+ demux->max_video_width = 0;
+ demux->max_video_height = 0;
+
/* Updates task */
g_static_rec_mutex_init (&demux->download_lock);
demux->download_task =
gst_task_create ((GstTaskFunction) gst_dash_demux_download_loop, demux);
gst_task_set_lock (demux->download_task, &demux->download_lock);
- demux->download_timed_lock = g_mutex_new ();
+ g_cond_init (&demux->download_cond);
+ g_mutex_init (&demux->download_mutex);
/* Streaming task */
g_static_rec_mutex_init (&demux->stream_lock);
GstDashDemux *demux = GST_DASH_DEMUX (object);
switch (prop_id) {
- case PROP_MIN_BUFFERING_TIME:
- demux->min_buffering_time = g_value_get_uint (value) * GST_SECOND;
- break;
case PROP_MAX_BUFFERING_TIME:
demux->max_buffering_time = g_value_get_uint (value) * GST_SECOND;
break;
GstDashDemux *demux = GST_DASH_DEMUX (object);
switch (prop_id) {
- case PROP_MIN_BUFFERING_TIME:
- g_value_set_uint (value, demux->min_buffering_time);
- demux->min_buffering_time *= GST_SECOND;
- break;
case PROP_MAX_BUFFERING_TIME:
- g_value_set_uint (value, demux->max_buffering_time);
- demux->max_buffering_time *= GST_SECOND;
+ g_value_set_uint (value, demux->max_buffering_time / GST_SECOND);
break;
case PROP_BANDWIDTH_USAGE:
g_value_set_float (value, demux->bandwidth_usage);
GstStateChangeReturn ret;
GstDashDemux *demux = GST_DASH_DEMUX (element);
+ GST_DEBUG_OBJECT (demux, "changing state %s - %s",
+ gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+ gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_dash_demux_reset (demux, FALSE);
break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- /* Start the streaming loop in paused only if we already received
- the manifest. It might have been stopped if we were in PAUSED
- state and we filled our queue with enough cached fragments
- */
- if (demux->client->mpd_node != NULL)
- gst_dash_demux_resume_stream_task (demux);
- break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- gst_dash_demux_pause_stream_task (demux);
- break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
demux->cancelled = TRUE;
gst_dash_demux_stop (demux);
}
void
-gst_dash_demux_clear_queue (GstDashDemux * demux)
+gst_dash_demux_flush_stream_queues (GstDashDemux * demux)
{
- while (!g_queue_is_empty (demux->queue)) {
- GList *listfragment = g_queue_pop_head (demux->queue);
- guint j = 0;
- while (j < g_list_length (listfragment)) {
- GstFragment *fragment = g_list_nth_data (listfragment, j);
- g_object_unref (fragment);
- j++;
- }
- g_list_free (listfragment);
+ GSList *it;
+ GstDashDemuxStream *stream;
+ for(it = demux->streams; it; it=it->next)
+ {
+ stream = it->data;
+ gst_data_queue_set_flushing(stream->queue, TRUE);
+ gst_data_queue_flush(stream->queue);
}
- g_queue_clear (demux->queue);
}
static gboolean
GstSeekType start_type, stop_type;
gint64 start, stop;
GList *list;
- GstClockTime current_pos, target_pos;
- guint current_sequence, current_period;
+ GstClockTime current_pos = GST_CLOCK_TIME_NONE;
+ GstClockTime target_pos;
+ guint current_period;
GstActiveStream *stream;
- GstMediaSegment *chunk;
- GstStreamPeriod *period;
+ GstStreamPeriod *period = NULL;
guint nb_active_stream;
- guint stream_idx;
+ guint stream_idx = 0;
+ guint *seek_idx = NULL; /*Seek positions on each stream*/
+ gboolean end_of_mpd = FALSE;
if (gst_mpd_client_is_live (demux->client)) {
GST_WARNING_OBJECT (demux, "Received seek event for live stream");
break;
}
}
+ if(target_pos == current_pos + period->duration) {
+ /*Seeking to the end of MPD*/
+ end_of_mpd = TRUE;
+ goto seeking;
+ }
if (list == NULL) {
GST_WARNING_OBJECT (demux, "Could not find seeked Period");
return FALSE;
return FALSE;
}
- stream = gst_mpdparser_get_active_stream_by_index (demux->client, 0);
- current_pos = 0;
- for (list = g_list_first (stream->segments); list;
- list = g_list_next (list)) {
- chunk = list->data;
- current_pos = chunk->start_time;
- current_sequence = chunk->number;
- if (current_pos <= target_pos
- && target_pos < current_pos + chunk->duration) {
- break;
- }
+ /*select the requested segments for all streams*/
+ nb_active_stream = gst_mpdparser_get_nb_active_stream (demux->client);
+ seek_idx = g_malloc0(sizeof(gint)*nb_active_stream);
+ gint video_idx = gst_mpd_client_get_video_active_stream_id(demux->client);
+ if(video_idx >= 0) {
+ /*Seeking on video stream firstly.*/
+ GstClockTime segment_start;
+ segment_start = gst_mpd_client_stream_find_segment(demux->client, video_idx,
+ target_pos, &seek_idx[video_idx]);
+ if(!GST_CLOCK_TIME_IS_VALID(segment_start))
+ goto no_segment;
+ target_pos = segment_start;
}
- //GST_MPD_CLIENT_UNLOCK (demux->client);
-
- if (list == NULL) {
- GST_WARNING_OBJECT (demux, "Could not find seeked fragment");
- return FALSE;
+ /*Seeking on non video streams*/
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ if (video_idx != stream_idx) {
+ GstClockTime stream_start = gst_mpd_client_stream_find_segment(demux->client,
+ stream_idx, target_pos, &seek_idx[stream_idx]);
+ if(!GST_CLOCK_TIME_IS_VALID (stream_start)) {
+ goto no_segment;
+ }
+ }
}
+seeking:
/* We can actually perform the seek */
nb_active_stream = gst_mpdparser_get_nb_active_stream (demux->client);
GST_DEBUG_OBJECT (demux, "sending flush start");
stream_idx = 0;
while (stream_idx < nb_active_stream) {
- gst_pad_push_event (demux->srcpad[stream_idx],
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ dash_stream->need_header = TRUE;
+ gst_pad_push_event (dash_stream->srcpad,
gst_event_new_flush_start ());
stream_idx++;
}
/* Stop the demux */
demux->cancelled = TRUE;
gst_dash_demux_stop (demux);
+ GST_DEBUG_OBJECT (demux, "joining tasks");
+ gst_task_join (demux->stream_task);
+ gst_task_join (demux->download_task);
+ GST_DEBUG_OBJECT (demux, "tasks was joined");
/* Wait for streaming to finish */
g_static_rec_mutex_lock (&demux->stream_lock);
- /* Clear the buffering queue */
- /* FIXME: allow seeking in the buffering queue */
- gst_dash_demux_clear_queue (demux);
-
//GST_MPD_CLIENT_LOCK (demux->client);
- GST_DEBUG_OBJECT (demux, "Seeking to sequence %d", current_sequence);
- /* Update the current sequence on all streams */
- gst_mpd_client_set_segment_index_for_all_streams (demux->client,
- current_sequence);
- /* Calculate offset in the next fragment */
- demux->position = gst_mpd_client_get_current_position (demux->client);
- demux->position_shift = start - demux->position;
- demux->need_segment = TRUE;
+ demux->end_of_period = end_of_mpd;
//GST_MPD_CLIENT_UNLOCK (demux->client);
+
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ GstCaps *caps = gst_pad_get_negotiated_caps (dash_stream->srcpad);
+ if(caps) {
+ gst_caps_replace (&dash_stream->input_caps, NULL);
+ gst_caps_unref (caps);
+ }
+ if(!end_of_mpd) {
+ GST_DEBUG_OBJECT (demux, "Seeking to sequence %d on stream %d", seek_idx[stream_idx], stream_idx);
+ stream = gst_mpdparser_get_active_stream_by_index (demux->client, stream_idx);
+ gst_mpd_client_set_segment_index(stream, seek_idx[stream_idx]);
+ }
+ gst_data_queue_set_flushing(dash_stream->queue, FALSE);
+ dash_stream->start_time = target_pos;
+ dash_stream->download_end_of_period = end_of_mpd;
+ dash_stream->stream_end_of_period = end_of_mpd;
+ dash_stream->stream_eos = end_of_mpd;
+ dash_stream->need_segment = TRUE;
+ }
+ if(!end_of_mpd)
+ g_free(seek_idx);
+
if (flags & GST_SEEK_FLAG_FLUSH) {
GST_DEBUG_OBJECT (demux, "Sending flush stop on all pad");
- stream_idx = 0;
- while (stream_idx < nb_active_stream) {
- gst_pad_push_event (demux->srcpad[stream_idx],
+
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ gst_pad_push_event (dash_stream->srcpad,
gst_event_new_flush_stop ());
- stream_idx++;
}
}
g_static_rec_mutex_unlock (&demux->stream_lock);
return TRUE;
+no_segment:
+ {
+ GST_WARNING_OBJECT (demux, "Could not find seeked fragment on stream %d", stream_idx);
+ g_free(seek_idx);
+ return FALSE;
+ }
}
default:
break;
}
static gboolean
-gst_dash_demux_setup_all_streams (GstDashDemux * demux)
+gst_dash_demux_setup_mpdparser_streams (GstDashDemux * demux, GstMpdClient *client)
{
GList *listLang = NULL;
guint i, nb_audio;
gchar *lang;
- GST_MPD_CLIENT_LOCK (demux->client);
+ GST_MPD_CLIENT_LOCK (client);
/* clean old active stream list, if any */
- gst_active_streams_free (demux->client);
+ gst_active_streams_free (client);
- if (!gst_mpd_client_setup_streaming (demux->client, GST_STREAM_VIDEO, ""))
+ if (!gst_mpd_client_setup_streaming (client, GST_STREAM_VIDEO, "")) {
GST_INFO_OBJECT (demux, "No video adaptation set found");
+ } else {
+ gst_mpd_client_get_max_video_dimensions(client, &demux->max_video_width,
+ &demux->max_video_height);
+ }
nb_audio =
- gst_mpdparser_get_list_and_nb_of_audio_language (demux->client,
+ gst_mpdparser_get_list_and_nb_of_audio_language (client,
&listLang);
if (nb_audio == 0)
nb_audio = 1;
for (i = 0; i < nb_audio; i++) {
lang = (gchar *) g_list_nth_data (listLang, i);
- if (gst_mpdparser_get_nb_adaptationSet (demux->client) > 1)
- if (!gst_mpd_client_setup_streaming (demux->client, GST_STREAM_AUDIO,
+ if (gst_mpdparser_get_nb_adaptationSet (client) > 1)
+ if (!gst_mpd_client_setup_streaming (client, GST_STREAM_AUDIO,
lang))
GST_INFO_OBJECT (demux, "No audio adaptation set found");
- if (gst_mpdparser_get_nb_adaptationSet (demux->client) > nb_audio)
- if (!gst_mpd_client_setup_streaming (demux->client,
+ if (gst_mpdparser_get_nb_adaptationSet (client) > nb_audio)
+ if (!gst_mpd_client_setup_streaming (client,
GST_STREAM_APPLICATION, lang))
GST_INFO_OBJECT (demux, "No application adaptation set found");
}
- GST_MPD_CLIENT_UNLOCK (demux->client);
+ GST_MPD_CLIENT_UNLOCK (client);
+ return TRUE;
+}
+
+static gboolean
+gst_dash_demux_setup_all_streams (GstDashDemux * demux)
+{
+ guint i;
+ if( !gst_dash_demux_setup_mpdparser_streams(demux, demux->client))
+ return FALSE;
+
+ GST_DEBUG_OBJECT (demux, "Creating dashdemux streams");
+ gst_dash_demux_clear_streams(demux);
+ for ( i =0; i < gst_mpdparser_get_nb_active_stream (demux->client); i++) {
+ GstDashDemuxStream *dash_stream;
+ GstCaps *caps;
+ GstActiveStream *active_stream;
+ dash_stream = g_new0(GstDashDemuxStream, 1);
+ demux->streams = g_slist_append(demux->streams, dash_stream);
+ dash_stream->idx = i;
+ dash_stream->queue = gst_data_queue_new ((GstDataQueueCheckFullFunction) _check_queue_full, demux);
+ dash_stream->need_header = TRUE;
+ dash_stream->need_segment = TRUE;
+ dash_stream->start_time = GST_CLOCK_TIME_NONE;
+ gst_download_rate_init (&dash_stream->dnl_rate);
+ gst_download_rate_set_max_length (&dash_stream->dnl_rate,
+ DOWNLOAD_RATE_HISTORY_MAX);
+ gst_download_rate_set_aver_period (&dash_stream->dnl_rate,
+ DOWNLOAD_RATE_TIME_MAX);
+ /*Create stream pad*/
+ active_stream = gst_mpdparser_get_active_stream_by_index(demux->client, i);
+ caps = gst_dash_demux_get_input_caps(demux, active_stream);
+ dash_stream->srcpad = gst_pad_new_from_static_template (&srctemplate, NULL);
+ gst_pad_set_event_function (dash_stream->srcpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_src_event));
+ gst_pad_set_query_function (dash_stream->srcpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_src_query));
+ gst_pad_set_element_private (dash_stream->srcpad, demux);
+ gst_pad_set_active (dash_stream->srcpad, TRUE);
+ gst_pad_set_caps (dash_stream->srcpad, caps);
+ gst_caps_unref(caps);
+ gst_element_add_pad (GST_ELEMENT (demux), gst_object_ref (dash_stream->srcpad));
+ }
+ /* Send 'no-more-pads' to have decodebin create the new group */
+ gst_element_no_more_pads (GST_ELEMENT (demux));
return TRUE;
}
(NULL));
return FALSE;
}
+
gst_buffer_unref (demux->manifest);
demux->manifest = NULL;
gst_dash_demux_stop (GstDashDemux * demux)
{
gst_uri_downloader_cancel (demux->downloader);
+ gst_dash_demux_flush_stream_queues (demux);
if (GST_TASK_STATE (demux->download_task) != GST_TASK_STOPPED) {
GST_TASK_SIGNAL (demux->download_task);
gst_task_stop (demux->download_task);
+ g_mutex_lock (&demux->download_mutex);
+ g_cond_signal (&demux->download_cond);
+ g_mutex_unlock (&demux->download_mutex);
}
if (GST_TASK_STATE (demux->stream_task) != GST_TASK_STOPPED) {
GST_TASK_SIGNAL (demux->stream_task);
gst_task_stop (demux->stream_task);
}
}
-#ifdef DASHDEMUX_MODIFICATION
-static void
-_update_caps(GstDashDemux * demux, guint nb_adaptation_set)
-{
- guint i = 0;
- GstCaps *caps;
-
- /* Create and activate new pads */
- i = 0;
- while (i < nb_adaptation_set) {
- caps = gst_pad_get_caps(demux->srcpad[i]);
- if(caps){
- caps = gst_caps_make_writable(caps);
- gst_caps_replace(&caps,demux->output_caps[i]);
- }
- else{
- caps = demux->output_caps[i];
- }
- gst_pad_use_fixed_caps (demux->srcpad[i]);
- gst_pad_set_caps (demux->srcpad[i], caps);
- gst_caps_unref(caps);
- i++;
- }
-}
-
-static void
-_add_pads(GstDashDemux * demux, guint nb_adaptation_set)
-{
- guint i = 0;
-
- /* Create and activate new pads */
- i = 0;
- while (i < nb_adaptation_set) {
- demux->srcpad[i] = gst_pad_new_from_static_template (&srctemplate, NULL);
- gst_pad_set_event_function (demux->srcpad[i],
- GST_DEBUG_FUNCPTR (gst_dash_demux_src_event));
- gst_pad_set_query_function (demux->srcpad[i],
- GST_DEBUG_FUNCPTR (gst_dash_demux_src_query));
- gst_pad_set_element_private (demux->srcpad[i], demux);
- gst_pad_set_active (demux->srcpad[i], TRUE);
- gst_pad_set_caps (demux->srcpad[i], demux->output_caps[i]);
- gst_caps_unref(demux->output_caps[i]);
- gst_element_add_pad (GST_ELEMENT (demux), demux->srcpad[i]);
- i++;
- }
- /* Send 'no-more-pads' to have decodebin create the new group */
- gst_element_no_more_pads (GST_ELEMENT (demux));
-}
-
-static gboolean
-_needs_caps_update (GstDashDemux * demux, GList * fragment)
-{
-
- gboolean update_caps = FALSE;
- guint i = 0;
- while (i < g_list_length (fragment)) {
- GstFragment *newFragment = g_list_nth_data (fragment, i);
- if (newFragment == NULL) {
- continue;
- }
- GstCaps *srccaps = NULL;
- demux->output_caps[i] = gst_fragment_get_caps (newFragment);
- if (G_LIKELY (demux->srcpad[i]))
- srccaps = gst_pad_get_negotiated_caps (demux->srcpad[i]);
- if (G_UNLIKELY (!srccaps
- || (!gst_caps_is_equal_fixed (demux->output_caps[i], srccaps))
- || demux->need_segment)) {
- update_caps = TRUE;
- }
- if (G_LIKELY (srccaps))
- gst_caps_unref (srccaps);
- i++;
- }
- return update_caps;
-}
-
-#else
-/* switch_pads:
- *
- * Called when switching from one set of representations to another, but
- * only if one of the new representations requires different downstream
- * elements (see the next function).
- *
- * This function first creates the new pads, then sends a no-more-pads
- * event (that will tell decodebin to create a new group), then sends
- * EOS on the old pads to trigger the group switch.
- *
- */
-static void
-switch_pads (GstDashDemux * demux, guint nb_adaptation_set)
-{
- GstPad *oldpad[MAX_LANGUAGES];
- guint i = 0;
- /* Remember old pads */
- while (i < nb_adaptation_set) {
- oldpad[i] = demux->srcpad[i];
- if (oldpad[i]) {
- GST_DEBUG_OBJECT (demux,
- "Switching pads (oldpad:%p)" GST_PTR_FORMAT, oldpad[i]);
- }
- i++;
- }
- /* Create and activate new pads */
- i = 0;
- while (i < nb_adaptation_set) {
- demux->srcpad[i] = gst_pad_new_from_static_template (&srctemplate, NULL);
- gst_pad_set_event_function (demux->srcpad[i],
- GST_DEBUG_FUNCPTR (gst_dash_demux_src_event));
- gst_pad_set_query_function (demux->srcpad[i],
- GST_DEBUG_FUNCPTR (gst_dash_demux_src_query));
- gst_pad_set_element_private (demux->srcpad[i], demux);
- gst_pad_set_active (demux->srcpad[i], TRUE);
- gst_pad_set_caps (demux->srcpad[i], demux->output_caps[i]);
- gst_element_add_pad (GST_ELEMENT (demux), demux->srcpad[i]);
- i++;
- }
- /* Send 'no-more-pads' to have decodebin create the new group */
- gst_element_no_more_pads (GST_ELEMENT (demux));
- /* Push out EOS on all old pads to switch to the new group */
- i = 0;
- while (i < nb_adaptation_set) {
- if (oldpad[i]) {
- gst_pad_push_event (oldpad[i], gst_event_new_eos ());
- gst_pad_set_active (oldpad[i], FALSE);
- gst_element_remove_pad (GST_ELEMENT (demux), oldpad[i]);
- }
- i++;
- }
-}
-
-/* needs_pad_switch:
- *
- * Figure out if the newly selected representations require a new set
- * of demuxers and decoders or if we can carry on with the existing ones.
- *
- * Basically, we look at the list of fragments we need to push downstream,
- * and compare their caps with those of the corresponding src pads.
- *
- * As soon as one fragment requires a new set of caps, we need to switch
- * all decoding pads to recreate a whole decoding group as we cannot
- * move pads between groups (FIXME: or can we ?).
- *
- * FIXME: redundant with need_add_header
- *
- */
-static gboolean
-needs_pad_switch (GstDashDemux * demux, GList * fragment)
-{
-
- gboolean switch_pad = FALSE;
- guint i = 0;
- while (i < g_list_length (fragment)) {
- GstFragment *newFragment = g_list_nth_data (fragment, i);
- if (newFragment == NULL) {
- continue;
- }
- GstCaps *srccaps = NULL;
- demux->output_caps[i] = gst_fragment_get_caps (newFragment);
- if (G_LIKELY (demux->srcpad[i]))
- srccaps = gst_pad_get_negotiated_caps (demux->srcpad[i]);
- if (G_UNLIKELY (!srccaps
- || (!gst_caps_is_equal_fixed (demux->output_caps[i], srccaps))
- || demux->need_segment)) {
- switch_pad = TRUE;
- }
- if (G_LIKELY (srccaps))
- gst_caps_unref (srccaps);
- i++;
- }
- return switch_pad;
-}
-#endif
/* gst_dash_demux_stream_loop:
*
static void
gst_dash_demux_stream_loop (GstDashDemux * demux)
{
- GList *listfragment;
GstFlowReturn ret;
- GstBufferList *buffer_list;
- guint nb_adaptation_set = 0;
- GstActiveStream *stream;
-
- /* Wait until the next scheduled push downstream */
- if (g_cond_timed_wait (GST_TASK_GET_COND (demux->stream_task),
- demux->stream_timed_lock, &demux->next_push)) {
- goto quit;
- }
+ GstActiveStream *active_stream;
+ GstDashDemuxStream *selected_stream = NULL;
+ GstClockTime min_ts = GST_CLOCK_TIME_NONE;
+ guint i = 0;
+ gboolean eos = TRUE;
+ gboolean eop = TRUE;
- if (g_queue_is_empty (demux->queue)) {
- if (demux->end_of_manifest)
- goto end_of_manifest;
+ for (i = 0; i < g_slist_length (demux->streams); i++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ GstBuffer *buffer;
+ GstDataQueueItem *item;
- return;
- }
+ if (dash_stream->stream_eos) {
+ GST_DEBUG_OBJECT (demux, "Stream %d is eos, skipping", dash_stream->idx);
+ continue;
+ }
- if (GST_STATE (demux) == GST_STATE_PLAYING) {
- if (!demux->end_of_manifest
- && gst_dash_demux_get_buffering_time (demux) <
- demux->min_buffering_time) {
- /* Warn we are below our threshold: this will eventually pause
- * the pipeline */
- gst_element_post_message (GST_ELEMENT (demux),
- gst_message_new_buffering (GST_OBJECT (demux),
- 100 * gst_dash_demux_get_buffering_ratio (demux)));
+ if (dash_stream->stream_end_of_period) {
+ GST_DEBUG_OBJECT (demux, "Stream %d is eop, skipping", dash_stream->idx);
+ eos = FALSE;
+ continue;
}
- }
- listfragment = g_queue_pop_head (demux->queue);
- nb_adaptation_set = g_list_length (listfragment);
-#ifdef DASHDEMUX_MODIFICATION
- /* Figure out if we need to create pads or update caps */
- gboolean caps_changed = _needs_caps_update (demux, listfragment);
- if (caps_changed) {
- if(demux->srcpad[0] == NULL)
- {
- _add_pads(demux,nb_adaptation_set);
- demux->need_segment = TRUE;
- }else{
- _update_caps(demux,nb_adaptation_set);
+ eos = FALSE;
+ eop = FALSE;
+
+ if (!gst_data_queue_peek (dash_stream->queue, &item))
+ goto flushing;
+
+ if(GST_IS_BUFFER(item->object)) {
+ buffer = GST_BUFFER(item->object);
+ if(GST_BUFFER_TIMESTAMP(buffer) < min_ts ||
+ !GST_CLOCK_TIME_IS_VALID(min_ts)) {
+ min_ts = GST_BUFFER_TIMESTAMP(buffer);
+ selected_stream = dash_stream;
+ } else if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (item->object))) {
+ selected_stream = dash_stream;
+ break;
+ }
+ } else {
+ selected_stream = dash_stream;
+ break;
}
}
-#else
- /* Figure out if we need to create/switch pads */
- gboolean switch_pad = needs_pad_switch (demux, listfragment);
- if (switch_pad) {
- switch_pads (demux, nb_adaptation_set);
- demux->need_segment = TRUE;
- }
-#endif
- guint i = 0;
- for (i = 0; i < nb_adaptation_set; i++) {
- GstFragment *fragment = g_list_nth_data (listfragment, i);
- stream = gst_mpdparser_get_active_stream_by_index (demux->client, i);
- if (demux->need_segment) {
- GstClockTime start = fragment->start_time + demux->position_shift;
- /* And send a newsegment */
- GST_DEBUG_OBJECT (demux, "Sending new-segment. segment start:%"
- GST_TIME_FORMAT, GST_TIME_ARGS (start));
- gst_pad_push_event (demux->srcpad[i],
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
- start, GST_CLOCK_TIME_NONE, start));
- demux->position_shift = 0;
- }
- GST_DEBUG_OBJECT (demux, "Pushing fragment #%d", fragment->index);
- buffer_list = gst_fragment_get_buffer_list (fragment);
- g_object_unref (fragment);
- ret = gst_pad_push_list (demux->srcpad[i], buffer_list);
- if ((ret != GST_FLOW_OK) && (stream->mimeType == GST_STREAM_VIDEO))
- goto error_pushing;
- }
- demux->need_segment = FALSE;
- g_list_free (listfragment);
- GST_STATE_LOCK(demux);
- if (GST_STATE (demux) == GST_STATE_PLAYING) {
- /* Wait for the duration of a fragment before resuming this task */
- g_get_current_time (&demux->next_push);
- g_time_val_add (&demux->next_push,
- gst_mpd_client_get_next_fragment_duration (demux->client)
- / GST_SECOND * G_USEC_PER_SEC);
- GST_DEBUG_OBJECT (demux, "Next push scheduled at %s",
- g_time_val_to_iso8601 (&demux->next_push));
+ if(selected_stream) {
+ GstBuffer *buffer;
+ GstDataQueueItem *item;
+
+ if (!gst_data_queue_pop (selected_stream->queue, &item))
+ goto end;
+ if ( GST_IS_BUFFER (item->object)) {
+ buffer = GST_BUFFER(item->object);
+ active_stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
+
+ if (selected_stream->need_segment) {
+ if(!GST_CLOCK_TIME_IS_VALID (selected_stream->start_time)) {
+ if(GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))){
+ selected_stream->start_time = GST_BUFFER_TIMESTAMP (buffer);
+ } else {
+ selected_stream->start_time = 0;
+ }
+ }
+ /* And send a newsegment */
+ GST_DEBUG_OBJECT (demux, "Sending new-segment stream #%d. segment start:%"
+ GST_TIME_FORMAT, selected_stream->idx, GST_TIME_ARGS (selected_stream->start_time));
+ gst_pad_push_event (selected_stream->srcpad,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ selected_stream->start_time, GST_CLOCK_TIME_NONE, selected_stream->start_time));
+ selected_stream->need_segment = FALSE;
+ }
+
+ GST_DEBUG_OBJECT (demux, "Pushing fragment #%llu (stream %d) ts=%"GST_TIME_FORMAT, GST_BUFFER_OFFSET (buffer),
+ selected_stream->idx, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+ ret = gst_pad_push (selected_stream->srcpad, gst_buffer_ref(buffer) );
+ item->destroy (item);
+ if ((ret != GST_FLOW_OK) && (active_stream->mimeType == GST_STREAM_VIDEO))
+ goto error_pushing;
+ } else {
+ GstEvent *event = GST_EVENT (item->object);
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ selected_stream->stream_eos = TRUE;
+ selected_stream->stream_end_of_period = TRUE;
+ } else if (GST_EVENT_TYPE (event) == GST_EVENT_DASH_EOP) {
+ selected_stream->stream_end_of_period = TRUE;
+ }
+
+ if (GST_EVENT_TYPE (item->object) != GST_EVENT_DASH_EOP) {
+ gst_pad_push_event (selected_stream->srcpad,
+ gst_event_ref (GST_EVENT_CAST (item->object)));
+ }
+
+ item->destroy (item);
+ }
} else {
- /* The pipeline is now set up, wait until playback begins */
- GST_INFO_OBJECT (demux, "Pausing streaming task");
- gst_task_pause (demux->stream_task);
+ if (eos) {
+ goto end_of_manifest;
+ } else if (eop) {
+ /*TODO Switch to next period*/
+ }
}
- GST_STATE_UNLOCK(demux);
-quit:
+end:
+ return;
+
+flushing:
{
+ GST_INFO_OBJECT (demux, "Queue is flushing. Stopped streaming task");
+ gst_task_stop (demux->stream_task);
return;
}
GST_INFO_OBJECT (demux, "Reached end of manifest, sending EOS");
guint i = 0;
for (i = 0; i < gst_mpdparser_get_nb_active_stream (demux->client); i++) {
- gst_pad_push_event (demux->srcpad[i], gst_event_new_eos ());
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ gst_pad_push_event (dash_stream->srcpad, gst_event_new_eos ());
}
GST_INFO_OBJECT (demux, "Stopped streaming task");
gst_task_stop (demux->stream_task);
}
static void
+gst_dash_demux_clear_streams(GstDashDemux * demux) {
+ guint i = 0;
+ gst_dash_demux_flush_stream_queues (demux);
+ for (i = 0; i < g_slist_length(demux->streams); i++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ gst_download_rate_deinit (&dash_stream->dnl_rate);
+ if (dash_stream->input_caps) {
+ gst_caps_unref (dash_stream->input_caps);
+ dash_stream->input_caps = NULL;
+ }
+ if (dash_stream->srcpad) {
+ gst_object_unref (dash_stream->srcpad);
+ dash_stream->srcpad = NULL;
+ }
+ /*TODO consider unref stream->output_caps*/
+ g_object_unref (dash_stream->queue);
+ }
+ if(demux->streams) {
+ g_slist_free(demux->streams);
+ demux->streams = NULL;
+ }
+}
+
+static void
gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose)
{
+ gint stream_idx;
demux->end_of_period = FALSE;
- demux->end_of_manifest = FALSE;
demux->cancelled = FALSE;
- guint i = 0;
- for (i = 0; i < MAX_LANGUAGES; i++)
- if (demux->input_caps[i]) {
- gst_caps_unref (demux->input_caps[i]);
- demux->input_caps[i] = NULL;
- }
+ gst_dash_demux_clear_streams(demux);
if (demux->manifest) {
gst_buffer_unref (demux->manifest);
demux->client = gst_mpd_client_new ();
}
- gst_dash_demux_clear_queue (demux);
-
demux->last_manifest_update = GST_CLOCK_TIME_NONE;
- demux->position = 0;
- demux->position_shift = 0;
- demux->need_segment = TRUE;
+ for (stream_idx = 0; stream_idx < g_slist_length (demux->streams); stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ dash_stream->need_segment = TRUE;
+ }
}
static GstClockTime
gst_dash_demux_get_buffering_time (GstDashDemux * demux)
{
GstClockTime buffer_time = 0;
- GList *listfragment;
- GstFragment *first_fragment, *last_fragment;
+ GSList *it;
+ GstDashDemuxStream *stream;
+ GstDataQueueSize queue_size;
- if (g_queue_is_empty (demux->queue))
- return 0;
+ for(it=demux->streams; it; it=it->next) {
+ stream = it->data;
+ gst_data_queue_get_level(stream->queue, &queue_size);
- /* get first fragment */
- listfragment = g_queue_peek_head (demux->queue);
- first_fragment = listfragment->data;
- /* get last fragment */
- listfragment = g_queue_peek_tail (demux->queue);
- last_fragment = listfragment->data;
-
- if (first_fragment && last_fragment) {
- buffer_time = last_fragment->stop_time - first_fragment->start_time;
+ if (queue_size.time > 0) {
+ buffer_time = queue_size.time;
+ break;
+ }
}
return buffer_time;
}
-static float
-gst_dash_demux_get_buffering_ratio (GstDashDemux * demux)
+static gboolean
+gst_dash_demux_update_manifest(GstDashDemux *demux) {
+ GstFragment *download;
+ GstBuffer *buffer;
+ GstClockTime duration, now = gst_util_get_timestamp();
+ gint64 update_period = demux->client->mpd_node->minimumUpdatePeriod;
+
+ if (update_period == -1) {
+ GST_DEBUG_OBJECT (demux, "minimumUpdatePeriod unspecified, will not update MPD");
+ return TRUE;
+ }
+
+ /* init reference time for manifest file updates */
+ if (!GST_CLOCK_TIME_IS_VALID (demux->last_manifest_update))
+ demux->last_manifest_update = now;
+
+ /* update the manifest file */
+ if (now >= demux->last_manifest_update + update_period * GST_MSECOND) {
+ GST_DEBUG_OBJECT (demux, "Updating manifest file from URL %s",
+ demux->client->mpd_uri);
+ download =
+ gst_uri_downloader_fetch_uri (demux->downloader,
+ demux->client->mpd_uri);
+ if (download == NULL) {
+ GST_WARNING_OBJECT (demux,
+ "Failed to update the manifest file from URL %s",
+ demux->client->mpd_uri);
+ } else {
+ GstMpdClient *new_client = NULL;
+ guint period_idx;
+ const gchar *period_id;
+ GSList *iter;
+
+ buffer = gst_fragment_get_buffer(download);
+ g_object_unref (download);
+ /* parse the manifest file */
+ if (buffer == NULL) {
+ GST_WARNING_OBJECT (demux, "Error validating the manifest.");
+ return TRUE;
+ }
+
+ new_client = gst_mpd_client_new ();
+ new_client->mpd_uri = g_strdup (demux->client->mpd_uri);
+ if (!gst_mpd_parse (new_client,
+ (gchar *) GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) {
+ /* In most cases, this will happen if we set a wrong url in the
+ * source element and we have received the 404 HTML response instead of
+ * the manifest */
+ GST_WARNING_OBJECT (demux, "Error parsing the manifest.");
+ gst_buffer_unref (buffer);
+ return TRUE;
+ }
+
+ gst_buffer_unref (buffer);
+ GST_DEBUG_OBJECT (demux, "Updating manifest");
+
+ period_id = gst_mpd_client_get_period_id (demux->client);
+ period_idx = gst_mpd_client_get_period_index (demux->client);
+
+ /* setup video, audio and subtitle streams, starting from current Period */
+ if (!gst_mpd_client_setup_media_presentation (new_client)) {
+ /* TODO */
+ }
+
+ if (period_idx) {
+ /*If more than one period exists.*/
+ if (!gst_mpd_client_set_period_id (new_client, period_id)) {
+ GST_DEBUG_OBJECT (demux,
+ "Error setting up the updated manifest file");
+ return FALSE;
+ }
+ } else {
+ if (!gst_mpd_client_set_period_index (new_client, period_idx)) {
+ GST_DEBUG_OBJECT (demux,
+ "Error setting up the updated manifest file");
+ return FALSE;
+ }
+ }
+
+ if (!gst_dash_demux_setup_mpdparser_streams (demux, new_client)) {
+ GST_ERROR_OBJECT (demux, "Failed to setup streams on manifest "
+ "update");
+ return FALSE;
+ }
+
+ /* update the streams to play from the next segment */
+ for (iter = demux->streams; iter; iter = g_slist_next (iter)) {
+ GstDashDemuxStream *demux_stream = iter->data;
+ GstActiveStream *new_stream;
+ GstClockTime ts;
+
+ new_stream = gst_mpdparser_get_active_stream_by_index (new_client,
+ demux_stream->idx);
+
+ if (!new_stream) {
+ GST_DEBUG_OBJECT (demux,
+ "Stream of index %d is missing from manifest update",
+ demux_stream->idx);
+ return FALSE;
+ }
+
+ if (gst_mpd_client_get_next_fragment_timestamp (demux->client,
+ demux_stream->idx, &ts)) {
+ gst_mpd_client_stream_seek (new_client, demux_stream->idx, ts);
+
+ } else
+ if (gst_mpd_client_get_last_fragment_timestamp (demux->client,
+ demux_stream->idx, &ts)) {
+ /* try to set to the old timestamp + 1 */
+ gst_mpd_client_stream_seek (new_client, demux_stream->idx, ts+1);
+ }
+ }
+
+ /*Remember download failed count*/
+ new_client->download_failed_count = demux->client->download_failed_count;
+
+ gst_mpd_client_free (demux->client);
+ demux->client = new_client;
+
+ /* Send an updated duration message */
+ duration =
+ gst_mpd_client_get_media_presentation_duration (demux->client);
+
+ if (duration != GST_CLOCK_TIME_NONE) {
+ GST_DEBUG_OBJECT (demux,
+ "Sending duration message : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration));
+ gst_element_post_message (GST_ELEMENT (demux),
+ gst_message_new_duration(GST_OBJECT (demux), GST_FORMAT_TIME, duration));
+ } else {
+ GST_DEBUG_OBJECT (demux,
+ "mediaPresentationDuration unknown, can not send the duration message");
+ }
+ demux->last_manifest_update = gst_util_get_timestamp ();
+ GST_DEBUG_OBJECT (demux, "Manifest file successfully updated");
+ }
+ }
+ return TRUE;
+}
+
+static void
+gst_dash_demux_download_wait (GstDashDemux * demux, GstClockTime time_diff)
{
- float buffering_time = gst_dash_demux_get_buffering_time (demux);
- if (buffering_time >= demux->min_buffering_time) {
- return 1.0;
- } else
- return buffering_time / demux->min_buffering_time;
+ gint64 end_time = g_get_monotonic_time () + time_diff / GST_USECOND;
+
+ GST_DEBUG_OBJECT (demux, "Download waiting for %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time_diff));
+ g_cond_wait_until (&demux->download_cond, &demux->download_mutex, end_time);
+ GST_DEBUG_OBJECT (demux, "Download finished waiting");
}
-static GstBuffer *
-gst_dash_demux_merge_buffer_list (GstFragment * fragment)
+static void
+gst_dash_demux_check_live(GstDashDemux* demux, GstActiveStream *fragment_stream,
+ GstClockTime fragment_ts)
{
- GstBufferList *list;
- GstBufferListIterator *it;
- GstBuffer *buffer, *ret = NULL;
- GstAdapter *adapter;
- gsize size;
-
- adapter = gst_adapter_new ();
- list = gst_fragment_get_buffer_list (fragment);
- it = gst_buffer_list_iterate (list);
- while (gst_buffer_list_iterator_next_group (it)) {
- while ((buffer = gst_buffer_list_iterator_next (it)) != NULL) {
- gst_adapter_push (adapter, gst_buffer_ref (buffer));
- }
+ gint64 time_diff;
+ gint pos;
+
+ pos =
+ gst_mpd_client_check_time_position (demux->client, fragment_stream,
+ fragment_ts, &time_diff);
+ GST_DEBUG_OBJECT (demux,
+ "Checked position for fragment ts %" GST_TIME_FORMAT
+ ", res: %d, diff: %" G_GINT64_FORMAT, GST_TIME_ARGS (fragment_ts),
+ pos, time_diff);
+
+ time_diff *= GST_USECOND;
+ if (pos < 0) {
+ /* we're behind, try moving to the 'present' */
+ GDateTime *now = g_date_time_new_now_utc ();
+
+ GST_DEBUG_OBJECT (demux,
+ "Falling behind live stream, moving forward");
+ gst_mpd_client_seek_to_time(demux->client, now);
+ g_date_time_unref (now);
+
+ } else if (pos > 0) {
+ /* we're ahead, wait a little */
+
+ GST_DEBUG_OBJECT (demux, "Waiting for next segment to be created");
+ gst_dash_demux_download_wait (demux, time_diff);
+ } else {
+ demux->client->download_failed_count++;
}
- gst_buffer_list_iterator_free (it);
- gst_buffer_list_unref (list);
- size = gst_adapter_available (adapter);
- if (size > 0)
- ret = gst_adapter_take_buffer (adapter, size);
- GST_DEBUG ("Extracted a buffer of size %d from the fragment", size);
- g_object_unref (adapter);
-
- return ret;
}
/* gst_dash_demux_download_loop:
void
gst_dash_demux_download_loop (GstDashDemux * demux)
{
- GstClock *clock = gst_element_get_clock (GST_ELEMENT (demux));
- gint64 update_period = demux->client->mpd_node->minimumUpdatePeriod;
-
- /* Wait until the next scheduled download */
- if (g_cond_timed_wait (GST_TASK_GET_COND (demux->download_task),
- demux->download_timed_lock, &demux->next_download)) {
- goto quit;
+ GstActiveStream *fragment_stream = NULL;
+ GstClockTime fragment_ts;
+ if ( gst_mpd_client_is_live (demux->client) && demux->client->mpd_uri != NULL ) {
+ if (!gst_dash_demux_update_manifest(demux))
+ goto end_of_manifest;
}
- if (clock && gst_mpd_client_is_live (demux->client)
- && demux->client->mpd_uri != NULL && update_period != -1) {
- GstFragment *download;
- GstBuffer *buffer;
- GstClockTime duration, now = gst_clock_get_time (clock);
-
- /* init reference time for manifest file updates */
- if (!GST_CLOCK_TIME_IS_VALID (demux->last_manifest_update))
- demux->last_manifest_update = now;
-
- /* update the manifest file */
- if (now >= demux->last_manifest_update + update_period * GST_MSECOND) {
- GST_DEBUG_OBJECT (demux, "Updating manifest file from URL %s",
- demux->client->mpd_uri);
- download =
- gst_uri_downloader_fetch_uri (demux->downloader,
- demux->client->mpd_uri);
- if (download == NULL) {
- GST_WARNING_OBJECT (demux,
- "Failed to update the manifest file from URL %s",
- demux->client->mpd_uri);
+ /* try to switch to another set of representations if needed */
+ gst_dash_demux_select_representations (demux);
+
+ /* fetch the next fragment */
+ while (!gst_dash_demux_get_next_fragment (demux, &fragment_stream, &fragment_ts)) {
+ if (demux->end_of_period) {
+ GST_INFO_OBJECT (demux, "Reached the end of the Period");
+ /* setup video, audio and subtitle streams, starting from the next Period */
+ if (!gst_mpd_client_set_period_index (demux->client,
+ gst_mpd_client_get_period_index (demux->client) + 1)
+ || !gst_dash_demux_setup_all_streams (demux)) {
+ GST_INFO_OBJECT (demux, "Reached the end of the manifest file");
+ gst_task_start (demux->stream_task);
+ goto end_of_manifest;
+ }
+ /* start playing from the first segment of the new period */
+ gst_mpd_client_set_segment_index_for_all_streams (demux->client, 0);
+ demux->end_of_period = FALSE;
+ } else if (!demux->cancelled) {
+ gst_uri_downloader_reset (demux->downloader);
+ if(gst_mpd_client_is_live (demux->client)) {
+ gst_dash_demux_check_live (demux, fragment_stream, fragment_ts);
} else {
- buffer = gst_dash_demux_merge_buffer_list (download);
- g_object_unref (download);
- /* parse the manifest file */
- if (buffer == NULL) {
- GST_WARNING_OBJECT (demux, "Error validating the manifest.");
- } else if (!gst_mpd_parse (demux->client,
- (gchar *) GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) {
- /* In most cases, this will happen if we set a wrong url in the
- * source element and we have received the 404 HTML response instead of
- * the manifest */
- GST_WARNING_OBJECT (demux, "Error parsing the manifest.");
- gst_buffer_unref (buffer);
- } else {
- GstActiveStream *stream;
- guint segment_index;
-
- gst_buffer_unref (buffer);
- stream = gst_mpdparser_get_active_stream_by_index (demux->client, 0);
- segment_index = gst_mpd_client_get_segment_index (stream);
- /* setup video, audio and subtitle streams, starting from first Period */
- if (!gst_mpd_client_setup_media_presentation (demux->client) ||
- !gst_mpd_client_set_period_index (demux->client,
- gst_mpd_client_get_period_index (demux->client))
- || !gst_dash_demux_setup_all_streams (demux)) {
- GST_DEBUG_OBJECT (demux,
- "Error setting up the updated manifest file");
- goto end_of_manifest;
- }
- /* continue playing from the the next segment */
- /* FIXME: support multiple streams with different segment duration */
- gst_mpd_client_set_segment_index_for_all_streams (demux->client,
- segment_index);
-
- /* Send an updated duration message */
- duration =
- gst_mpd_client_get_media_presentation_duration (demux->client);
-
- if (duration != GST_CLOCK_TIME_NONE) {
- GST_DEBUG_OBJECT (demux,
- "Sending duration message : %" GST_TIME_FORMAT,
- GST_TIME_ARGS (duration));
- gst_element_post_message (GST_ELEMENT (demux),
- gst_message_new_duration (GST_OBJECT (demux), GST_FORMAT_TIME,
- duration));
- } else {
- GST_DEBUG_OBJECT (demux,
- "mediaPresentationDuration unknown, can not send the duration message");
- }
- demux->last_manifest_update += update_period * GST_MSECOND;
- GST_DEBUG_OBJECT (demux, "Manifest file successfully updated");
- }
+ demux->client->download_failed_count++;
}
- }
- }
-
-
- /* Target buffering time MUST at least exceeds mimimum buffering time
- * by the duration of a fragment, but SHOULD NOT exceed maximum
- * buffering time */
- GstClockTime target_buffering_time =
- demux->min_buffering_time +
- gst_mpd_client_get_next_fragment_duration (demux->client);
- if (demux->max_buffering_time > target_buffering_time)
- target_buffering_time = demux->max_buffering_time;
- if (!demux->end_of_manifest
- && gst_dash_demux_get_buffering_time (demux) < target_buffering_time) {
- if (GST_STATE (demux) != GST_STATE_PLAYING) {
- /* Signal our buffering status (this will eventually restart the
- * pipeline when we have reached 100 %) */
- gst_element_post_message (GST_ELEMENT (demux),
- gst_message_new_buffering (GST_OBJECT (demux),
- 100 * gst_dash_demux_get_buffering_ratio (demux)));
- }
- /* try to switch to another set of representations if needed */
- gst_dash_demux_select_representations (demux,
- demux->bandwidth_usage * demux->dnl_rate *
- gst_dash_demux_get_buffering_ratio (demux));
-
- /* fetch the next fragment */
- while (!gst_dash_demux_get_next_fragment_set (demux)) {
- if (demux->end_of_period) {
- GST_INFO_OBJECT (demux, "Reached the end of the Period");
- /* setup video, audio and subtitle streams, starting from the next Period */
- if (!gst_mpd_client_set_period_index (demux->client,
- gst_mpd_client_get_period_index (demux->client) + 1)
- || !gst_dash_demux_setup_all_streams (demux)) {
- GST_INFO_OBJECT (demux, "Reached the end of the manifest file");
- demux->end_of_manifest = TRUE;
- if (GST_STATE (demux) != GST_STATE_PLAYING) {
- /* Restart the pipeline regardless of the current buffering level */
- gst_element_post_message (GST_ELEMENT (demux),
- gst_message_new_buffering (GST_OBJECT (demux), 100));
- }
- gst_task_start (demux->stream_task);
- goto end_of_manifest;
- }
- /* start playing from the first segment of the new period */
- gst_mpd_client_set_segment_index_for_all_streams (demux->client, 0);
- demux->end_of_period = FALSE;
- } else if (!demux->cancelled) {
- demux->client->update_failed_count++;
- if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
- GST_WARNING_OBJECT (demux, "Could not fetch the next fragment");
- goto quit;
- } else {
- goto error_downloading;
- }
- } else {
+ if (demux->client->download_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not fetch the next fragment");
goto quit;
+ } else if (gst_mpd_client_set_next_baseURL_for_stream (demux->client)) {
+ GST_INFO_OBJECT (demux, "Current baseURL is %s. Trying to select another",
+ gst_mpdparser_get_baseURL (demux->client));
+ demux->client->download_failed_count = 0;
+ } else {
+ goto error_downloading;
}
+ } else {
+ goto quit;
}
- GST_INFO_OBJECT (demux, "Internal buffering : %" PRIu64 " s",
- gst_dash_demux_get_buffering_time (demux) / GST_SECOND);
- demux->client->update_failed_count = 0;
- } else {
- /* schedule the next download in 100 ms */
- g_get_current_time (&demux->next_download);
- g_time_val_add (&demux->next_download, 100000);
}
+ GST_INFO_OBJECT (demux, "Internal buffering : %" PRIu64 " s",
+ gst_dash_demux_get_buffering_time (demux) / GST_SECOND);
+ demux->client->download_failed_count = 0;
+
quit:
{
}
static void
-gst_dash_demux_pause_stream_task (GstDashDemux * demux)
-{
- /* Send a signal to the stream task so that it pauses itself */
- GST_TASK_SIGNAL (demux->stream_task);
- /* Pause it explicitly (if it was not in the COND) */
- gst_task_pause (demux->stream_task);
-}
-
-static void
gst_dash_demux_resume_stream_task (GstDashDemux * demux)
{
- g_get_current_time (&demux->next_push);
gst_task_start (demux->stream_task);
}
static void
gst_dash_demux_resume_download_task (GstDashDemux * demux)
{
- g_get_current_time (&demux->next_download);
+ gst_uri_downloader_reset(demux->downloader);
gst_task_start (demux->download_task);
}
* Returns TRUE if a new set of representations has been selected
*/
static gboolean
-gst_dash_demux_select_representations (GstDashDemux * demux, guint64 bitrate)
+gst_dash_demux_select_representations (GstDashDemux * demux)
{
- GstActiveStream *stream = NULL;
+ GstDashDemuxStream *stream = NULL;
+ GstActiveStream *active_stream = NULL;
GList *rep_list = NULL;
+ GSList *iter;
gint new_index;
gboolean ret = FALSE;
- guint i = 0;
- while (i < gst_mpdparser_get_nb_active_stream (demux->client)) {
- stream = gst_mpdparser_get_active_stream_by_index (demux->client, i);
- if (!stream)
+ GST_MPD_CLIENT_LOCK (demux->client);
+ for (iter = demux->streams; iter; iter = g_slist_next (iter)) {
+ guint64 bitrate;
+
+ stream = iter->data;
+ active_stream =
+ gst_mpdparser_get_active_stream_by_index (demux->client, stream->idx);
+ if (!active_stream)
return FALSE;
/* retrieve representation list */
- if (stream->cur_adapt_set)
- rep_list = stream->cur_adapt_set->Representations;
+ if (active_stream->cur_adapt_set)
+ rep_list = active_stream->cur_adapt_set->Representations;
if (!rep_list)
return FALSE;
+ bitrate = gst_download_rate_get_current_rate (&stream->dnl_rate) *
+ demux->bandwidth_usage;
+
+ GST_DEBUG_OBJECT (demux, "Trying to change bitrate to %" G_GUINT64_FORMAT, bitrate);
+
/* get representation index with current max_bandwidth */
new_index =
gst_mpdparser_get_rep_idx_with_max_bandwidth (rep_list, bitrate);
if (new_index == -1)
new_index = 0;
- if (new_index != stream->representation_idx) {
- GST_MPD_CLIENT_LOCK (demux->client);
- ret =
- gst_mpd_client_setup_representation (demux->client, stream,
- g_list_nth_data (rep_list, new_index));
- GST_MPD_CLIENT_UNLOCK (demux->client);
- if (ret) {
+ if (new_index != active_stream->representation_idx) {
+ GstRepresentationNode *rep = g_list_nth_data (rep_list, new_index);
+ GST_INFO_OBJECT (demux, "Changing representation idx: %d %d %u",
+ stream->idx, new_index, rep->bandwidth);
+ if (gst_mpd_client_setup_representation (demux->client, active_stream,
+ rep)) {
+ ret = TRUE;
+ stream->need_header = TRUE;
GST_INFO_OBJECT (demux, "Switching bitrate to %d",
- stream->cur_representation->bandwidth);
+ active_stream->cur_representation->bandwidth);
} else {
GST_WARNING_OBJECT (demux,
"Can not switch representation, aborting...");
}
}
- i++;
}
+ GST_MPD_CLIENT_UNLOCK (demux->client);
return ret;
}
fragment = gst_uri_downloader_fetch_uri (demux->downloader, next_header_uri);
g_free (next_header_uri);
+ g_free (initializationURL);
return fragment;
}
-static GstBufferListItem
-gst_dash_demux_add_buffer_cb (GstBuffer ** buffer,
- guint group, guint idx, gpointer user_data)
-{
- GstFragment *frag = GST_FRAGMENT (user_data);
- /* This buffer still belongs to the original fragment */
- /* so we need to increase refcount */
- gst_fragment_add_buffer (frag, gst_buffer_ref (*buffer));
- return GST_BUFFER_LIST_CONTINUE;
-}
-
-/* Since we cannot add headers after the chunk has been downloaded, we have to recreate a new fragment */
-static GstFragment *
-gst_dash_demux_prepend_header (GstDashDemux * demux,
- GstFragment * frag, GstFragment * header)
-{
- GstFragment *res = gst_fragment_new ();
- res->name = g_strdup (frag->name);
- res->download_start_time = frag->download_start_time;
- res->download_stop_time = frag->download_stop_time;
- res->start_time = frag->start_time;
- res->stop_time = frag->stop_time;
- res->index = frag->index;
- res->discontinuous = frag->discontinuous;
-
- GstBufferList *list;
- list = gst_fragment_get_buffer_list (header);
- gst_buffer_list_foreach (list, gst_dash_demux_add_buffer_cb, res);
- gst_buffer_list_unref (list);
- list = gst_fragment_get_buffer_list (frag);
- gst_buffer_list_foreach (list, gst_dash_demux_add_buffer_cb, res);
- gst_buffer_list_unref (list);
-
- res->completed = TRUE;
-
- return res;
-}
-
static GstCaps *
gst_dash_demux_get_video_input_caps (GstDashDemux * demux,
GstActiveStream * stream)
{
- guint width = 0, height = 0;
+ guint width = 0, height = 0, bandwidth = 0;
const gchar *mimeType = NULL;
GstCaps *caps = NULL;
if (stream == NULL)
return NULL;
#ifdef DASHDEMUX_MODIFICATION
- /* caps need to inlcude resolution */
+ /* caps need to inlcude resolution and bandwidth */
width = gst_mpd_client_get_video_stream_width (stream);
height = gst_mpd_client_get_video_stream_height (stream);
+ bandwidth = gst_mpd_client_get_video_stream_bandwidth (stream);
#else
- /* if bitstreamSwitching is true we dont need to swich pads on resolution change */
+ /* if bitstreamSwitching is true we dont need to swich pads on resolution change */
if (!gst_mpd_client_get_bitstream_switching_flag (stream)) {
width = gst_mpd_client_get_video_stream_width (stream);
height = gst_mpd_client_get_video_stream_height (stream);
G_TYPE_INT, height, NULL);
}
+ if (bandwidth > 0)
+ gst_caps_set_simple (caps, "bandwidth", G_TYPE_INT, bandwidth, NULL);
+
+ gst_caps_set_simple (caps, "max-width", G_TYPE_INT, demux->max_video_width, "max-height",
+ G_TYPE_INT, demux->max_video_height, NULL);
+
/* add ContentProtection to caps */
if ( stream->cur_adapt_set->RepresentationBase->ContentProtection != NULL){
GList *list;
static GstCaps *
gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream)
{
+ GstCaps *caps;
switch (stream->mimeType) {
case GST_STREAM_VIDEO:
- return gst_dash_demux_get_video_input_caps (demux, stream);
+ caps = gst_dash_demux_get_video_input_caps (demux, stream);
+ break;
case GST_STREAM_AUDIO:
- return gst_dash_demux_get_audio_input_caps (demux, stream);
+ caps = gst_dash_demux_get_audio_input_caps (demux, stream);
+ break;
case GST_STREAM_APPLICATION:
- return gst_dash_demux_get_application_input_caps (demux, stream);
+ caps = gst_dash_demux_get_application_input_caps (demux, stream);
+ break;
default:
return GST_CAPS_NONE;
}
-}
-
-static gboolean
-need_add_header (GstDashDemux * demux)
-{
- GstActiveStream *stream;
- GstCaps *caps;
- guint stream_idx = 0;
- gboolean switch_caps = FALSE;
- while (stream_idx < gst_mpdparser_get_nb_active_stream (demux->client)) {
- stream =
- gst_mpdparser_get_active_stream_by_index (demux->client, stream_idx);
- if (stream == NULL)
- return FALSE;
- caps = gst_dash_demux_get_input_caps (demux, stream);
- if (!demux->input_caps[stream_idx]
- || !gst_caps_is_equal (caps, demux->input_caps[stream_idx])) {
- switch_caps = TRUE;
- gst_caps_unref (caps);
- break;
- }
- gst_caps_unref (caps);
- stream_idx++;
- }
- return switch_caps;
+ /*Need to signal downstream elements about dash*/
+ gst_caps_set_simple(caps, "variant", G_TYPE_STRING, "dash-fragmented", NULL);
+ return caps;
}
/* gst_dash_demux_get_next_fragment_set:
*
*/
static gboolean
-gst_dash_demux_get_next_fragment_set (GstDashDemux * demux)
+gst_dash_demux_get_next_fragment (GstDashDemux * demux,GstActiveStream **fragment_stream,
+ GstClockTime *selected_ts)
{
GstActiveStream *stream;
+ GstDashDemuxStream *dash_stream;
+ GstDashDemuxStream *selected_stream = NULL;
GstFragment *download, *header;
- GList *fragment_set;
gchar *next_fragment_uri;
GstClockTime duration;
GstClockTime timestamp;
+ GstClockTime min_timestamp = GST_CLOCK_TIME_NONE;
gboolean discont;
GTimeVal now;
GTimeVal start;
GstClockTime diff;
guint64 size_buffer = 0;
+ GstBuffer *buffer;
+ guint stream_idx;
+ gboolean end_of_period = TRUE;
- g_get_current_time (&start);
- /* Figure out if we will need to switch pads, thus requiring a new
- * header to initialize the new decoding chain
- * FIXME: redundant with needs_pad_switch */
- gboolean need_header = need_add_header (demux);
- guint stream_idx = 0;
- fragment_set = NULL;
- /* Get the fragment corresponding to each stream index */
- while (stream_idx < gst_mpdparser_get_nb_active_stream (demux->client)) {
- if (!gst_mpd_client_get_next_fragment (demux->client,
- stream_idx, &discont, &next_fragment_uri, &duration, ×tamp)) {
- GST_INFO_OBJECT (demux, "This Period doesn't contain more fragments");
- demux->end_of_period = TRUE;
- return FALSE;
+ /*Select stream with smallest progress*/
+ for (stream_idx = 0; stream_idx < g_slist_length (demux->streams); stream_idx++) {
+ dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+
+ if (dash_stream->download_end_of_period)
+ continue;
+
+ if (gst_mpd_client_get_next_fragment_timestamp (demux->client, stream_idx, ×tamp)) {
+ if( timestamp < min_timestamp || !GST_CLOCK_TIME_IS_VALID(min_timestamp) ) {
+ selected_stream = dash_stream;
+ min_timestamp = timestamp;
+ }
+ } else {
+ GstEvent *event = NULL;
+
+ GST_INFO_OBJECT (demux,
+ "This Period doesn't contain more fragments for stream %u",
+ dash_stream->idx);
+
+ /* check if this is live and we should wait for more data */
+ if (gst_mpd_client_is_live (demux->client)
+ && demux->client->mpd_node->minimumUpdatePeriod != -1) {
+ end_of_period = FALSE;
+ continue;
+ }
+
+ if (gst_mpd_client_has_next_period (demux->client)) {
+ event = gst_event_new_dash_eop ();
+ } else {
+ GST_DEBUG_OBJECT (demux,
+ "No more fragments or periods for this stream, setting EOS");
+ event = gst_event_new_eos ();
+ }
+ dash_stream->download_end_of_period = TRUE;
+ gst_dash_demux_stream_push_event (dash_stream, event);
}
+ }
+
+ if (selected_ts)
+ *selected_ts = min_timestamp;
+ if (fragment_stream && selected_stream)
+ *fragment_stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
+ /* Fetch next fragment from selected stream */
+ if(selected_stream) {
- GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri);
+ if (!gst_mpd_client_get_next_fragment (demux->client,
+ selected_stream->idx, &discont, &next_fragment_uri, &duration, ×tamp)) {
+ GST_WARNING_OBJECT (demux, "Failed to download fragment for stream %d", selected_stream->idx);
+ } else {
- download = gst_uri_downloader_fetch_uri (demux->downloader,
- next_fragment_uri);
- g_free (next_fragment_uri);
+ g_get_current_time (&start);
+ GST_INFO_OBJECT (demux, "Fetching next fragment stream=%d ts=%"GST_TIME_FORMAT" url=%s",
+ selected_stream->idx, GST_TIME_ARGS(timestamp), next_fragment_uri);
- if (download == NULL)
- return FALSE;
+ stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
- download->start_time = timestamp;
- download->stop_time = timestamp + duration;
+ end_of_period = FALSE;
- stream =
- gst_mpdparser_get_active_stream_by_index (demux->client, stream_idx);
- if (stream == NULL)
- return FALSE;
+ download = gst_uri_downloader_fetch_uri (demux->downloader,
+ next_fragment_uri);
+ g_free (next_fragment_uri);
+
+ if (stream == NULL)
+ return FALSE;
+
+ if (download == NULL) {
+ guint segment_idx = gst_mpd_client_get_segment_index ( stream );
+ if(segment_idx > 0) {
+ /*Move to previous segment if download failed*/
+ gst_mpd_client_set_segment_index (stream, segment_idx - 1);
+ }
+ return FALSE;
+ }
+
+ download->start_time = timestamp;
+ download->stop_time = timestamp + duration;
- download->index = gst_mpd_client_get_segment_index (stream) - 1;
+ download->index = gst_mpd_client_get_segment_index (stream) - 1;
- GstCaps *caps = gst_dash_demux_get_input_caps (demux, stream);
+ GstCaps *caps = gst_dash_demux_get_input_caps (demux, stream);
+ buffer = gst_fragment_get_buffer (download);
+ g_return_val_if_fail (buffer != NULL, FALSE);
- if (need_header) {
- /* Store the new input caps for that stream */
- gst_caps_replace (&demux->input_caps[stream_idx], caps);
- GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT,
- demux->input_caps[stream_idx]);
- /* We need to fetch a new header */
- if ((header = gst_dash_demux_get_next_header (demux, stream_idx)) == NULL) {
- GST_INFO_OBJECT (demux, "Unable to fetch header");
+ if (selected_stream->need_header) {
+ /* Store the new input caps for that stream */
+ gst_caps_replace (&dash_stream->input_caps, caps);
+ GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT,
+ dash_stream->input_caps);
+
+ /* We need to fetch a new header */
+ if ((header = gst_dash_demux_get_next_header (demux, selected_stream->idx)) == NULL) {
+ GST_INFO_OBJECT (demux, "Unable to fetch header");
+ } else {
+ /* Replace fragment buffer with a new one including the header */
+ GstBuffer *header_buffer = gst_fragment_get_buffer(header);
+ buffer = gst_buffer_join(header_buffer, buffer);
+ g_object_unref (header);
+ selected_stream->need_header = FALSE;
+ }
} else {
- /* Replace fragment with a new one including the header */
- GstFragment *new_fragment =
- gst_dash_demux_prepend_header (demux, download, header);
- g_object_unref (header);
- g_object_unref (download);
- download = new_fragment;
+ gst_caps_unref (caps);
}
- } else
- gst_caps_unref (caps);
- gst_fragment_set_caps (download, demux->input_caps[stream_idx]);
- fragment_set = g_list_append (fragment_set, download);
- size_buffer += gst_fragment_get_buffer_size (download);
- stream_idx++;
+ g_get_current_time (&now);
+ g_object_unref (download);
+
+ gst_buffer_set_caps(buffer, dash_stream->input_caps);
+ GST_BUFFER_TIMESTAMP(buffer) = timestamp;
+ GST_BUFFER_DURATION(buffer) = duration;
+ GST_BUFFER_OFFSET(buffer) = gst_mpd_client_get_segment_index (stream) - 1;
+ size_buffer = GST_BUFFER_SIZE (buffer);
+ /* Push fragment into the queue */
+ gst_dash_demux_stream_push_data (selected_stream, buffer);
+ diff = (GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (start));
+ gst_download_rate_add_rate (&selected_stream->dnl_rate, size_buffer, diff, duration);
+ GST_DEBUG_OBJECT (demux,
+ "Stream: %d Download rate = %" G_GUINT64_FORMAT " Kbits/s (%" G_GUINT64_FORMAT
+ " Ko in %.2f s)\n", selected_stream->idx,
+ gst_download_rate_get_current_rate (&selected_stream->dnl_rate) / 1000,
+ size_buffer / 1024,
+ ((double) diff / GST_SECOND));
+ }
}
- /* Push fragment set into the queue */
- g_queue_push_tail (demux->queue, fragment_set);
- /* Wake the download task up */
- GST_TASK_SIGNAL (demux->download_task);
- g_get_current_time (&now);
- diff = (GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (start));
- demux->dnl_rate = (size_buffer * 8) / ((double) diff / GST_SECOND);
- GST_INFO_OBJECT (demux,
- "Download rate = %" PRIu64 " Kbits/s (%" PRIu64 " Ko in %.2f s)",
- demux->dnl_rate / 1000, size_buffer / 1024, ((double) diff / GST_SECOND));
- return TRUE;
+
+ demux->end_of_period = end_of_period;
+
+ return !end_of_period;
}