dashdemux: complete support for Media Presentations with several Periods
authorGianluca Gennari <gennarone@gmail.com>
Wed, 24 Oct 2012 09:49:51 +0000 (11:49 +0200)
committerThiago Santos <thiago.sousa.santos@collabora.com>
Wed, 8 May 2013 21:14:10 +0000 (18:14 -0300)
- 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
ext/dash/gstdashdemux.h
ext/dash/gstmpdparser.c
ext/dash/gstmpdparser.h

index a3051e5..baea346 100644 (file)
@@ -594,6 +594,38 @@ gst_dash_demux_src_event (GstPad * pad, GstEvent * 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)
 {
   GstDashDemux *demux = GST_DASH_DEMUX (gst_pad_get_parent (pad));
@@ -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;
     }
 
index 8e87e74..798ad40 100644 (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 */
index 439199e..75b02c5 100644 (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);
@@ -1573,19 +1572,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);
@@ -3058,6 +3036,23 @@ gst_mpd_client_get_target_duration (GstMpdClient * client)
 }
 
 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)
 {
   g_return_val_if_fail (client != NULL, FALSE);
index 0a8f3fe..b061f5d 100644 (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);