dashdemux: add support for manifest file updates
authorGianluca Gennari <gennarone@gmail.com>
Mon, 17 Dec 2012 14:12:58 +0000 (15:12 +0100)
committerThiago Santos <thiago.sousa.santos@collabora.com>
Wed, 8 May 2013 21:14:32 +0000 (18:14 -0300)
- the MPD file is updated in the download loop (only if we have a "dynamic" MPD and minimumUpdatePeriod is valid);
- properly LOCK/UNLOCK the GstMpdClient;

ext/dash/gstdashdemux.c
ext/dash/gstdashdemux.h
ext/dash/gstmpdparser.c

index 0321f47..29a66eb 100644 (file)
@@ -213,6 +213,7 @@ static gboolean gst_dash_demux_get_next_fragment_set (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)
@@ -1092,6 +1093,7 @@ gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose)
 
   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;
@@ -1131,6 +1133,34 @@ gst_dash_demux_get_buffering_ratio (GstDashDemux * demux)
     return buffering_time / demux->min_buffering_time;
 }
 
+static GstBuffer *
+gst_dash_demux_merge_buffer_list (GstFragment *fragment)
+{
+  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));
+    }
+  }
+  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:
  * 
  * Loop for the "download' task that fetches fragments based on the 
@@ -1162,12 +1192,72 @@ gst_dash_demux_get_buffering_ratio (GstDashDemux * demux)
 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;
   }
 
+  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);
+      } 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 {
+          gst_buffer_unref (buffer);
+          /* 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;
+          }
+          /* 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");
+        }
+      }
+    }
+  }
+
+
   /* Target buffering time MUST at least exceeds mimimum buffering time 
    * by the duration of a fragment, but SHOULD NOT exceed maximum
    * buffering time */
index 798ad40..7150968 100644 (file)
@@ -90,6 +90,9 @@ struct _GstDashDemux
   GMutex *download_timed_lock;
   GTimeVal next_download;       /* Time of the next download */
 
+  /* Manifest update */
+  GstClockTime last_manifest_update;
+
   /* Position in the stream */
   GstClockTime position;
   GstClockTime position_shift;
index df267f2..58c6ffe 100644 (file)
@@ -2422,6 +2422,7 @@ gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
 
     GST_DEBUG ("MPD file fully buffered, start parsing...");
 
+    GST_MPD_CLIENT_LOCK (client);
     /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
 
     /* this initialize the library and check potential ABI mismatches
@@ -2434,6 +2435,7 @@ gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
     doc = xmlReadMemory (data, size, "noname.xml", NULL, 0);
     if (doc == NULL) {
       GST_ERROR ("failed to parse the MPD file");
+      GST_MPD_CLIENT_UNLOCK (client);
       return FALSE;
     } else {
       /* get the root element node */
@@ -2453,6 +2455,7 @@ gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
       /* dump XML library memory for debugging */
       xmlMemoryDump ();
     }
+    GST_MPD_CLIENT_UNLOCK (client);
 
     return TRUE;
   }