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;
This commit is contained in:
Gianluca Gennari 2012-10-24 11:49:51 +02:00 committed by Thiago Santos
parent 9d09e99ebe
commit 0abd777257
4 changed files with 109 additions and 90 deletions

View file

@ -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, &timestamp)) {
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;
}

View file

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

View file

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

View file

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