From 0abd777257096412e105c95047450e0ebdb04d21 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Wed, 24 Oct 2012 11:49:51 +0200 Subject: [PATCH] dashdemux: complete support for Media Presentations with several Periods - Periods are played in sequence, from PeriodStart to PeriodEnd - seamless switching from one Period to the next one works fine; - the 'new-segment' generation is broken, so if we need to switch pads for a new Period there is a crash; --- ext/dash/gstdashdemux.c | 115 +++++++++++++++++++++++----------------- ext/dash/gstdashdemux.h | 3 +- ext/dash/gstmpdparser.c | 77 +++++++++++++-------------- ext/dash/gstmpdparser.h | 4 ++ 4 files changed, 109 insertions(+), 90 deletions(-) diff --git a/ext/dash/gstdashdemux.c b/ext/dash/gstdashdemux.c index a3051e554b..baea346328 100644 --- a/ext/dash/gstdashdemux.c +++ b/ext/dash/gstdashdemux.c @@ -593,6 +593,38 @@ gst_dash_demux_src_event (GstPad * pad, GstEvent * event) return gst_pad_event_default (pad, event); } +static gboolean +gst_dash_demux_setup_all_streams (GstDashDemux *demux) +{ + GList *listLang = NULL; + guint i, nb_audio; + gchar *lang; + + /* clean old active stream list, if any */ + gst_active_streams_free (demux->client); + + if (!gst_mpd_client_setup_streaming (demux->client, GST_STREAM_VIDEO, "")) + GST_INFO_OBJECT (demux, "No video adaptation set found"); + + nb_audio = gst_mpdparser_get_list_and_nb_of_audio_language (demux->client, &listLang); + if (nb_audio == 0) + nb_audio = 1; + GST_INFO_OBJECT (demux, "Number of language is=%d", nb_audio); + + 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, 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, GST_STREAM_APPLICATION, lang)) + GST_INFO_OBJECT (demux, "No application adaptation set found"); + } + + return TRUE; +} + static gboolean gst_dash_demux_sink_event (GstPad * pad, GstEvent * event) { @@ -646,35 +678,11 @@ gst_dash_demux_sink_event (GstPad * pad, GstEvent * event) ("Incompatible manifest file."), (NULL)); return FALSE; } - - if (!gst_mpd_client_setup_streaming (demux->client, GST_STREAM_VIDEO, "")) { - GST_ELEMENT_ERROR (demux, STREAM, DECODE, - ("Incompatible manifest file."), (NULL)); + /* start from first Period */ + demux->client->period_idx = 0; + /* setup video, audio and subtitle streams */ + if (!gst_dash_demux_setup_all_streams (demux)) return FALSE; - } - - GList *listLang = NULL; - guint nb_audio = - gst_mpdparser_get_list_and_nb_of_audio_language (demux->client, - &listLang); - if (nb_audio == 0) - nb_audio = 1; - GST_INFO_OBJECT (demux, "Number of language is=%d", nb_audio); - guint i = 0; - for (i = 0; i < nb_audio; i++) { - gchar *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, - 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, - GST_STREAM_APPLICATION, lang)) { - GST_INFO_OBJECT (demux, "No application adaptation set found"); - } - } - /* Send duration message */ if (!gst_mpd_client_is_live (demux->client)) { GstClockTime duration = gst_mpd_client_get_duration (demux->client); @@ -1035,6 +1043,7 @@ pause_streaming: static void gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose) { + demux->end_of_period = FALSE; demux->end_of_manifest = FALSE; demux->cancelled = FALSE; @@ -1142,23 +1151,40 @@ gst_dash_demux_download_loop (GstDashDemux * demux) gst_dash_demux_get_buffering_ratio (demux)); /* fetch the next fragment */ - if (!gst_dash_demux_get_next_fragment_set (demux)) { - if (demux->end_of_manifest) { - GST_INFO_OBJECT (demux, "Reached the end of the manifest file"); - goto end_of_manifest; + while (!gst_dash_demux_get_next_fragment_set (demux)) { + if (demux->end_of_period) { + GST_INFO_OBJECT (demux, "Reached the end of the Period"); + /* load the next Period in the Media Presentation */ + if (!gst_mpd_client_get_next_period (demux->client) || !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; + } + /* create a new set of pads and send new_segment events */ + /* FIXME: fix pad switching */ + //demux->need_segment = TRUE; + 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"); - return; - } else + goto quit; + } else { goto error_downloading; + } + } else { + goto quit; } - } else { - GST_INFO_OBJECT (demux, "Internal buffering : %d s", - gst_dash_demux_get_buffering_time (demux) / GST_SECOND); - demux->client->update_failed_count = 0; } + GST_INFO_OBJECT (demux, "Internal buffering : %d 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); @@ -1231,8 +1257,7 @@ gst_dash_demux_select_representations (GstDashDemux * demux, guint64 bitrate) guint i = 0; while (i < gst_mpdparser_get_nb_active_stream (demux->client)) { - if (demux->client->active_streams) - stream = g_list_nth_data (demux->client->active_streams, i); + stream = gst_mpdparser_get_active_stream_by_index (demux->client, i); if (!stream) return FALSE; @@ -1482,14 +1507,8 @@ gst_dash_demux_get_next_fragment_set (GstDashDemux * demux) 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 manifest doesn't contain more fragments"); - 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); + GST_INFO_OBJECT (demux, "This Period doesn't contain more fragments"); + demux->end_of_period = TRUE; return FALSE; } diff --git a/ext/dash/gstdashdemux.h b/ext/dash/gstdashdemux.h index 8e87e749ac..798ad40add 100644 --- a/ext/dash/gstdashdemux.h +++ b/ext/dash/gstdashdemux.h @@ -67,7 +67,8 @@ struct _GstDashDemux GstBuffer *manifest; GstUriDownloader *downloader; GstMpdClient *client; /* MPD client */ - GQueue *queue; /*Video/Audio/Application List of fragment storing the fetched fragments */ + GQueue *queue; /* Video/Audio/Application List of fragment storing the fetched fragments */ + gboolean end_of_period; gboolean end_of_manifest; /* Properties */ diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c index 439199e447..75b02c5019 100644 --- a/ext/dash/gstmpdparser.c +++ b/ext/dash/gstmpdparser.c @@ -85,7 +85,6 @@ static gboolean gst_mpd_client_add_media_segment (GstActiveStream *stream, GstSe static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType); /* Adaptation Set */ -static GstAdaptationSetNode *gst_mpdparser_get_first_adapt_set (GList *AdaptationSets); static GstAdaptationSetNode *gst_mpdparser_get_first_adapt_set_with_mimeType (GList *AdaptationSets, const gchar *mimeType); static GstAdaptationSetNode *gst_mpdparser_get_adapt_set_with_mimeType_and_idx (GList *AdaptationSets, const gchar *mimeType, gint idx); static GstAdaptationSetNode *gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (GList *AdaptationSets, const gchar *mimeType, const gchar *lang); @@ -1572,19 +1571,6 @@ strncmp_ext (const char *s1, const char *s2) } /* navigation functions */ -static GstAdaptationSetNode * -gst_mpdparser_get_first_adapt_set (GList * AdaptationSets) -{ - GList *list = NULL; - - if (AdaptationSets == NULL) - return NULL; - - list = g_list_first (AdaptationSets); - - return list ? (GstAdaptationSetNode *) list->data : NULL; -} - static GstAdaptationSetNode * gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets, const gchar * mimeType) @@ -2359,6 +2345,16 @@ GstMpdClient *gst_mpd_client_new () return client; } +void gst_active_streams_free (GstMpdClient * client) +{ + if (client->active_streams) { + g_list_foreach (client->active_streams, + (GFunc) gst_mpdparser_free_active_stream, NULL); + g_list_free (client->active_streams); + client->active_streams = NULL; + } +} + void gst_mpd_client_free (GstMpdClient * client) { g_return_if_fail (client != NULL); @@ -2372,11 +2368,7 @@ void gst_mpd_client_free (GstMpdClient * client) g_list_free (client->periods); } - if (client->active_streams) { - g_list_foreach (client->active_streams, - (GFunc) gst_mpdparser_free_active_stream, NULL); - g_list_free (client->active_streams); - } + gst_active_streams_free (client); if (client->lock) g_mutex_free (client->lock); @@ -2486,7 +2478,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str { GstStreamPeriod *stream_period; GList *rep_list; - GstClockTime PeriodStart = 0, PeriodEnd, start_time, duration; + GstClockTime PeriodStart, PeriodEnd, start_time, duration; GstMediaSegment *last_media_segment; guint i, start; @@ -2531,7 +2523,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str gst_mpdparser_get_segment_list (stream_period->period, stream->cur_adapt_set, representation)) == NULL) { GST_DEBUG ("No useful SegmentList node for the current Representation"); /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */ - if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0, PeriodEnd)) { + if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, PeriodStart, PeriodEnd)) { return FALSE; } } else { @@ -2545,7 +2537,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str /* build segment list */ i = stream->cur_segment_list->MultSegBaseType->startNumber; start = 0; - start_time = 0; + start_time = PeriodStart; GST_LOG ("Building media segment list using a SegmentList node"); if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) { @@ -2611,7 +2603,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str /* build segment list */ i = stream->cur_seg_template->MultSegBaseType->startNumber; start = 0; - start_time = 0; + start_time = PeriodStart; GST_LOG ("Building media segment list using this template: %s", stream->cur_seg_template->media); if (stream->cur_seg_template->MultSegBaseType->SegmentTimeline) { @@ -2786,12 +2778,8 @@ gst_mpd_client_setup_streaming (GstMpdClient * client, /* select the adaptation set for the video pipeline */ adapt_set = gst_mpdparser_get_adapt_set_with_mimeType_and_idx (stream_period->period->AdaptationSets, "video", 0); - /* if we found no 'video' adaptation set, just get the first one */ - if (!adapt_set) - adapt_set = - gst_mpdparser_get_first_adapt_set (stream_period->period->AdaptationSets); if (!adapt_set) { - GST_INFO ("No adaptation set found, aborting..."); + GST_INFO ("No video adaptation set found"); return FALSE; } /* retrive the list of representations */ @@ -2802,12 +2790,6 @@ gst_mpd_client_setup_streaming (GstMpdClient * client, } break; case GST_STREAM_AUDIO: -#if 0 - if (g_strcmp0 (client->audio_lang, "none") == 0) { - GST_INFO ("Audio stream disabled"); - return FALSE; - } -#endif adapt_set = gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (stream_period->period->AdaptationSets, "audio", lang); /* if we did not found the requested audio language, get the first one */ @@ -2825,12 +2807,6 @@ gst_mpd_client_setup_streaming (GstMpdClient * client, } break; case GST_STREAM_APPLICATION: -#if 0 - if (g_strcmp0 (client->subtitle_lang, "none") == 0) { - GST_INFO ("Subtitles pipeline disabled"); - return FALSE; - } -#endif adapt_set = gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (stream_period->period->AdaptationSets, "application", lang); /* if we did not found the requested subtitles language, get the first one */ @@ -2933,6 +2909,9 @@ gst_mpd_client_get_next_fragment (GstMpdClient * client, } gst_mpd_client_get_current_position (client, timestamp); + *duration = gst_mpd_client_get_target_duration (client); + //*timestamp = currentChunk->start_time; + //*duration = currentChunk->duration; *discontinuity = stream->segment_idx != currentChunk->number; stream->segment_idx += 1; if (mediaURL == NULL) { @@ -2944,7 +2923,6 @@ gst_mpd_client_get_next_fragment (GstMpdClient * client, } else { *uri = mediaURL; } - *duration = gst_mpd_client_get_target_duration (client); GST_MPD_CLIENT_UNLOCK (client); GST_DEBUG ("Loading chunk with URL %s", *uri); @@ -3057,6 +3035,23 @@ gst_mpd_client_get_target_duration (GstMpdClient * client) return duration; } +gboolean +gst_mpd_client_get_next_period (GstMpdClient *client) +{ + GstStreamPeriod *next_stream_period; + + g_return_val_if_fail (client != NULL, FALSE); + g_return_val_if_fail (client->periods != NULL, FALSE); + + next_stream_period = g_list_nth_data (client->periods, client->period_idx + 1); + if (next_stream_period == NULL) + return FALSE; + + client->period_idx++; + + return TRUE; +} + gboolean gst_mpd_client_is_live (GstMpdClient * client) { diff --git a/ext/dash/gstmpdparser.h b/ext/dash/gstmpdparser.h index 0a8f3fe365..b061f5d039 100644 --- a/ext/dash/gstmpdparser.h +++ b/ext/dash/gstmpdparser.h @@ -457,6 +457,7 @@ struct _GstMpdClient /* Basic initialization/deinitialization functions */ GstMpdClient *gst_mpd_client_new (); +void gst_active_streams_free (GstMpdClient * client); void gst_mpd_client_free (GstMpdClient * client); /* MPD file parsing */ @@ -473,6 +474,9 @@ gboolean gst_mpd_client_get_next_fragment (GstMpdClient *client, guint indexStre gboolean gst_mpd_client_get_next_header (GstMpdClient *client, const gchar **uri, guint stream_idx); gboolean gst_mpd_client_is_live (GstMpdClient * client); +/* Period selection */ +gboolean gst_mpd_client_get_next_period (GstMpdClient *client); + /* Representation selection */ gint gst_mpdparser_get_rep_idx_with_max_bandwidth (GList *Representations, gint max_bandwidth);