mssdemux: more manifest parsing and helper functions
authorThiago Santos <thiago.sousa.santos@collabora.com>
Fri, 16 Nov 2012 20:29:38 +0000 (17:29 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.com>
Wed, 8 May 2013 00:05:10 +0000 (21:05 -0300)
Now the mss manifest is able to generate the files urls

ext/smoothstreaming/gstmssmanifest.c
ext/smoothstreaming/gstmssmanifest.h

index f71270f..1bfbe64 100644 (file)
 
 #include "gstmssmanifest.h"
 
+#define MSS_NODE_STREAM_FRAGMENT      "c"
+#define MSS_NODE_STREAM_QUALITY       "QualityLevel"
+
+#define MSS_PROP_BITRATE              "Bitrate"
+#define MSS_PROP_DURATION             "d"
+#define MSS_PROP_NUMBER               "n"
+#define MSS_PROP_TIME                 "t"
+#define MSS_PROP_URL                  "Url"
+
+/* TODO check if atoi is successful? */
+
+typedef struct _GstMssManifestStreamFragment
+{
+  guint number;
+  guint64 time;
+  guint64 duration;
+} GstMssManifestStreamFragment;
+
 struct _GstMssManifestStream
 {
   xmlNodePtr xmlnode;
 
   gint selectedQualityIndex;
+
+  GList *fragments;
+  GList *qualities;
+
+  gchar *url;
+
+  GList *current_fragment;
+  GList *current_quality;
+
+  /* TODO move this to somewhere static */
+  GRegex *regex_bitrate;
+  GRegex *regex_position;
 };
 
 struct _GstMssManifest
@@ -42,6 +72,89 @@ struct _GstMssManifest
   GSList *streams;
 };
 
+static gboolean
+node_has_type (xmlNodePtr node, const gchar * name)
+{
+  return strcmp ((gchar *) node->name, name) == 0;
+}
+
+static void
+_gst_mss_manifest_stream_init (GstMssManifestStream * stream, xmlNodePtr node)
+{
+  xmlNodePtr iter;
+  GstMssManifestStreamFragment *previous_fragment = NULL;
+  guint fragment_number = 0;
+  guint fragment_time_accum = 0;
+  GError *gerror = NULL;
+
+  stream->xmlnode = node;
+
+  /* get the base url path generator */
+  stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
+
+  for (iter = node->children; iter; iter = iter->next) {
+    if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
+      gchar *duration_str;
+      gchar *time_str;
+      gchar *seqnum_str;
+      GstMssManifestStreamFragment *fragment =
+          g_new (GstMssManifestStreamFragment, 1);
+
+      duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
+      time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
+      seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
+
+      /* use the node's seq number or use the previous + 1 */
+      if (seqnum_str) {
+        fragment->number = atoi (seqnum_str);
+        g_free (seqnum_str);
+      } else {
+        fragment->number = fragment_number;
+      }
+      fragment_number = fragment->number + 1;
+
+      if (time_str) {
+        fragment->time = atoi (time_str);
+        g_free (time_str);
+      } else {
+        fragment->time = fragment_time_accum;
+      }
+
+      /* if we have a previous fragment, means we need to set its duration */
+      if (previous_fragment)
+        previous_fragment->duration = fragment->time - previous_fragment->time;
+
+      if (duration_str) {
+        fragment->duration = atoi (duration_str);
+
+        previous_fragment = NULL;
+        fragment_time_accum += fragment->duration;
+        g_free (duration_str);
+      } else {
+        /* store to set the duration at the next iteration */
+        previous_fragment = fragment;
+      }
+
+      /* we reverse it later */
+      stream->fragments = g_list_prepend (stream->fragments, fragment);
+
+    } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
+      stream->qualities = g_list_prepend (stream->qualities, iter);
+    } else {
+      /* TODO gst log this */
+    }
+  }
+
+  stream->fragments = g_list_reverse (stream->fragments);
+  stream->qualities = g_list_reverse (stream->qualities);
+
+  stream->current_fragment = stream->fragments;
+  stream->current_quality = stream->qualities;
+
+  stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, &gerror);
+  stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, &gerror);
+}
+
 GstMssManifest *
 gst_mss_manifest_new (const GstBuffer * data)
 {
@@ -61,19 +174,31 @@ gst_mss_manifest_new (const GstBuffer * data)
       GstMssManifestStream *stream = g_new0 (GstMssManifestStream, 1);
 
       manifest->streams = g_slist_append (manifest->streams, stream);
-      stream->xmlnode = nodeiter;
+      _gst_mss_manifest_stream_init (stream, nodeiter);
     }
   }
 
   return manifest;
 }
 
