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); 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 static gboolean
gst_dash_demux_sink_event (GstPad * pad, GstEvent * event) 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)); ("Incompatible manifest file."), (NULL));
return FALSE; return FALSE;
} }
/* start from first Period */
if (!gst_mpd_client_setup_streaming (demux->client, GST_STREAM_VIDEO, "")) { demux->client->period_idx = 0;
GST_ELEMENT_ERROR (demux, STREAM, DECODE, /* setup video, audio and subtitle streams */
("Incompatible manifest file."), (NULL)); if (!gst_dash_demux_setup_all_streams (demux))
return FALSE; 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 */ /* Send duration message */
if (!gst_mpd_client_is_live (demux->client)) { if (!gst_mpd_client_is_live (demux->client)) {
GstClockTime duration = gst_mpd_client_get_duration (demux->client); GstClockTime duration = gst_mpd_client_get_duration (demux->client);
@ -1035,6 +1043,7 @@ pause_streaming:
static void static void
gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose) gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose)
{ {
demux->end_of_period = FALSE;
demux->end_of_manifest = FALSE; demux->end_of_manifest = FALSE;
demux->cancelled = FALSE; demux->cancelled = FALSE;
@ -1142,23 +1151,40 @@ gst_dash_demux_download_loop (GstDashDemux * demux)
gst_dash_demux_get_buffering_ratio (demux)); gst_dash_demux_get_buffering_ratio (demux));
/* fetch the next fragment */ /* fetch the next fragment */
if (!gst_dash_demux_get_next_fragment_set (demux)) { while (!gst_dash_demux_get_next_fragment_set (demux)) {
if (demux->end_of_manifest) { 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"); 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; 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) { } else if (!demux->cancelled) {
demux->client->update_failed_count++; demux->client->update_failed_count++;
if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
GST_WARNING_OBJECT (demux, "Could not fetch the next fragment"); GST_WARNING_OBJECT (demux, "Could not fetch the next fragment");
return; goto quit;
} else } else {
goto error_downloading; goto error_downloading;
} }
} else { } else {
goto quit;
}
}
GST_INFO_OBJECT (demux, "Internal buffering : %d s", GST_INFO_OBJECT (demux, "Internal buffering : %d s",
gst_dash_demux_get_buffering_time (demux) / GST_SECOND); gst_dash_demux_get_buffering_time (demux) / GST_SECOND);
demux->client->update_failed_count = 0; demux->client->update_failed_count = 0;
}
} else { } else {
/* schedule the next download in 100 ms */ /* schedule the next download in 100 ms */
g_get_current_time (&demux->next_download); g_get_current_time (&demux->next_download);
@ -1231,8 +1257,7 @@ gst_dash_demux_select_representations (GstDashDemux * demux, guint64 bitrate)
guint i = 0; guint i = 0;
while (i < gst_mpdparser_get_nb_active_stream (demux->client)) { while (i < gst_mpdparser_get_nb_active_stream (demux->client)) {
if (demux->client->active_streams) stream = gst_mpdparser_get_active_stream_by_index (demux->client, i);
stream = g_list_nth_data (demux->client->active_streams, i);
if (!stream) if (!stream)
return FALSE; 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)) { while (stream_idx < gst_mpdparser_get_nb_active_stream (demux->client)) {
if (!gst_mpd_client_get_next_fragment (demux->client, if (!gst_mpd_client_get_next_fragment (demux->client,
stream_idx, &discont, &next_fragment_uri, &duration, &timestamp)) { stream_idx, &discont, &next_fragment_uri, &duration, &timestamp)) {
GST_INFO_OBJECT (demux, "This manifest doesn't contain more fragments"); GST_INFO_OBJECT (demux, "This Period doesn't contain more fragments");
demux->end_of_manifest = TRUE; demux->end_of_period = 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);
return FALSE; return FALSE;
} }

View file

@ -67,7 +67,8 @@ struct _GstDashDemux
GstBuffer *manifest; GstBuffer *manifest;
GstUriDownloader *downloader; GstUriDownloader *downloader;
GstMpdClient *client; /* MPD client */ 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; gboolean end_of_manifest;
/* Properties */ /* 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); static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
/* Adaptation Set */ /* 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_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_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); 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 */ /* 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 * static GstAdaptationSetNode *
gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets, gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets,
const gchar * mimeType) const gchar * mimeType)
@ -2359,6 +2345,16 @@ GstMpdClient *gst_mpd_client_new ()
return client; 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) void gst_mpd_client_free (GstMpdClient * client)
{ {
g_return_if_fail (client != NULL); g_return_if_fail (client != NULL);
@ -2372,11 +2368,7 @@ void gst_mpd_client_free (GstMpdClient * client)
g_list_free (client->periods); g_list_free (client->periods);
} }
if (client->active_streams) { gst_active_streams_free (client);
g_list_foreach (client->active_streams,
(GFunc) gst_mpdparser_free_active_stream, NULL);
g_list_free (client->active_streams);
}
if (client->lock) if (client->lock)
g_mutex_free (client->lock); g_mutex_free (client->lock);
@ -2486,7 +2478,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str
{ {
GstStreamPeriod *stream_period; GstStreamPeriod *stream_period;
GList *rep_list; GList *rep_list;
GstClockTime PeriodStart = 0, PeriodEnd, start_time, duration; GstClockTime PeriodStart, PeriodEnd, start_time, duration;
GstMediaSegment *last_media_segment; GstMediaSegment *last_media_segment;
guint i, start; 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_mpdparser_get_segment_list (stream_period->period, stream->cur_adapt_set, representation)) == NULL) {
GST_DEBUG ("No useful SegmentList node for the current Representation"); 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 */ /* 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; return FALSE;
} }
} else { } else {
@ -2545,7 +2537,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str
/* build segment list */ /* build segment list */
i = stream->cur_segment_list->MultSegBaseType->startNumber; i = stream->cur_segment_list->MultSegBaseType->startNumber;
start = 0; start = 0;
start_time = 0; start_time = PeriodStart;
GST_LOG ("Building media segment list using a SegmentList node"); GST_LOG ("Building media segment list using a SegmentList node");
if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) { if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
@ -2611,7 +2603,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, GstActiveStream *str
/* build segment list */ /* build segment list */
i = stream->cur_seg_template->MultSegBaseType->startNumber; i = stream->cur_seg_template->MultSegBaseType->startNumber;
start = 0; start = 0;
start_time = 0; start_time = PeriodStart;
GST_LOG ("Building media segment list using this template: %s", stream->cur_seg_template->media); GST_LOG ("Building media segment list using this template: %s", stream->cur_seg_template->media);
if (stream->cur_seg_template->MultSegBaseType->SegmentTimeline) { 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 */ /* select the adaptation set for the video pipeline */
adapt_set = adapt_set =
gst_mpdparser_get_adapt_set_with_mimeType_and_idx (stream_period->period->AdaptationSets, "video", 0); 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) { if (!adapt_set) {
GST_INFO ("No adaptation set found, aborting..."); GST_INFO ("No video adaptation set found");
return FALSE; return FALSE;
} }
/* retrive the list of representations */ /* retrive the list of representations */
@ -2802,12 +2790,6 @@ gst_mpd_client_setup_streaming (GstMpdClient * client,
} }
break; break;
case GST_STREAM_AUDIO: case GST_STREAM_AUDIO:
#if 0
if (g_strcmp0 (client->audio_lang, "none") == 0) {
GST_INFO ("Audio stream disabled");
return FALSE;
}
#endif
adapt_set = adapt_set =
gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (stream_period->period->AdaptationSets, "audio", lang); 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 */ /* 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; break;
case GST_STREAM_APPLICATION: case GST_STREAM_APPLICATION:
#if 0
if (g_strcmp0 (client->subtitle_lang, "none") == 0) {
GST_INFO ("Subtitles pipeline disabled");
return FALSE;
}
#endif
adapt_set = adapt_set =
gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (stream_period->period->AdaptationSets, "application", lang); 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 */ /* 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); 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; *discontinuity = stream->segment_idx != currentChunk->number;
stream->segment_idx += 1; stream->segment_idx += 1;
if (mediaURL == NULL) { if (mediaURL == NULL) {
@ -2944,7 +2923,6 @@ gst_mpd_client_get_next_fragment (GstMpdClient * client,
} else { } else {
*uri = mediaURL; *uri = mediaURL;
} }
*duration = gst_mpd_client_get_target_duration (client);
GST_MPD_CLIENT_UNLOCK (client); GST_MPD_CLIENT_UNLOCK (client);
GST_DEBUG ("Loading chunk with URL %s", *uri); GST_DEBUG ("Loading chunk with URL %s", *uri);
@ -3057,6 +3035,23 @@ gst_mpd_client_get_target_duration (GstMpdClient * client)
return duration; 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 gboolean
gst_mpd_client_is_live (GstMpdClient * client) gst_mpd_client_is_live (GstMpdClient * client)
{ {

View file

@ -457,6 +457,7 @@ struct _GstMpdClient
/* Basic initialization/deinitialization functions */ /* Basic initialization/deinitialization functions */
GstMpdClient *gst_mpd_client_new (); GstMpdClient *gst_mpd_client_new ();
void gst_active_streams_free (GstMpdClient * client);
void gst_mpd_client_free (GstMpdClient * client); void gst_mpd_client_free (GstMpdClient * client);
/* MPD file parsing */ /* 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_get_next_header (GstMpdClient *client, const gchar **uri, guint stream_idx);
gboolean gst_mpd_client_is_live (GstMpdClient * client); gboolean gst_mpd_client_is_live (GstMpdClient * client);
/* Period selection */
gboolean gst_mpd_client_get_next_period (GstMpdClient *client);
/* Representation selection */ /* Representation selection */
gint gst_mpdparser_get_rep_idx_with_max_bandwidth (GList *Representations, gint max_bandwidth); gint gst_mpdparser_get_rep_idx_with_max_bandwidth (GList *Representations, gint max_bandwidth);