summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2015-08-21 16:40:10 +0300
committerSebastian Dröge <sebastian@centricular.com>2015-10-02 11:01:29 +0300
commit2fc565690183a3e2db7008717120800d5499a860 (patch)
treeaf60615b533dbd1d3aa1f3a5940729e60a62a7a2
parentbdf5f252c17e71b729c0f84d2f9e970fda7bfdeb (diff)
downloadgstreamer-plugins-bad-2fc565690183a3e2db7008717120800d5499a860.tar.gz
mpdparser: Implement loading of external Period nodes
The same has to be done for AdaptationSet and SegmentList nodes still. Also this does not correctly implement the semantics: by default Period (and other nodes) should only be loaded when needed, not in the very beginning. We need to implement lazy loading for them, which means adjusting gst_mpd_client_setup_media_presentation(). https://bugzilla.gnome.org/show_bug.cgi?id=752230
-rw-r--r--ext/dash/gstmpdparser.c203
1 files changed, 190 insertions, 13 deletions
diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c
index 5d596425d..10195b3d3 100644
--- a/ext/dash/gstmpdparser.c
+++ b/ext/dash/gstmpdparser.c
@@ -2895,14 +2895,13 @@ gst_mpdparser_clone_URL (GstURLType * url)
* baseURLs. Takes ownership of base and returns a new base.
*/
static GstUri *
-combine_urls (GstUri * base, GList * list, gchar ** query,
- GstActiveStream * stream)
+combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
{
GstBaseURL *baseURL;
GstUri *ret = base;
if (list != NULL) {
- baseURL = g_list_nth_data (list, stream->baseURL_idx);
+ baseURL = g_list_nth_data (list, idx);
if (!baseURL) {
baseURL = list->data;
}
@@ -2949,22 +2948,26 @@ gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
/* combine a BaseURL at the MPD level with the current base url */
- abs_url = combine_urls (abs_url, client->mpd_node->BaseURLs, query, stream);
+ abs_url =
+ combine_urls (abs_url, client->mpd_node->BaseURLs, query,
+ stream->baseURL_idx);
/* combine a BaseURL at the Period level with the current base url */
abs_url =
- combine_urls (abs_url, stream_period->period->BaseURLs, query, stream);
+ combine_urls (abs_url, stream_period->period->BaseURLs, query,
+ stream->baseURL_idx);
GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
stream->cur_adapt_set->contentType);
/* combine a BaseURL at the AdaptationSet level with the current base url */
abs_url =
- combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query, stream);
+ combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
+ stream->baseURL_idx);
/* combine a BaseURL at the Representation level with the current base url */
abs_url =
combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
- stream);
+ stream->baseURL_idx);
ret = gst_uri_to_string (abs_url);
gst_uri_unref (abs_url);
@@ -3492,11 +3495,106 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
return TRUE;
}
+static GList *
+gst_mpd_client_fetch_external_period (GstMpdClient * client,
+ GstPeriodNode * period_node, gboolean * error)
+{
+ GstFragment *download;
+ GstBuffer *period_buffer;
+ GstMapInfo map;
+ GError *err = NULL;
+ xmlDocPtr doc;
+ GstUri *base_uri, *uri;
+ gchar *query = NULL;
+ gchar *uri_string;
+ GList *new_periods = NULL;
+
+ *error = FALSE;
+
+ /* ISO/IEC 23009-1:2014 5.5.3 4)
+ * Remove nodes that resolve to nothing when resolving
+ */
+ if (strcmp (period_node->xlink_href,
+ "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
+ return NULL;
+ }
+
+ if (!client->downloader) {
+ *error = TRUE;
+ return NULL;
+ }
+
+ /* Build absolute URI */
+
+ /* Get base URI at the MPD level */
+ base_uri =
+ gst_uri_from_string (client->
+ mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
+
+ /* combine a BaseURL at the MPD level with the current base url */
+ base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
+ uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
+ if (query)
+ gst_uri_set_query_string (uri, query);
+ g_free (query);
+ uri_string = gst_uri_to_string (uri);
+ gst_uri_unref (base_uri);
+ gst_uri_unref (uri);
+
+ download =
+ gst_uri_downloader_fetch_uri (client->downloader,
+ uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
+ g_free (uri_string);
+
+ if (!download) {
+ GST_ERROR ("Failed to download external Period node at '%s': %s",
+ period_node->xlink_href, err->message);
+ g_clear_error (&err);
+ *error = TRUE;
+ return NULL;
+ }
+
+ period_buffer = gst_fragment_get_buffer (download);
+ g_object_unref (download);
+
+ gst_buffer_map (period_buffer, &map, GST_MAP_READ);
+
+ doc =
+ xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
+ XML_PARSE_NONET);
+ if (doc) {
+ xmlNode *root_element = xmlDocGetRootElement (doc);
+ if (root_element->type != XML_ELEMENT_NODE ||
+ xmlStrcmp (root_element->name, (xmlChar *) "Period") != 0) {
+ xmlFreeDoc (doc);
+ gst_buffer_unmap (period_buffer, &map);
+ gst_buffer_unref (period_buffer);
+ *error = TRUE;
+ return NULL;
+ }
+
+ gst_mpdparser_parse_period_node (&new_periods, root_element);
+ } else {
+ GST_ERROR ("Failed to parse period node XML");
+ gst_buffer_unmap (period_buffer, &map);
+ gst_buffer_unref (period_buffer);
+ *error = TRUE;
+ return NULL;
+ }
+ gst_buffer_unmap (period_buffer, &map);
+ gst_buffer_unref (period_buffer);
+
+ return new_periods;
+}
+
+/* TODO: Implement xlink actuation onRequest properly. Currently we download
+ * each external MPD immediately when iterating over the periods. We should
+ * do this only when actually switching to this period.
+ */
gboolean
gst_mpd_client_setup_media_presentation (GstMpdClient * client)
{
GstStreamPeriod *stream_period;
- GstPeriodNode *period_node;
GstClockTime start, duration;
GList *list, *next;
guint idx;
@@ -3517,9 +3615,51 @@ gst_mpd_client_setup_media_presentation (GstMpdClient * client)
idx = 0;
start = 0;
duration = GST_CLOCK_TIME_NONE;
- for (list = g_list_first (client->mpd_node->Periods); list;
- list = g_list_next (list)) {
- period_node = (GstPeriodNode *) list->data;
+ for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
+ GstPeriodNode *period_node = list->data;
+ GstPeriodNode *next_period_node = NULL;
+
+ /* Download external period */
+ if (period_node->xlink_href) {
+ GList *new_periods;
+ gboolean error = FALSE;
+ GList *prev;
+
+ new_periods =
+ gst_mpd_client_fetch_external_period (client, period_node, &error);
+ if (!new_periods && error)
+ goto syntax_error;
+
+ prev = list->prev;
+ client->mpd_node->Periods =
+ g_list_delete_link (client->mpd_node->Periods, list);
+ gst_mpdparser_free_period_node (period_node);
+ period_node = NULL;
+
+ /* Get new next node, we will insert before this */
+ if (prev)
+ next = prev->next;
+ else
+ next = client->mpd_node->Periods;
+
+ while (new_periods) {
+ client->mpd_node->Periods =
+ g_list_insert_before (client->mpd_node->Periods, next,
+ new_periods->data);
+ new_periods = g_list_delete_link (new_periods, new_periods);
+ }
+ next = NULL;
+
+ /* Update our iterator to the first new period if any, or the next */
+ if (prev)
+ list = prev->next;
+ else
+ list = client->mpd_node->Periods;
+
+ /* And try again */
+ continue;
+ }
+
if (period_node->start != -1) {
/* we have a regular period */
/* start cannot be smaller than previous start */
@@ -3551,9 +3691,44 @@ gst_mpd_client_setup_media_presentation (GstMpdClient * client)
"The Period extends until the PeriodStart of the next Period, or until
the end of the Media Presentation in the case of the last Period."
*/
- if ((next = g_list_next (list)) != NULL) {
+
+ while ((next = g_list_next (list)) != NULL) {
/* try to infer this period duration from the start time of the next period */
- GstPeriodNode *next_period_node = next->data;
+ next_period_node = next->data;
+
+ if (next_period_node->xlink_href) {
+ gboolean next_error;
+ GList *new_periods;
+
+ new_periods =
+ gst_mpd_client_fetch_external_period (client, next_period_node,
+ &next_error);
+
+ if (!new_periods && next_error)
+ goto syntax_error;
+
+ client->mpd_node->Periods =
+ g_list_delete_link (client->mpd_node->Periods, next);
+ gst_mpdparser_free_period_node (next_period_node);
+ next_period_node = NULL;
+ /* Get new next node, we will insert before this */
+ next = g_list_next (list);
+ while (new_periods) {
+ client->mpd_node->Periods =
+ g_list_insert_before (client->mpd_node->Periods, next,
+ new_periods->data);
+ new_periods = g_list_delete_link (new_periods, new_periods);
+ }
+
+ /* And try again, getting the next list element which is now our newly
+ * inserted nodes. If any */
+ } else {
+ /* Got the next period and it doesn't have to be downloaded first */
+ break;
+ }
+ }
+
+ if (next_period_node) {
if (next_period_node->start != -1) {
if (start >= next_period_node->start * GST_MSECOND) {
/* Invalid MPD file: duration would be negative or zero */
@@ -3594,6 +3769,8 @@ gst_mpd_client_setup_media_presentation (GstMpdClient * client)
ret = TRUE;
GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
+
+ list = list->next;
}
GST_DEBUG ("Found a total of %d valid Periods in the Media Presentation",