+static void
+gst_mss_manifest_stream_free (GstMssManifestStream * stream)
+{
+  g_list_free_full (stream->fragments, g_free);
+  g_list_free (stream->qualities);
+  g_free (stream->url);
+  g_regex_unref (stream->regex_position);
+  g_regex_unref (stream->regex_bitrate);
+  g_free (stream);
+}
+
 void
 gst_mss_manifest_free (GstMssManifest * manifest)
 {
   g_return_if_fail (manifest != NULL);
 
-  g_slist_free_full (manifest->streams, g_free);
+  g_slist_free_full (manifest->streams,
+      (GDestroyNotify) gst_mss_manifest_stream_free);
 
   xmlFreeDoc (manifest->xml);
   g_free (manifest);
@@ -208,12 +333,7 @@ gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream)
 {
   GstMssManifestStreamType streamtype =
       gst_mss_manifest_stream_get_type (stream);
-
-  /* TODO properly get the stream */
-  xmlNodePtr qualitylevel = stream->xmlnode->children;
-  while (strcmp ((gchar *) qualitylevel->name, "QualityLevel")) {
-    qualitylevel = qualitylevel->next;
-  }
+  xmlNodePtr qualitylevel = stream->current_quality->data;
 
   if (streamtype == MSS_STREAM_TYPE_VIDEO)
     return
@@ -227,6 +347,46 @@ gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream)
   return NULL;
 }
 
+GstFlowReturn
+gst_mss_manifest_stream_get_fragment_url (GstMssManifestStream * stream,
+    gchar ** url)
+{
+  gchar *tmp;
+  gchar *bitrate_str;
+  gchar *start_time_str;
+  GstMssManifestStreamFragment *fragment = stream->current_fragment->data;
+
+  if (stream->current_fragment == NULL) /* stream is over */
+    return GST_FLOW_UNEXPECTED;
+
+  bitrate_str =
+      (gchar *) xmlGetProp (stream->current_quality->data,
+      (xmlChar *) MSS_PROP_BITRATE);
+  start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, fragment->time);
+
+  tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
+      strlen (stream->url), 0, bitrate_str, 0, NULL);
+  *url = g_regex_replace_literal (stream->regex_position, tmp,
+      strlen (tmp), 0, start_time_str, 0, NULL);
+
+  g_free (tmp);
+  g_free (start_time_str);
+  g_free (bitrate_str);
+  return GST_FLOW_OK;
+}
+
+GstFlowReturn
+gst_mss_manifest_stream_advance_fragment (GstMssManifestStream * stream)
+{
+  if (stream->current_fragment == NULL)
+    return GST_FLOW_UNEXPECTED;
+
+  stream->current_fragment = g_list_next (stream->current_fragment);
+  if (stream->current_fragment == NULL);
+  return GST_FLOW_UNEXPECTED;
+  return GST_FLOW_OK;
+}
+
 const gchar *
 gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype)
 {
index bbe907d..e3e727f 100644 (file)
@@ -44,6 +44,8 @@ GSList * gst_mss_manifest_get_streams (GstMssManifest * manifest);
 
 GstMssManifestStreamType gst_mss_manifest_stream_get_type (GstMssManifestStream *stream);
 GstCaps * gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream);
+GstFlowReturn gst_mss_manifest_stream_get_fragment_url (GstMssManifestStream * stream, gchar ** url);
+GstFlowReturn gst_mss_manifest_stream_advance_fragment (GstMssManifestStream * stream);
 
 const gchar * gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype);