From 6b63a7dc81d17178e6641c4b76b3d5fd681eadb2 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Fri, 16 Nov 2012 17:29:38 -0300 Subject: [PATCH] mssdemux: more manifest parsing and helper functions Now the mss manifest is able to generate the files urls --- ext/smoothstreaming/gstmssmanifest.c | 176 +++++++++++++++++++++++++++++++++-- ext/smoothstreaming/gstmssmanifest.h | 2 + 2 files changed, 170 insertions(+), 8 deletions(-) diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c index f71270f..1bfbe64 100644 --- a/ext/smoothstreaming/gstmssmanifest.c +++ b/ext/smoothstreaming/gstmssmanifest.c @@ -27,11 +27,41 @@ #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) { diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h index bbe907d..e3e727f 100644 --- a/ext/smoothstreaming/gstmssmanifest.h +++ b/ext/smoothstreaming/gstmssmanifest.h @@ -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); -- 2.7.4