mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-01 21:18:52 +00:00
d9baed8302
The xmlCleanupParser function seems to cleanup all statically allocated libxml variables, making it unusable. We can't guarantee that dashdemux won't need it anymore, so better not call it.
3747 lines
120 KiB
C
3747 lines
120 KiB
C
/*
|
|
* DASH MPD parsing library
|
|
*
|
|
* gstmpdparser.c
|
|
*
|
|
* Copyright (C) 2012 STMicroelectronics
|
|
*
|
|
* Authors:
|
|
* Gianluca Gennari <gennarone@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library (COPYING); if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
#include <string.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
#include "gstmpdparser.h"
|
|
|
|
/* Property parsing */
|
|
static gchar *gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
|
|
const gchar * property);
|
|
static gchar **gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
|
|
const gchar * property);
|
|
static guint gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
|
|
const gchar * property, guint default_val);
|
|
static guint64 gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
|
|
const gchar * property, guint64 default_val);
|
|
static guint *gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
|
|
const gchar * property, guint * size);
|
|
static gdouble gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
|
|
const gchar * property);
|
|
static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstMPDFileType gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstSAPType gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstRange *gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstRatio *gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstFrameRate *gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
|
|
const gchar * property);
|
|
static GstConditionalUintType *gst_mpdparser_get_xml_prop_cond_uint (xmlNode *
|
|
a_node, const gchar * property);
|
|
static GstDateTime *gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
|
|
const gchar * property);
|
|
static gint64 gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
|
|
const gchar * property);
|
|
static gchar *gst_mpdparser_get_xml_node_content (xmlNode * a_node);
|
|
static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
|
|
const gchar * prefix);
|
|
|
|
/* XML node parsing */
|
|
static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_content_component_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_segment_url_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType **
|
|
pointer, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_s_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
|
|
pointer, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType
|
|
** pointer, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
|
|
pointer, xmlNode * a_node);
|
|
static void
|
|
gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
|
|
pointer, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_representation_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_adaptation_set_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode **
|
|
pointer, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_program_info_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_metrics_range_node (GList ** list,
|
|
xmlNode * a_node);
|
|
static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
|
|
static void gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
|
|
xmlNode * a_node);
|
|
|
|
/* Helper functions */
|
|
static gint convert_to_millisecs (gint decimals, gint pos);
|
|
static int strncmp_ext (const char *s1, const char *s2);
|
|
static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
|
|
static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
|
|
GstActiveStream * stream, gchar ** query);
|
|
static gchar *gst_mpdparser_get_segmentURL_for_range (gchar * url,
|
|
GstRange * range);
|
|
static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
|
|
GstSegmentURLNode * segmentURL);
|
|
static gchar *gst_mpdparser_get_initializationURL (GstURLType *
|
|
InitializationURL);
|
|
static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
|
|
const gchar * id, guint number, guint bandwidth, guint64 time);
|
|
static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
|
|
GstSegmentURLNode * url_node, guint number, guint64 start,
|
|
GstClockTime start_time, GstClockTime duration);
|
|
static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
|
|
static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client,
|
|
GstActiveStream * stream);
|
|
|
|
/* Adaptation Set */
|
|
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);
|
|
|
|
/* Representation */
|
|
static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
|
|
Representations);
|
|
#if 0
|
|
static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
|
|
Representations);
|
|
static GstRepresentationNode
|
|
* gst_mpdparser_get_representation_with_max_bandwidth (GList *
|
|
Representations, gint max_bandwidth);
|
|
#endif
|
|
static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
|
|
Period, GstAdaptationSetNode * AdaptationSet,
|
|
GstRepresentationNode * Representation);
|
|
static GstSegmentListNode *gst_mpdparser_get_segment_list (GstPeriodNode *
|
|
Period, GstAdaptationSetNode * AdaptationSet,
|
|
GstRepresentationNode * Representation);
|
|
|
|
/* Segments */
|
|
static guint gst_mpd_client_get_segments_counts (GstActiveStream * stream);
|
|
|
|
/* Memory management */
|
|
static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
|
|
static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
|
|
prog_info_node);
|
|
static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
|
|
static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
|
|
metrics_range_node);
|
|
static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
|
|
static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
|
|
static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
|
|
segment_template_node);
|
|
static void
|
|
gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
|
|
representation_base);
|
|
static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
|
|
adaptation_set_node);
|
|
static void gst_mpdparser_free_representation_node (GstRepresentationNode *
|
|
representation_node);
|
|
static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
|
|
* subrep_node);
|
|
static void gst_mpdparser_free_s_node (GstSNode * s_node);
|
|
static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
|
|
seg_timeline);
|
|
static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
|
|
static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
|
|
seg_base_type);
|
|
static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
|
|
mult_seg_base_type);
|
|
static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
|
|
segment_list_node);
|
|
static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
|
|
segment_url);
|
|
static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
|
|
static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
|
|
descriptor_type);
|
|
static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
|
|
content_component_node);
|
|
static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
|
|
static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
|
|
static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
|
|
|
|
/* functions to parse node namespaces, content and properties */
|
|
static gchar *
|
|
gst_mpdparser_get_xml_prop_string (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
GST_LOG (" - %s: %s", property, prop_string);
|
|
}
|
|
|
|
return (gchar *) prop_string;
|
|
}
|
|
|
|
static gchar **
|
|
gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
|
|
const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
gchar **prop_string_vector = NULL;
|
|
guint i = 0;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
|
|
if (!prop_string_vector) {
|
|
GST_WARNING ("Scan of string vector property failed!");
|
|
return NULL;
|
|
}
|
|
GST_LOG (" - %s:", property);
|
|
while (prop_string_vector[i]) {
|
|
GST_LOG (" %s", prop_string_vector[i]);
|
|
i++;
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_string_vector;
|
|
}
|
|
|
|
static guint
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
|
|
const gchar * property, guint default_val)
|
|
{
|
|
xmlChar *prop_string;
|
|
guint prop_unsigned_integer = default_val;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (sscanf ((gchar *) prop_string, "%u", &prop_unsigned_integer)) {
|
|
GST_LOG (" - %s: %u", property, prop_unsigned_integer);
|
|
} else {
|
|
GST_WARNING
|
|
("failed to parse unsigned integer property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_unsigned_integer;
|
|
}
|
|
|
|
static guint64
|
|
gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
|
|
const gchar * property, guint64 default_val)
|
|
{
|
|
xmlChar *prop_string;
|
|
guint64 prop_unsigned_integer = default_val;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
|
|
&prop_unsigned_integer)) {
|
|
GST_LOG (" - %s: %" G_GUINT64_FORMAT, property, prop_unsigned_integer);
|
|
} else {
|
|
GST_WARNING
|
|
("failed to parse unsigned integer property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_unsigned_integer;
|
|
}
|
|
|
|
static guint *
|
|
gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
|
|
const gchar * property, guint * size)
|
|
{
|
|
xmlChar *prop_string;
|
|
gchar **str_vector;
|
|
guint *prop_uint_vector = NULL, i;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
|
|
if (!str_vector) {
|
|
GST_WARNING ("Scan of uint vector property failed!");
|
|
return NULL;
|
|
}
|
|
*size = g_strv_length (str_vector);
|
|
prop_uint_vector = g_malloc (*size * sizeof (guint));
|
|
if (!prop_uint_vector) {
|
|
GST_WARNING ("Array allocation failed!");
|
|
} else {
|
|
GST_LOG (" - %s:", property);
|
|
for (i = 0; i < *size; i++) {
|
|
if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i])) {
|
|
GST_LOG (" %u", prop_uint_vector[i]);
|
|
} else {
|
|
GST_WARNING
|
|
("failed to parse uint vector type property %s from xml string %s",
|
|
property, str_vector[i]);
|
|
}
|
|
}
|
|
}
|
|
xmlFree (prop_string);
|
|
g_strfreev (str_vector);
|
|
}
|
|
|
|
return prop_uint_vector;
|
|
}
|
|
|
|
static gdouble
|
|
gst_mpdparser_get_xml_prop_double (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
gdouble prop_double = 0;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (sscanf ((gchar *) prop_string, "%lf", &prop_double)) {
|
|
GST_LOG (" - %s: %lf", property, prop_double);
|
|
} else {
|
|
GST_WARNING ("failed to parse double property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_double;
|
|
}
|
|
|
|
static gboolean
|
|
gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
gboolean prop_bool = FALSE;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
|
|
GST_LOG (" - %s: false", property);
|
|
} else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
|
|
GST_LOG (" - %s: true", property);
|
|
prop_bool = TRUE;
|
|
} else {
|
|
GST_WARNING ("failed to parse boolean property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_bool;
|
|
}
|
|
|
|
static GstMPDFileType
|
|
gst_mpdparser_get_xml_prop_type (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
GstMPDFileType prop_type = GST_MPD_FILE_TYPE_STATIC; /* default */
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
|
|
|| xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
|
|
GST_LOG (" - %s: static", property);
|
|
prop_type = GST_MPD_FILE_TYPE_STATIC;
|
|
} else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
|
|
|| xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
|
|
GST_LOG (" - %s: dynamic", property);
|
|
prop_type = GST_MPD_FILE_TYPE_DYNAMIC;
|
|
} else {
|
|
GST_WARNING ("failed to parse MPD type property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_type;
|
|
}
|
|
|
|
static GstSAPType
|
|
gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
guint prop_SAP_type = 0;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type)
|
|
&& prop_SAP_type <= 6) {
|
|
GST_LOG (" - %s: %u", property, prop_SAP_type);
|
|
} else {
|
|
GST_WARNING
|
|
("failed to parse unsigned integer property %s from xml string %s",
|
|
property, prop_string);
|
|
}
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return (GstSAPType) prop_SAP_type;
|
|
}
|
|
|
|
static GstRange *
|
|
gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
GstRange *prop_range = NULL;
|
|
guint64 first_byte_pos = 0, last_byte_pos = 0;
|
|
guint len, pos;
|
|
gchar *str;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
len = xmlStrlen (prop_string);
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("range: %s, len %d", str, len);
|
|
|
|
/* read "-" */
|
|
pos = strcspn (str, "-");
|
|
if (pos >= len) {
|
|
GST_TRACE ("pos %d >= len %d", pos, len);
|
|
goto error;
|
|
}
|
|
/* read first_byte_pos */
|
|
if (pos != 0) {
|
|
if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* read last_byte_pos */
|
|
if (pos < (len - 1)) {
|
|
if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* malloc return data structure */
|
|
prop_range = g_slice_new0 (GstRange);
|
|
if (prop_range == NULL) {
|
|
GST_WARNING ("Allocation of GstRange failed!");
|
|
goto error;
|
|
}
|
|
prop_range->first_byte_pos = first_byte_pos;
|
|
prop_range->last_byte_pos = last_byte_pos;
|
|
GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
|
|
property, first_byte_pos, last_byte_pos);
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_range;
|
|
|
|
error:
|
|
GST_WARNING ("failed to parse property %s from xml string %s", property,
|
|
prop_string);
|
|
return NULL;
|
|
}
|
|
|
|
static GstRatio *
|
|
gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
GstRatio *prop_ratio = NULL;
|
|
guint num = 0, den = 1;
|
|
guint len, pos;
|
|
gchar *str;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
len = xmlStrlen (prop_string);
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("ratio: %s, len %d", str, len);
|
|
|
|
/* read ":" */
|
|
pos = strcspn (str, ":");
|
|
if (pos >= len) {
|
|
GST_TRACE ("pos %d >= len %d", pos, len);
|
|
goto error;
|
|
}
|
|
/* read num */
|
|
if (pos != 0) {
|
|
if (sscanf (str, "%u", &num) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* read den */
|
|
if (pos < (len - 1)) {
|
|
if (sscanf (str + pos + 1, "%u", &den) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* malloc return data structure */
|
|
prop_ratio = g_slice_new0 (GstRatio);
|
|
if (prop_ratio == NULL) {
|
|
GST_WARNING ("Allocation of GstRatio failed!");
|
|
goto error;
|
|
}
|
|
prop_ratio->num = num;
|
|
prop_ratio->den = den;
|
|
GST_LOG (" - %s: %u:%u", property, num, den);
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_ratio;
|
|
|
|
error:
|
|
GST_WARNING ("failed to parse property %s from xml string %s", property,
|
|
prop_string);
|
|
return NULL;
|
|
}
|
|
|
|
static GstFrameRate *
|
|
gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
GstFrameRate *prop_framerate = NULL;
|
|
guint num = 0, den = 1;
|
|
guint len, pos;
|
|
gchar *str;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
len = xmlStrlen (prop_string);
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("framerate: %s, len %d", str, len);
|
|
|
|
/* read "/" if available */
|
|
pos = strcspn (str, "/");
|
|
/* read num */
|
|
if (pos != 0) {
|
|
if (sscanf (str, "%u", &num) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* read den (if available) */
|
|
if (pos < (len - 1)) {
|
|
if (sscanf (str + pos + 1, "%u", &den) != 1) {
|
|
goto error;
|
|
}
|
|
}
|
|
/* alloc return data structure */
|
|
prop_framerate = g_slice_new0 (GstFrameRate);
|
|
if (prop_framerate == NULL) {
|
|
GST_WARNING ("Allocation of GstFrameRate failed!");
|
|
goto error;
|
|
}
|
|
prop_framerate->num = num;
|
|
prop_framerate->den = den;
|
|
if (den == 1)
|
|
GST_LOG (" - %s: %u", property, num);
|
|
else
|
|
GST_LOG (" - %s: %u/%u", property, num, den);
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_framerate;
|
|
|
|
error:
|
|
GST_WARNING ("failed to parse property %s from xml string %s", property,
|
|
prop_string);
|
|
return NULL;
|
|
}
|
|
|
|
static GstConditionalUintType *
|
|
gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
GstConditionalUintType *prop_cond_uint = NULL;
|
|
gchar *str;
|
|
gboolean flag;
|
|
guint val;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("conditional uint: %s", str);
|
|
|
|
if (strcmp (str, "false") == 0) {
|
|
flag = FALSE;
|
|
val = 0;
|
|
} else if (strcmp (str, "true") == 0) {
|
|
flag = TRUE;
|
|
val = 0;
|
|
} else {
|
|
flag = TRUE;
|
|
if (sscanf (str, "%u", &val) != 1)
|
|
goto error;
|
|
}
|
|
|
|
/* alloc return data structure */
|
|
prop_cond_uint = g_slice_new0 (GstConditionalUintType);
|
|
if (prop_cond_uint == NULL) {
|
|
GST_WARNING ("Allocation of GstConditionalUintType failed!");
|
|
goto error;
|
|
}
|
|
prop_cond_uint->flag = flag;
|
|
prop_cond_uint->value = val;
|
|
GST_LOG (" - %s: flag=%s val=%u", property, flag ? "true" : "false", val);
|
|
xmlFree (prop_string);
|
|
}
|
|
|
|
return prop_cond_uint;
|
|
|
|
error:
|
|
GST_WARNING ("failed to parse property %s from xml string %s", property,
|
|
prop_string);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
DateTime Data Type
|
|
|
|
The dateTime data type is used to specify a date and a time.
|
|
|
|
The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:
|
|
|
|
* YYYY indicates the year
|
|
* MM indicates the month
|
|
* DD indicates the day
|
|
* T indicates the start of the required time section
|
|
* hh indicates the hour
|
|
* mm indicates the minute
|
|
* ss indicates the second
|
|
|
|
Note: All components are required!
|
|
*/
|
|
|
|
static GstDateTime *
|
|
gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
gchar *str;
|
|
gint ret, len, pos;
|
|
gint year, month, day, hour, minute, second;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
len = xmlStrlen (prop_string);
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("dateTime: %s, len %d", str, len);
|
|
/* parse year */
|
|
ret = sscanf (str, "%d", &year);
|
|
if (ret != 1)
|
|
goto error;
|
|
pos = strcspn (str, "-");
|
|
str += (pos + 1);
|
|
GST_TRACE (" - year %d", year);
|
|
/* parse month */
|
|
ret = sscanf (str, "%d", &month);
|
|
if (ret != 1)
|
|
goto error;
|
|
pos = strcspn (str, "-");
|
|
str += (pos + 1);
|
|
GST_TRACE (" - month %d", month);
|
|
/* parse day */
|
|
ret = sscanf (str, "%d", &day);
|
|
if (ret != 1)
|
|
goto error;
|
|
pos = strcspn (str, "T");
|
|
str += (pos + 1);
|
|
GST_TRACE (" - day %d", day);
|
|
/* parse hour */
|
|
ret = sscanf (str, "%d", &hour);
|
|
if (ret != 1)
|
|
goto error;
|
|
pos = strcspn (str, ":");
|
|
str += (pos + 1);
|
|
GST_TRACE (" - hour %d", hour);
|
|
/* parse minute */
|
|
ret = sscanf (str, "%d", &minute);
|
|
if (ret != 1)
|
|
goto error;
|
|
pos = strcspn (str, ":");
|
|
str += (pos + 1);
|
|
GST_TRACE (" - minute %d", minute);
|
|
/* parse second */
|
|
ret = sscanf (str, "%d", &second);
|
|
if (ret != 1)
|
|
goto error;
|
|
GST_TRACE (" - second %d", second);
|
|
|
|
GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%02d", property,
|
|
year, month, day, hour, minute, second);
|
|
|
|
return gst_date_time_new (0, year, month, day, hour, minute, second);
|
|
}
|
|
|
|
return NULL;
|
|
|
|
error:
|
|
GST_WARNING ("failed to parse property %s from xml string %s", property,
|
|
prop_string);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
Duration Data Type
|
|
|
|
The duration data type is used to specify a time interval.
|
|
|
|
The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
|
|
|
|
* - indicates the negative sign (optional)
|
|
* P indicates the period (required)
|
|
* nY indicates the number of years
|
|
* nM indicates the number of months
|
|
* nD indicates the number of days
|
|
* T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
|
|
* nH indicates the number of hours
|
|
* nM indicates the number of minutes
|
|
* nS indicates the number of seconds
|
|
*/
|
|
|
|
/* this function computes decimals * 10 ^ (3 - pos) */
|
|
static gint
|
|
convert_to_millisecs (gint decimals, gint pos)
|
|
{
|
|
gint num = 1, den = 1, i = 3 - pos;
|
|
|
|
while (i < 0) {
|
|
den *= 10;
|
|
i++;
|
|
}
|
|
while (i > 0) {
|
|
num *= 10;
|
|
i--;
|
|
}
|
|
/* if i == 0 we have exactly 3 decimals and nothing to do */
|
|
return decimals * num / den;
|
|
}
|
|
|
|
static gint64
|
|
gst_mpdparser_get_xml_prop_duration (xmlNode * a_node, const gchar * property)
|
|
{
|
|
xmlChar *prop_string;
|
|
gchar *str;
|
|
gint64 prop_duration = -1;
|
|
gint ret, read, len, pos, posT;
|
|
gint years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds =
|
|
0, decimals = 0;
|
|
gint sign = 1;
|
|
gboolean have_ms = FALSE;
|
|
|
|
prop_string = xmlGetProp (a_node, (const xmlChar *) property);
|
|
if (prop_string) {
|
|
len = xmlStrlen (prop_string);
|
|
str = (gchar *) prop_string;
|
|
GST_TRACE ("duration: %s, len %d", str, len);
|
|
/* read "-" for sign, if present */
|
|
pos = strcspn (str, "-");
|
|
if (pos < len) { /* found "-" */
|
|
if (pos != 0) {
|
|
GST_WARNING ("sign \"-\" non at the beginning of the string");
|
|
return -1;
|
|
}
|
|
GST_TRACE ("found - sign at the beginning");
|
|
sign = -1;
|
|
str++;
|
|
len--;
|
|
}
|
|
/* read "P" for period */
|
|
pos = strcspn (str, "P");
|
|
if (pos != 0) {
|
|
GST_WARNING ("P not found at the beginning of the string!");
|
|
return -1;
|
|
}
|
|
str++;
|
|
len--;
|
|
/* read "T" for time (if present) */
|
|
posT = strcspn (str, "T");
|
|
len -= posT;
|
|
if (posT > 0) {
|
|
/* there is some room between P and T, so there must be a period section */
|
|
/* read years, months, days */
|
|
do {
|
|
GST_TRACE ("parsing substring %s", str);
|
|
pos = strcspn (str, "YMD");
|
|
ret = sscanf (str, "%d", &read);
|
|
if (ret != 1) {
|
|
GST_WARNING ("can not read integer value from string %s!", str);
|
|
return -1;
|
|
}
|
|
switch (str[pos]) {
|
|
case 'Y':
|
|
years = read;
|
|
break;
|
|
case 'M':
|
|
months = read;
|
|
break;
|
|
case 'D':
|
|
days = read;
|
|
break;
|
|
default:
|
|
GST_WARNING ("unexpected char %c!", str[pos]);
|
|
return -1;
|
|
break;
|
|
}
|
|
GST_TRACE ("read number %d type %c", read, str[pos]);
|
|
str += (pos + 1);
|
|
posT -= (pos + 1);
|
|
} while (posT > 0);
|
|
|
|
GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
|
|
}
|
|
/* read "T" for time (if present) */
|
|
/* here T is at pos == 0 */
|
|
str++;
|
|
len--;
|
|
pos = 0;
|
|
if (pos < len) {
|
|
/* T found, there is a time section */
|
|
/* read hours, minutes, seconds, cents of second */
|
|
do {
|
|
GST_TRACE ("parsing substring %s", str);
|
|
pos = strcspn (str, "HMS,.");
|
|
ret = sscanf (str, "%d", &read);
|
|
if (ret != 1) {
|
|
GST_WARNING ("can not read integer value from string %s!", str);
|
|
return -1;
|
|
}
|
|
switch (str[pos]) {
|
|
case 'H':
|
|
hours = read;
|
|
break;
|
|
case 'M':
|
|
minutes = read;
|
|
break;
|
|
case 'S':
|
|
if (have_ms) {
|
|
/* we have read the decimal part of the seconds */
|
|
decimals = convert_to_millisecs (read, pos);
|
|
GST_TRACE ("decimal number %d (%d digits) -> %d ms", read, pos,
|
|
decimals);
|
|
} else {
|
|
/* no decimals */
|
|
seconds = read;
|
|
}
|
|
break;
|
|
case '.':
|
|
case ',':
|
|
/* we have read the integer part of a decimal number in seconds */
|
|
seconds = read;
|
|
have_ms = TRUE;
|
|
break;
|
|
default:
|
|
GST_WARNING ("unexpected char %c!", str[pos]);
|
|
return -1;
|
|
break;
|
|
}
|
|
GST_TRACE ("read number %d type %c", read, str[pos]);
|
|
str += pos + 1;
|
|
len -= (pos + 1);
|
|
} while (len > 0);
|
|
|
|
GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
|
|
}
|
|
|
|
xmlFree (prop_string);
|
|
prop_duration =
|
|
sign * ((((((gint64) years * 365 + months * 30 + days) * 24 +
|
|
hours) * 60 + minutes) * 60 + seconds) * 1000 + decimals);
|
|
GST_LOG (" - %s: %" G_GINT64_FORMAT, property, prop_duration);
|
|
}
|
|
|
|
return prop_duration;
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_get_xml_node_content (xmlNode * a_node)
|
|
{
|
|
xmlChar *content = NULL;
|
|
|
|
content = xmlNodeGetContent (a_node);
|
|
if (content) {
|
|
GST_LOG (" - %s: %s", a_node->name, content);
|
|
}
|
|
|
|
return (gchar *) content;
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
|
|
{
|
|
xmlNs *curr_ns;
|
|
gchar *namespace = NULL;
|
|
|
|
if (prefix == NULL) {
|
|
/* return the default namespace */
|
|
namespace = g_strdup ((gchar *) a_node->ns->href);
|
|
if (namespace) {
|
|
GST_LOG (" - default namespace: %s", namespace);
|
|
}
|
|
} else {
|
|
/* look for the specified prefix in the namespace list */
|
|
for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
|
|
if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
|
|
namespace = g_strdup ((gchar *) curr_ns->href);
|
|
if (namespace) {
|
|
GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return namespace;
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstBaseURL *new_base_url;
|
|
|
|
new_base_url = g_slice_new0 (GstBaseURL);
|
|
if (new_base_url == NULL) {
|
|
GST_WARNING ("Allocation of BaseURL node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_base_url);
|
|
|
|
GST_LOG ("content of BaseURL node:");
|
|
new_base_url->baseURL = gst_mpdparser_get_xml_node_content (a_node);
|
|
|
|
GST_LOG ("attributes of BaseURL node:");
|
|
new_base_url->serviceLocation =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation");
|
|
new_base_url->byteRange =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "byteRange");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstDescriptorType *new_descriptor;
|
|
|
|
new_descriptor = g_slice_new0 (GstDescriptorType);
|
|
if (new_descriptor == NULL) {
|
|
GST_WARNING ("Allocation of DescriptorType node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_descriptor);
|
|
|
|
GST_LOG ("attributes of %s node:", a_node->name);
|
|
new_descriptor->schemeIdUri =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri");
|
|
new_descriptor->value = gst_mpdparser_get_xml_prop_string (a_node, "value");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstContentComponentNode *new_content_component;
|
|
|
|
new_content_component = g_slice_new0 (GstContentComponentNode);
|
|
if (new_content_component == NULL) {
|
|
GST_WARNING ("Allocation of ContentComponent node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_content_component);
|
|
|
|
GST_LOG ("attributes of ContentComponent node:");
|
|
new_content_component->id =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0);
|
|
new_content_component->lang =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "lang");
|
|
new_content_component->contentType =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "contentType");
|
|
new_content_component->par = gst_mpdparser_get_xml_prop_ratio (a_node, "par");
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&new_content_component->Accessibility, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&new_content_component->Rating, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&new_content_component->Viewpoint, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
gchar *location;
|
|
|
|
GST_LOG ("content of Location node:");
|
|
location = gst_mpdparser_get_xml_node_content (a_node);
|
|
|
|
*list = g_list_append (*list, location);
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstSubRepresentationNode *new_subrep;
|
|
|
|
new_subrep = g_slice_new0 (GstSubRepresentationNode);
|
|
if (new_subrep == NULL) {
|
|
GST_WARNING ("Allocation of SubRepresentation node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_subrep);
|
|
|
|
GST_LOG ("attributes of SubRepresentation node:");
|
|
new_subrep->level =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0);
|
|
new_subrep->dependencyLevel =
|
|
gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
|
|
&new_subrep->size);
|
|
new_subrep->bandwidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0);
|
|
new_subrep->contentComponent =
|
|
gst_mpdparser_get_xml_prop_string_vector_type (a_node,
|
|
"contentComponent");
|
|
|
|
/* RepresentationBase extension */
|
|
gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
|
|
a_node);
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstSegmentURLNode *new_segment_url;
|
|
|
|
new_segment_url = g_slice_new0 (GstSegmentURLNode);
|
|
if (new_segment_url == NULL) {
|
|
GST_WARNING ("Allocation of SegmentURL node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_segment_url);
|
|
|
|
GST_LOG ("attributes of SegmentURL node:");
|
|
new_segment_url->media = gst_mpdparser_get_xml_prop_string (a_node, "media");
|
|
new_segment_url->mediaRange =
|
|
gst_mpdparser_get_xml_prop_range (a_node, "mediaRange");
|
|
new_segment_url->index = gst_mpdparser_get_xml_prop_string (a_node, "index");
|
|
new_segment_url->indexRange =
|
|
gst_mpdparser_get_xml_prop_range (a_node, "indexRange");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
|
|
{
|
|
GstURLType *new_url_type;
|
|
|
|
gst_mpdparser_free_url_type_node (*pointer);
|
|
*pointer = new_url_type = g_slice_new0 (GstURLType);
|
|
if (new_url_type == NULL) {
|
|
GST_WARNING ("Allocation of URLType node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("attributes of URLType node:");
|
|
new_url_type->sourceURL =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "sourceURL");
|
|
new_url_type->range = gst_mpdparser_get_xml_prop_range (a_node, "range");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
|
|
xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstSegmentBaseType *seg_base_type;
|
|
|
|
gst_mpdparser_free_seg_base_type_ext (*pointer);
|
|
*pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
|
|
if (seg_base_type == NULL) {
|
|
GST_WARNING ("Allocation of SegmentBaseType node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("attributes of SegmentBaseType extension:");
|
|
seg_base_type->timescale =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 0);
|
|
seg_base_type->presentationTimeOffset =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node,
|
|
"presentationTimeOffset", 0);
|
|
seg_base_type->indexRange =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "indexRange");
|
|
seg_base_type->indexRangeExact =
|
|
gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact");
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
|
|
xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
|
|
gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "RepresentationIndex") == 0) {
|
|
gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
|
|
cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_s_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstSNode *new_s_node;
|
|
|
|
new_s_node = g_slice_new0 (GstSNode);
|
|
if (new_s_node == NULL) {
|
|
GST_WARNING ("Allocation of S node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_s_node);
|
|
|
|
GST_LOG ("attributes of S node:");
|
|
new_s_node->t =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "t", 0);
|
|
new_s_node->d =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0);
|
|
new_s_node->r = gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "r", 0);
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
|
|
xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstSegmentTimelineNode *new_seg_timeline;
|
|
|
|
gst_mpdparser_free_segment_timeline_node (*pointer);
|
|
*pointer = new_seg_timeline = g_slice_new0 (GstSegmentTimelineNode);
|
|
if (new_seg_timeline == NULL) {
|
|
GST_WARNING ("Allocation of SegmentTimeline node failed!");
|
|
return;
|
|
}
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
|
|
gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
|
|
xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstMultSegmentBaseType *mult_seg_base_type;
|
|
|
|
gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
|
|
*pointer = mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
|
|
if (mult_seg_base_type == NULL) {
|
|
GST_WARNING ("Allocation of MultipleSegmentBaseType node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("attributes of MultipleSegmentBaseType extension:");
|
|
mult_seg_base_type->duration =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0);
|
|
mult_seg_base_type->startNumber =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1);
|
|
|
|
GST_LOG ("extension of MultipleSegmentBaseType extension:");
|
|
gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
|
|
a_node);
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
|
|
gst_mpdparser_parse_segment_timeline_node
|
|
(&mult_seg_base_type->SegmentTimeline, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "BitstreamSwitching") == 0) {
|
|
gst_mpdparser_parse_url_type_node
|
|
(&mult_seg_base_type->BitstreamSwitching, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
|
|
xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstSegmentListNode *new_segment_list;
|
|
|
|
gst_mpdparser_free_segment_list_node (*pointer);
|
|
*pointer = new_segment_list = g_slice_new0 (GstSegmentListNode);
|
|
if (new_segment_list == NULL) {
|
|
GST_WARNING ("Allocation of SegmentList node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("extension of SegmentList node:");
|
|
gst_mpdparser_parse_mult_seg_base_type_ext
|
|
(&new_segment_list->MultSegBaseType, a_node);
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
|
|
gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
|
|
cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
|
|
pointer, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstRepresentationBaseType *representation_base;
|
|
|
|
gst_mpdparser_free_representation_base_type (*pointer);
|
|
*pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
|
|
if (representation_base == NULL) {
|
|
GST_WARNING ("Allocation of RepresentationBaseType node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("attributes of RepresentationBaseType extension:");
|
|
representation_base->profiles =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "profiles");
|
|
representation_base->width =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0);
|
|
representation_base->height =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0);
|
|
representation_base->sar = gst_mpdparser_get_xml_prop_ratio (a_node, "sar");
|
|
representation_base->frameRate =
|
|
gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate");
|
|
representation_base->audioSamplingRate =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate");
|
|
representation_base->mimeType =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "mimeType");
|
|
representation_base->segmentProfiles =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles");
|
|
representation_base->codecs =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "codecs");
|
|
representation_base->maximumSAPPeriod =
|
|
gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod");
|
|
representation_base->startWithSAP =
|
|
gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP");
|
|
representation_base->maxPlayoutRate =
|
|
gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate");
|
|
representation_base->codingDependency =
|
|
gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency");
|
|
representation_base->scanType =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "scanType");
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&representation_base->FramePacking, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "AudioChannelConfiguration") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&representation_base->AudioChannelConfiguration, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "ContentProtection") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node
|
|
(&representation_base->ContentProtection, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstRepresentationNode *new_representation;
|
|
|
|
new_representation = g_slice_new0 (GstRepresentationNode);
|
|
if (new_representation == NULL) {
|
|
GST_WARNING ("Allocation of Representation node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_representation);
|
|
|
|
GST_LOG ("attributes of Representation node:");
|
|
new_representation->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
|
|
new_representation->bandwidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0);
|
|
new_representation->qualityRanking =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0);
|
|
new_representation->dependencyId =
|
|
gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId");
|
|
new_representation->mediaStreamStructureId =
|
|
gst_mpdparser_get_xml_prop_string_vector_type (a_node,
|
|
"mediaStreamStructureId");
|
|
|
|
/* RepresentationBase extension */
|
|
gst_mpdparser_parse_representation_base_type
|
|
(&new_representation->RepresentationBase, a_node);
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
|
|
gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
|
|
gst_mpdparser_parse_segment_template_node
|
|
(&new_representation->SegmentTemplate, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
|
|
gst_mpdparser_parse_segment_list_node (&new_representation->SegmentList,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
|
|
gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "SubRepresentation") == 0) {
|
|
gst_mpdparser_parse_subrepresentation_node
|
|
(&new_representation->SubRepresentations, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstAdaptationSetNode *new_adap_set;
|
|
|
|
new_adap_set = g_slice_new0 (GstAdaptationSetNode);
|
|
if (new_adap_set == NULL) {
|
|
GST_WARNING ("Allocation of AdaptationSet node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_adap_set);
|
|
|
|
GST_LOG ("attributes of AdaptationSet node:");
|
|
new_adap_set->id =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0);
|
|
new_adap_set->group =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0);
|
|
new_adap_set->lang = gst_mpdparser_get_xml_prop_string (a_node, "lang");
|
|
new_adap_set->contentType =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "contentType");
|
|
new_adap_set->par = gst_mpdparser_get_xml_prop_ratio (a_node, "par");
|
|
new_adap_set->minBandwidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0);
|
|
new_adap_set->maxBandwidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0);
|
|
new_adap_set->minWidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0);
|
|
new_adap_set->maxWidth =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0);
|
|
new_adap_set->minHeight =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0);
|
|
new_adap_set->maxHeight =
|
|
gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0);
|
|
new_adap_set->minFrameRate =
|
|
gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate");
|
|
new_adap_set->maxFrameRate =
|
|
gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate");
|
|
new_adap_set->segmentAlignment =
|
|
gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment");
|
|
new_adap_set->subsegmentAlignment =
|
|
gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment");
|
|
new_adap_set->subsegmentStartsWithSAP =
|
|
gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP");
|
|
new_adap_set->bitstreamSwitching =
|
|
gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching");
|
|
|
|
/* RepresentationBase extension */
|
|
gst_mpdparser_parse_representation_base_type
|
|
(&new_adap_set->RepresentationBase, a_node);
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Accessibility,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Role,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Rating,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
|
|
gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Viewpoint,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
|
|
gst_mpdparser_parse_representation_node (&new_adap_set->Representations,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
|
|
gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
|
|
gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
|
|
gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "ContentComponent") == 0) {
|
|
gst_mpdparser_parse_content_component_node
|
|
(&new_adap_set->ContentComponents, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
|
|
gst_mpdparser_parse_segment_template_node
|
|
(&new_adap_set->SegmentTemplate, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstSubsetNode *new_subset;
|
|
|
|
new_subset = g_slice_new0 (GstSubsetNode);
|
|
if (new_subset == NULL) {
|
|
GST_WARNING ("Allocation of Subset node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_subset);
|
|
|
|
GST_LOG ("attributes of Subset node:");
|
|
new_subset->contains =
|
|
gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
|
|
&new_subset->size);
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
|
|
xmlNode * a_node)
|
|
{
|
|
GstSegmentTemplateNode *new_segment_template;
|
|
|
|
gst_mpdparser_free_segment_template_node (*pointer);
|
|
*pointer = new_segment_template = g_slice_new0 (GstSegmentTemplateNode);
|
|
if (new_segment_template == NULL) {
|
|
GST_WARNING ("Allocation of SegmentTemplate node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("extension of SegmentTemplate node:");
|
|
gst_mpdparser_parse_mult_seg_base_type_ext
|
|
(&new_segment_template->MultSegBaseType, a_node);
|
|
|
|
GST_LOG ("attributes of SegmentTemplate node:");
|
|
new_segment_template->media =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "media");
|
|
new_segment_template->index =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "index");
|
|
new_segment_template->initialization =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "initialization");
|
|
new_segment_template->bitstreamSwitching =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstPeriodNode *new_period;
|
|
|
|
new_period = g_slice_new0 (GstPeriodNode);
|
|
if (new_period == NULL) {
|
|
GST_WARNING ("Allocation of Period node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_period);
|
|
|
|
new_period->start = GST_CLOCK_TIME_NONE;
|
|
|
|
GST_LOG ("attributes of Period node:");
|
|
new_period->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
|
|
new_period->start = gst_mpdparser_get_xml_prop_duration (a_node, "start");
|
|
new_period->duration =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "duration");
|
|
new_period->bitstreamSwitching =
|
|
gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching");
|
|
|
|
/* explore children nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
|
|
gst_mpdparser_parse_adaptation_set_node (&new_period->AdaptationSets,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
|
|
gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
|
|
gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
|
|
gst_mpdparser_parse_segment_template_node (&new_period->SegmentTemplate,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
|
|
gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
|
|
gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstProgramInformationNode *new_prog_info;
|
|
|
|
new_prog_info = g_slice_new0 (GstProgramInformationNode);
|
|
if (new_prog_info == NULL) {
|
|
GST_WARNING ("Allocation of ProgramInfo node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_prog_info);
|
|
|
|
GST_LOG ("attributes of ProgramInformation node:");
|
|
new_prog_info->lang = gst_mpdparser_get_xml_prop_string (a_node, "lang");
|
|
new_prog_info->moreInformationURL =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL");
|
|
|
|
/* explore children nodes */
|
|
GST_LOG ("children of ProgramInformation node:");
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
|
|
new_prog_info->Title = gst_mpdparser_get_xml_node_content (cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
|
|
new_prog_info->Source = gst_mpdparser_get_xml_node_content (cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
|
|
new_prog_info->Copyright =
|
|
gst_mpdparser_get_xml_node_content (cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
GstMetricsRangeNode *new_metrics_range;
|
|
|
|
new_metrics_range = g_slice_new0 (GstMetricsRangeNode);
|
|
if (new_metrics_range == NULL) {
|
|
GST_WARNING ("Allocation of Metrics Range node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_metrics_range);
|
|
|
|
GST_LOG ("attributes of Metrics Range node:");
|
|
new_metrics_range->starttime =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "starttime");
|
|
new_metrics_range->duration =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "duration");
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstMetricsNode *new_metrics;
|
|
|
|
new_metrics = g_slice_new0 (GstMetricsNode);
|
|
if (new_metrics == NULL) {
|
|
GST_WARNING ("Allocation of Metrics node failed!");
|
|
return;
|
|
}
|
|
*list = g_list_append (*list, new_metrics);
|
|
|
|
GST_LOG ("attributes of Metrics node:");
|
|
new_metrics->metrics = gst_mpdparser_get_xml_prop_string (a_node, "metrics");
|
|
|
|
/* explore children nodes */
|
|
GST_LOG ("children of Metrics node:");
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
|
|
gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
|
|
cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
|
|
/* No reporting scheme is specified in this part of ISO/IEC 23009.
|
|
* It is expected that external specifications may define formats
|
|
* and delivery for the reporting data. */
|
|
GST_LOG (" - Reporting node found (unknown structure)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
|
|
{
|
|
xmlNode *cur_node;
|
|
GstMPDNode *new_mpd;
|
|
|
|
gst_mpdparser_free_mpd_node (*pointer);
|
|
*pointer = new_mpd = g_slice_new0 (GstMPDNode);
|
|
if (new_mpd == NULL) {
|
|
GST_WARNING ("Allocation of MPD node failed!");
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("namespaces of root MPD node:");
|
|
new_mpd->default_namespace =
|
|
gst_mpdparser_get_xml_node_namespace (a_node, NULL);
|
|
new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
|
|
new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
|
|
|
|
GST_LOG ("attributes of root MPD node:");
|
|
new_mpd->schemaLocation =
|
|
gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation");
|
|
new_mpd->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
|
|
new_mpd->profiles = gst_mpdparser_get_xml_prop_string (a_node, "profiles");
|
|
new_mpd->type = gst_mpdparser_get_xml_prop_type (a_node, "type");
|
|
new_mpd->availabilityStartTime =
|
|
gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime");
|
|
new_mpd->availabilityEndTime =
|
|
gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime");
|
|
new_mpd->mediaPresentationDuration =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration");
|
|
new_mpd->minimumUpdatePeriod =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod");
|
|
new_mpd->minBufferTime =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime");
|
|
new_mpd->timeShiftBufferDepth =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth");
|
|
new_mpd->suggestedPresentationDelay =
|
|
gst_mpdparser_get_xml_prop_duration (a_node,
|
|
"suggestedPresentationDelay");
|
|
new_mpd->maxSegmentDuration =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration");
|
|
new_mpd->maxSubsegmentDuration =
|
|
gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration");
|
|
|
|
/* explore children Period nodes */
|
|
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
|
|
if (cur_node->type == XML_ELEMENT_NODE) {
|
|
if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
|
|
gst_mpdparser_parse_period_node (&new_mpd->Periods, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name,
|
|
(xmlChar *) "ProgramInformation") == 0) {
|
|
gst_mpdparser_parse_program_info_node (&new_mpd->ProgramInfo, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
|
|
gst_mpdparser_parse_baseURL_node (&new_mpd->BaseURLs, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
|
|
gst_mpdparser_parse_location_node (&new_mpd->Locations, cur_node);
|
|
} else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
|
|
gst_mpdparser_parse_metrics_node (&new_mpd->Metrics, cur_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* comparison functions */
|
|
static int
|
|
strncmp_ext (const char *s1, const char *s2)
|
|
{
|
|
if (s1 == NULL && s2 == NULL)
|
|
return 0;
|
|
if (s1 == NULL && s2 != NULL)
|
|
return 1;
|
|
if (s2 == NULL && s1 != NULL)
|
|
return 1;
|
|
return strncmp (s1, s2, strlen (s2));
|
|
}
|
|
|
|
/* navigation functions */
|
|
static GstAdaptationSetNode *
|
|
gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets,
|
|
const gchar * mimeType)
|
|
{
|
|
GList *list;
|
|
GstAdaptationSetNode *adapt_set;
|
|
|
|
if (AdaptationSets == NULL)
|
|
return NULL;
|
|
|
|
for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
|
|
adapt_set = (GstAdaptationSetNode *) list->data;
|
|
if (adapt_set) {
|
|
gchar *this_mimeType = NULL;
|
|
GstRepresentationNode *rep;
|
|
rep =
|
|
gst_mpdparser_get_lowest_representation (adapt_set->Representations);
|
|
if (rep->RepresentationBase)
|
|
this_mimeType = rep->RepresentationBase->mimeType;
|
|
if (!this_mimeType && adapt_set->RepresentationBase) {
|
|
this_mimeType = adapt_set->RepresentationBase->mimeType;
|
|
}
|
|
GST_DEBUG ("Looking for mime type %s -> %s", mimeType, this_mimeType);
|
|
if (strncmp_ext (this_mimeType, mimeType) == 0)
|
|
return adapt_set;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* if idx < 0, returns the highest adaptation set with the given mimeType
|
|
* if idx >= 0, returns the highest adaptation set with the given mimeType and an index <= idx
|
|
*/
|
|
static GstAdaptationSetNode *
|
|
gst_mpdparser_get_adapt_set_with_mimeType_and_idx (GList * AdaptationSets,
|
|
const gchar * mimeType, gint idx)
|
|
{
|
|
GList *list;
|
|
GstAdaptationSetNode *adapt_set, *selected = NULL;
|
|
gint i = 0;
|
|
|
|
if (AdaptationSets == NULL)
|
|
return NULL;
|
|
|
|
// FIXME Use ContentComponent to determine if this adaptation set contains
|
|
// the content type we're looking for.
|
|
for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
|
|
adapt_set = (GstAdaptationSetNode *) list->data;
|
|
if (adapt_set) {
|
|
gchar *this_mimeType = NULL;
|
|
GstRepresentationNode *rep;
|
|
rep =
|
|
gst_mpdparser_get_lowest_representation (adapt_set->Representations);
|
|
if (rep->RepresentationBase)
|
|
this_mimeType = rep->RepresentationBase->mimeType;
|
|
if (!this_mimeType && adapt_set->RepresentationBase)
|
|
this_mimeType = adapt_set->RepresentationBase->mimeType;
|
|
GST_DEBUG ("Looking for mime type %s -> %i: %s", mimeType, i,
|
|
this_mimeType);
|
|
if (strncmp_ext (this_mimeType, mimeType) == 0) {
|
|
if (idx < 0 || i <= idx)
|
|
selected = adapt_set;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return selected;
|
|
}
|
|
|
|
static GstAdaptationSetNode *
|
|
gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (GList *
|
|
AdaptationSets, const gchar * mimeType, const gchar * lang)
|
|
{
|
|
GList *list;
|
|
GstAdaptationSetNode *adapt_set;
|
|
|
|
if (AdaptationSets == NULL)
|
|
return NULL;
|
|
|
|
for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
|
|
adapt_set = (GstAdaptationSetNode *) list->data;
|
|
if (adapt_set) {
|
|
GstRepresentationNode *rep;
|
|
gchar *this_lang = adapt_set->lang;
|
|
gchar *this_mimeType = NULL;
|
|
rep =
|
|
gst_mpdparser_get_lowest_representation (adapt_set->Representations);
|
|
if (rep && rep->BaseURLs) {
|
|
GstBaseURL *url = rep->BaseURLs->data;
|
|
GST_DEBUG ("%s", url->baseURL);
|
|
}
|
|
if (rep->RepresentationBase)
|
|
this_mimeType = rep->RepresentationBase->mimeType;
|
|
if (!this_mimeType && adapt_set->RepresentationBase) {
|
|
this_mimeType = adapt_set->RepresentationBase->mimeType;
|
|
}
|
|
GST_DEBUG ("Looking for mime type %s -> %s", mimeType, this_mimeType);
|
|
if (strncmp_ext (this_mimeType, mimeType) == 0
|
|
&& strncmp_ext (this_lang, lang) == 0)
|
|
return adapt_set;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GstRepresentationNode *
|
|
gst_mpdparser_get_lowest_representation (GList * Representations)
|
|
{
|
|
GList *list = NULL;
|
|
GstRepresentationNode *rep = NULL;
|
|
GstRepresentationNode *lowest = NULL;
|
|
|
|
if (Representations == NULL)
|
|
return NULL;
|
|
|
|
for (list = g_list_first (Representations); list; list = g_list_next (list)) {
|
|
rep = (GstRepresentationNode *) list->data;
|
|
if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
|
|
lowest = rep;
|
|
}
|
|
}
|
|
|
|
return lowest;
|
|
}
|
|
|
|
#if 0
|
|
static GstRepresentationNode *
|
|
gst_mpdparser_get_highest_representation (GList * Representations)
|
|
{
|
|
GList *list = NULL;
|
|
|
|
if (Representations == NULL)
|
|
return NULL;
|
|
|
|
list = g_list_last (Representations);
|
|
|
|
return list ? (GstRepresentationNode *) list->data : NULL;
|
|
}
|
|
|
|
static GstRepresentationNode *
|
|
gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
|
|
gint max_bandwidth)
|
|
{
|
|
GList *list = NULL;
|
|
GstRepresentationNode *representation, *best_rep = NULL;
|
|
|
|
if (Representations == NULL)
|
|
return NULL;
|
|
|
|
if (max_bandwidth <= 0) /* 0 => get highest representation available */
|
|
return gst_mpdparser_get_highest_representation (Representations);
|
|
|
|
for (list = g_list_first (Representations); list; list = g_list_next (list)) {
|
|
representation = (GstRepresentationNode *) list->data;
|
|
if (representation && representation->bandwidth <= max_bandwidth) {
|
|
best_rep = representation;
|
|
}
|
|
}
|
|
|
|
return best_rep;
|
|
}
|
|
#endif
|
|
|
|
static GstSegmentBaseType *
|
|
gst_mpdparser_get_segment_base (GstPeriodNode * Period,
|
|
GstAdaptationSetNode * AdaptationSet,
|
|
GstRepresentationNode * Representation)
|
|
{
|
|
GstSegmentBaseType *SegmentBase = NULL;
|
|
|
|
if (Representation && Representation->SegmentBase
|
|
&& Representation->SegmentBase->Initialization) {
|
|
SegmentBase = Representation->SegmentBase;
|
|
} else if (AdaptationSet && AdaptationSet->SegmentBase
|
|
&& AdaptationSet->SegmentBase->Initialization) {
|
|
SegmentBase = AdaptationSet->SegmentBase;
|
|
} else if (Period && Period->SegmentBase
|
|
&& Period->SegmentBase->Initialization) {
|
|
SegmentBase = Period->SegmentBase;
|
|
}
|
|
/* the SegmentBase element could be encoded also inside a SegmentList element */
|
|
if (SegmentBase == NULL) {
|
|
if (Representation && Representation->SegmentList
|
|
&& Representation->SegmentList->MultSegBaseType
|
|
&& Representation->SegmentList->MultSegBaseType->SegBaseType
|
|
&& Representation->SegmentList->MultSegBaseType->
|
|
SegBaseType->Initialization) {
|
|
SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
|
|
} else if (AdaptationSet && AdaptationSet->SegmentList
|
|
&& AdaptationSet->SegmentList->MultSegBaseType
|
|
&& AdaptationSet->SegmentList->MultSegBaseType->SegBaseType
|
|
&& AdaptationSet->SegmentList->MultSegBaseType->
|
|
SegBaseType->Initialization) {
|
|
SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
|
|
} else if (Period && Period->SegmentList
|
|
&& Period->SegmentList->MultSegBaseType
|
|
&& Period->SegmentList->MultSegBaseType->SegBaseType
|
|
&& Period->SegmentList->MultSegBaseType->SegBaseType->Initialization) {
|
|
SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
|
|
}
|
|
}
|
|
|
|
return SegmentBase;
|
|
}
|
|
|
|
gint
|
|
gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations)
|
|
{
|
|
GList *list = NULL, *lowest = NULL;
|
|
GstRepresentationNode *rep = NULL;
|
|
gint lowest_bandwidth = -1;
|
|
|
|
if (Representations == NULL)
|
|
return -1;
|
|
|
|
for (list = g_list_first (Representations); list; list = g_list_next (list)) {
|
|
rep = (GstRepresentationNode *) list->data;
|
|
if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
|
|
lowest = list;
|
|
lowest_bandwidth = rep->bandwidth;
|
|
}
|
|
}
|
|
|
|
return lowest ? g_list_position (Representations, lowest) : -1;
|
|
}
|
|
|
|
gint
|
|
gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
|
|
gint max_bandwidth)
|
|
{
|
|
GList *list = NULL, *best = NULL;
|
|
GstRepresentationNode *representation;
|
|
gint best_bandwidth = 0;
|
|
|
|
GST_DEBUG ("max_bandwidth = %i", max_bandwidth);
|
|
|
|
if (Representations == NULL)
|
|
return -1;
|
|
|
|
if (max_bandwidth <= 0) /* 0 => get lowest representation available */
|
|
return 0;
|
|
|
|
for (list = g_list_first (Representations); list; list = g_list_next (list)) {
|
|
representation = (GstRepresentationNode *) list->data;
|
|
if (representation && representation->bandwidth <= max_bandwidth &&
|
|
representation->bandwidth > best_bandwidth) {
|
|
best = list;
|
|
best_bandwidth = representation->bandwidth;
|
|
}
|
|
}
|
|
|
|
return best ? g_list_position (Representations, best) : -1;
|
|
}
|
|
|
|
static GstSegmentListNode *
|
|
gst_mpdparser_get_segment_list (GstPeriodNode * Period,
|
|
GstAdaptationSetNode * AdaptationSet,
|
|
GstRepresentationNode * Representation)
|
|
{
|
|
GstSegmentListNode *SegmentList = NULL;
|
|
|
|
if (Representation && Representation->SegmentList) {
|
|
SegmentList = Representation->SegmentList;
|
|
} else if (AdaptationSet && AdaptationSet->SegmentList) {
|
|
SegmentList = AdaptationSet->SegmentList;
|
|
} else {
|
|
SegmentList = Period->SegmentList;
|
|
}
|
|
|
|
return SegmentList;
|
|
}
|
|
|
|
/* memory management functions */
|
|
static void
|
|
gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
|
|
{
|
|
if (mpd_node) {
|
|
g_free (mpd_node->default_namespace);
|
|
g_free (mpd_node->namespace_xsi);
|
|
g_free (mpd_node->namespace_ext);
|
|
g_free (mpd_node->schemaLocation);
|
|
g_free (mpd_node->id);
|
|
g_free (mpd_node->profiles);
|
|
if (mpd_node->availabilityStartTime)
|
|
gst_date_time_unref (mpd_node->availabilityStartTime);
|
|
if (mpd_node->availabilityEndTime)
|
|
gst_date_time_unref (mpd_node->availabilityEndTime);
|
|
g_list_foreach (mpd_node->ProgramInfo,
|
|
(GFunc) gst_mpdparser_free_prog_info_node, NULL);
|
|
g_list_free (mpd_node->ProgramInfo);
|
|
g_list_foreach (mpd_node->BaseURLs,
|
|
(GFunc) gst_mpdparser_free_base_url_node, NULL);
|
|
g_list_free (mpd_node->BaseURLs);
|
|
g_list_foreach (mpd_node->Locations, (GFunc) g_free, NULL);
|
|
g_list_free (mpd_node->Locations);
|
|
g_list_foreach (mpd_node->Periods, (GFunc) gst_mpdparser_free_period_node,
|
|
NULL);
|
|
g_list_free (mpd_node->Periods);
|
|
g_list_foreach (mpd_node->Metrics, (GFunc) gst_mpdparser_free_metrics_node,
|
|
NULL);
|
|
g_list_free (mpd_node->Metrics);
|
|
g_slice_free (GstMPDNode, mpd_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
|
|
{
|
|
if (prog_info_node) {
|
|
g_free (prog_info_node->lang);
|
|
g_free (prog_info_node->moreInformationURL);
|
|
g_free (prog_info_node->Title);
|
|
g_free (prog_info_node->Source);
|
|
g_free (prog_info_node->Copyright);
|
|
g_slice_free (GstProgramInformationNode, prog_info_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
|
|
{
|
|
if (metrics_node) {
|
|
g_free (metrics_node->metrics);
|
|
g_list_foreach (metrics_node->MetricsRanges,
|
|
(GFunc) gst_mpdparser_free_metrics_range_node, NULL);
|
|
g_list_free (metrics_node->MetricsRanges);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
|
|
{
|
|
if (metrics_range_node) {
|
|
g_slice_free (GstMetricsRangeNode, metrics_range_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_period_node (GstPeriodNode * period_node)
|
|
{
|
|
if (period_node) {
|
|
g_free (period_node->id);
|
|
gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
|
|
gst_mpdparser_free_segment_list_node (period_node->SegmentList);
|
|
gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
|
|
g_list_foreach (period_node->AdaptationSets,
|
|
(GFunc) gst_mpdparser_free_adaptation_set_node, NULL);
|
|
g_list_free (period_node->AdaptationSets);
|
|
g_list_foreach (period_node->Subsets,
|
|
(GFunc) gst_mpdparser_free_subset_node, NULL);
|
|
g_list_free (period_node->Subsets);
|
|
g_list_foreach (period_node->BaseURLs,
|
|
(GFunc) gst_mpdparser_free_base_url_node, NULL);
|
|
g_list_free (period_node->BaseURLs);
|
|
g_slice_free (GstPeriodNode, period_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
|
|
{
|
|
if (subset_node) {
|
|
g_free (subset_node->contains);
|
|
g_slice_free (GstSubsetNode, subset_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
|
|
segment_template_node)
|
|
{
|
|
if (segment_template_node) {
|
|
g_free (segment_template_node->media);
|
|
g_free (segment_template_node->index);
|
|
g_free (segment_template_node->initialization);
|
|
g_free (segment_template_node->bitstreamSwitching);
|
|
/* MultipleSegmentBaseType extension */
|
|
gst_mpdparser_free_mult_seg_base_type_ext
|
|
(segment_template_node->MultSegBaseType);
|
|
g_slice_free (GstSegmentTemplateNode, segment_template_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
|
|
representation_base)
|
|
{
|
|
if (representation_base) {
|
|
g_free (representation_base->profiles);
|
|
g_slice_free (GstRatio, representation_base->sar);
|
|
g_slice_free (GstFrameRate, representation_base->frameRate);
|
|
g_free (representation_base->audioSamplingRate);
|
|
g_free (representation_base->mimeType);
|
|
g_free (representation_base->segmentProfiles);
|
|
g_free (representation_base->codecs);
|
|
g_free (representation_base->scanType);
|
|
g_list_foreach (representation_base->FramePacking,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (representation_base->FramePacking);
|
|
g_list_foreach (representation_base->AudioChannelConfiguration,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (representation_base->AudioChannelConfiguration);
|
|
g_list_foreach (representation_base->ContentProtection,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (representation_base->ContentProtection);
|
|
g_slice_free (GstRepresentationBaseType, representation_base);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
|
|
adaptation_set_node)
|
|
{
|
|
if (adaptation_set_node) {
|
|
g_free (adaptation_set_node->lang);
|
|
g_free (adaptation_set_node->contentType);
|
|
g_slice_free (GstRatio, adaptation_set_node->par);
|
|
g_slice_free (GstFrameRate, adaptation_set_node->minFrameRate);
|
|
g_slice_free (GstFrameRate, adaptation_set_node->maxFrameRate);
|
|
g_slice_free (GstConditionalUintType,
|
|
adaptation_set_node->segmentAlignment);
|
|
g_slice_free (GstConditionalUintType,
|
|
adaptation_set_node->subsegmentAlignment);
|
|
g_list_foreach (adaptation_set_node->Accessibility,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (adaptation_set_node->Accessibility);
|
|
g_list_foreach (adaptation_set_node->Role,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (adaptation_set_node->Role);
|
|
g_list_foreach (adaptation_set_node->Rating,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (adaptation_set_node->Rating);
|
|
g_list_foreach (adaptation_set_node->Viewpoint,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (adaptation_set_node->Viewpoint);
|
|
gst_mpdparser_free_representation_base_type
|
|
(adaptation_set_node->RepresentationBase);
|
|
gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
|
|
gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
|
|
gst_mpdparser_free_segment_template_node
|
|
(adaptation_set_node->SegmentTemplate);
|
|
g_list_foreach (adaptation_set_node->BaseURLs,
|
|
(GFunc) gst_mpdparser_free_base_url_node, NULL);
|
|
g_list_free (adaptation_set_node->BaseURLs);
|
|
g_list_foreach (adaptation_set_node->Representations,
|
|
(GFunc) gst_mpdparser_free_representation_node, NULL);
|
|
g_list_free (adaptation_set_node->Representations);
|
|
g_list_foreach (adaptation_set_node->ContentComponents,
|
|
(GFunc) gst_mpdparser_free_content_component_node, NULL);
|
|
g_list_free (adaptation_set_node->ContentComponents);
|
|
g_slice_free (GstAdaptationSetNode, adaptation_set_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_representation_node (GstRepresentationNode *
|
|
representation_node)
|
|
{
|
|
if (representation_node) {
|
|
g_free (representation_node->id);
|
|
g_strfreev (representation_node->dependencyId);
|
|
g_strfreev (representation_node->mediaStreamStructureId);
|
|
gst_mpdparser_free_representation_base_type
|
|
(representation_node->RepresentationBase);
|
|
g_list_foreach (representation_node->SubRepresentations,
|
|
(GFunc) gst_mpdparser_free_subrepresentation_node, NULL);
|
|
g_list_free (representation_node->SubRepresentations);
|
|
gst_mpdparser_free_seg_base_type_ext (representation_node->SegmentBase);
|
|
gst_mpdparser_free_segment_template_node
|
|
(representation_node->SegmentTemplate);
|
|
gst_mpdparser_free_segment_list_node (representation_node->SegmentList);
|
|
g_list_foreach (representation_node->BaseURLs,
|
|
(GFunc) gst_mpdparser_free_base_url_node, NULL);
|
|
g_list_free (representation_node->BaseURLs);
|
|
g_slice_free (GstRepresentationNode, representation_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
|
|
subrep_node)
|
|
{
|
|
if (subrep_node) {
|
|
gst_mpdparser_free_representation_base_type
|
|
(subrep_node->RepresentationBase);
|
|
g_free (subrep_node->dependencyLevel);
|
|
g_strfreev (subrep_node->contentComponent);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_s_node (GstSNode * s_node)
|
|
{
|
|
if (s_node) {
|
|
g_slice_free (GstSNode, s_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
|
|
{
|
|
if (seg_timeline) {
|
|
g_list_foreach (seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
|
|
g_list_free (seg_timeline->S);
|
|
g_slice_free (GstSegmentTimelineNode, seg_timeline);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
|
|
{
|
|
if (url_type_node) {
|
|
g_free (url_type_node->sourceURL);
|
|
g_slice_free (GstRange, url_type_node->range);
|
|
g_slice_free (GstURLType, url_type_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
|
|
{
|
|
if (seg_base_type) {
|
|
g_free (seg_base_type->indexRange);
|
|
gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
|
|
gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
|
|
g_slice_free (GstSegmentBaseType, seg_base_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
|
|
mult_seg_base_type)
|
|
{
|
|
if (mult_seg_base_type) {
|
|
/* SegmentBaseType extension */
|
|
gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
|
|
gst_mpdparser_free_segment_timeline_node
|
|
(mult_seg_base_type->SegmentTimeline);
|
|
gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
|
|
g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
|
|
{
|
|
if (segment_list_node) {
|
|
g_list_foreach (segment_list_node->SegmentURL,
|
|
(GFunc) gst_mpdparser_free_segment_url_node, NULL);
|
|
g_list_free (segment_list_node->SegmentURL);
|
|
/* MultipleSegmentBaseType extension */
|
|
gst_mpdparser_free_mult_seg_base_type_ext
|
|
(segment_list_node->MultSegBaseType);
|
|
g_slice_free (GstSegmentListNode, segment_list_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
|
|
{
|
|
if (segment_url) {
|
|
g_free (segment_url->media);
|
|
g_slice_free (GstRange, segment_url->mediaRange);
|
|
g_free (segment_url->index);
|
|
g_slice_free (GstRange, segment_url->indexRange);
|
|
g_slice_free (GstSegmentURLNode, segment_url);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
|
|
{
|
|
if (base_url_node) {
|
|
g_free (base_url_node->baseURL);
|
|
g_free (base_url_node->serviceLocation);
|
|
g_free (base_url_node->byteRange);
|
|
g_slice_free (GstBaseURL, base_url_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
|
|
{
|
|
if (descriptor_type) {
|
|
g_free (descriptor_type->schemeIdUri);
|
|
g_free (descriptor_type->value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_content_component_node (GstContentComponentNode *
|
|
content_component_node)
|
|
{
|
|
if (content_component_node) {
|
|
g_free (content_component_node->lang);
|
|
g_free (content_component_node->contentType);
|
|
g_slice_free (GstRatio, content_component_node->par);
|
|
g_list_foreach (content_component_node->Accessibility,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (content_component_node->Accessibility);
|
|
g_list_foreach (content_component_node->Role,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (content_component_node->Role);
|
|
g_list_foreach (content_component_node->Rating,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (content_component_node->Rating);
|
|
g_list_foreach (content_component_node->Viewpoint,
|
|
(GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
|
|
g_list_free (content_component_node->Viewpoint);
|
|
g_slice_free (GstContentComponentNode, content_component_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
|
|
{
|
|
if (stream_period) {
|
|
g_slice_free (GstStreamPeriod, stream_period);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
|
|
{
|
|
if (media_segment) {
|
|
g_slice_free (GstMediaSegment, media_segment);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
|
|
{
|
|
if (active_stream) {
|
|
g_free (active_stream->baseURL);
|
|
active_stream->baseURL = NULL;
|
|
g_free (active_stream->queryURL);
|
|
active_stream->queryURL = NULL;
|
|
g_list_foreach (active_stream->segments,
|
|
(GFunc) gst_mpdparser_free_media_segment, NULL);
|
|
g_list_free (active_stream->segments);
|
|
g_slice_free (GstActiveStream, active_stream);
|
|
}
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_get_segmentURL_for_range (gchar * url, GstRange * range)
|
|
{
|
|
gchar *segmentURL;
|
|
|
|
if (range) {
|
|
gchar *range_suffix;
|
|
range_suffix =
|
|
g_strdup_printf ("?range=%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
|
|
range->first_byte_pos, range->last_byte_pos);
|
|
segmentURL = g_strconcat (url, range_suffix, NULL);
|
|
g_free (range_suffix);
|
|
} else {
|
|
segmentURL = g_strdup (url);
|
|
}
|
|
|
|
return segmentURL;
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_get_mediaURL (GstActiveStream * stream,
|
|
GstSegmentURLNode * segmentURL)
|
|
{
|
|
const gchar *url_prefix;
|
|
|
|
g_return_val_if_fail (stream != NULL, NULL);
|
|
g_return_val_if_fail (segmentURL != NULL, NULL);
|
|
|
|
url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
|
|
g_return_val_if_fail (url_prefix != NULL, NULL);
|
|
|
|
return gst_mpdparser_get_segmentURL_for_range (segmentURL->media,
|
|
segmentURL->mediaRange);
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_get_initializationURL (GstURLType * InitializationURL)
|
|
{
|
|
g_return_val_if_fail (InitializationURL != NULL, NULL);
|
|
g_return_val_if_fail (InitializationURL->sourceURL != NULL, NULL);
|
|
|
|
return gst_mpdparser_get_segmentURL_for_range (InitializationURL->sourceURL,
|
|
InitializationURL->range);
|
|
}
|
|
|
|
static gchar *
|
|
gst_mpdparser_build_URL_from_template (const gchar * url_template,
|
|
const gchar * id, guint number, guint bandwidth, guint64 time)
|
|
{
|
|
static gchar default_format[] = "%01d";
|
|
gchar **tokens, *token, *ret;
|
|
const gchar *format;
|
|
gint i, num_tokens;
|
|
gboolean last_token_par = TRUE; /* last token was a parameter */
|
|
|
|
g_return_val_if_fail (url_template != NULL, NULL);
|
|
tokens = g_strsplit_set (url_template, "$", -1);
|
|
if (!tokens) {
|
|
GST_WARNING ("Scan of URL template failed!");
|
|
return NULL;
|
|
}
|
|
num_tokens = g_strv_length (tokens);
|
|
|
|
for (i = 0; i < num_tokens; i++) {
|
|
token = tokens[i];
|
|
format = default_format;
|
|
|
|
if (!g_strcmp0 (token, "RepresentationID")) {
|
|
tokens[i] = g_strdup_printf ("%s", id);
|
|
g_free (token);
|
|
last_token_par = TRUE;
|
|
} else if (!strncmp (token, "Number", 6)) {
|
|
if (strlen (token) > 6) {
|
|
format = token + 6; /* format tag */
|
|
}
|
|
tokens[i] = g_strdup_printf (format, number);
|
|
g_free (token);
|
|
last_token_par = TRUE;
|
|
} else if (!strncmp (token, "Bandwidth", 9)) {
|
|
if (strlen (token) > 9) {
|
|
format = token + 9; /* format tag */
|
|
}
|
|
tokens[i] = g_strdup_printf (format, bandwidth);
|
|
g_free (token);
|
|
last_token_par = TRUE;
|
|
} else if (!strncmp (token, "Time", 4)) {
|
|
if (strlen (token) > 4) {
|
|
format = token + 4; /* format tag */
|
|
} else {
|
|
format = "%" G_GUINT64_FORMAT;
|
|
}
|
|
tokens[i] = g_strdup_printf (format, time);
|
|
g_free (token);
|
|
last_token_par = TRUE;
|
|
} else if (!g_strcmp0 (token, "")) {
|
|
if (!last_token_par) {
|
|
tokens[i] = g_strdup_printf ("%s", "$");
|
|
g_free (token);
|
|
last_token_par = TRUE;
|
|
}
|
|
} else {
|
|
last_token_par = FALSE;
|
|
}
|
|
}
|
|
|
|
ret = g_strjoinv (NULL, tokens);
|
|
g_strfreev (tokens);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstStreamPeriod *
|
|
gst_mpdparser_get_stream_period (GstMpdClient * client)
|
|
{
|
|
g_return_val_if_fail (client != NULL, NULL);
|
|
g_return_val_if_fail (client->periods != NULL, NULL);
|
|
|
|
return g_list_nth_data (client->periods, client->period_idx);
|
|
}
|
|
|
|
/* select a stream and extract the baseURL (if present) */
|
|
static gchar *
|
|
gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
|
|
gchar ** query)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
GstBaseURL *baseURL;
|
|
GList *list;
|
|
static gchar *baseURL_array[5];
|
|
static gchar empty[] = "";
|
|
gchar *ret = NULL;
|
|
|
|
g_return_val_if_fail (stream != NULL, empty);
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, empty);
|
|
g_return_val_if_fail (stream_period->period != NULL, empty);
|
|
|
|
baseURL_array[0] = baseURL_array[1] = baseURL_array[2] = baseURL_array[3] =
|
|
empty;
|
|
baseURL_array[4] = NULL;
|
|
|
|
/* FIXME: this simple implementation is not fully compliant with RFC 3986 */
|
|
if ((list = client->mpd_node->BaseURLs) != NULL) {
|
|
baseURL = g_list_nth_data (list, stream->baseURL_idx);
|
|
if (!baseURL) {
|
|
baseURL = list->data;
|
|
}
|
|
baseURL_array[0] = baseURL->baseURL;
|
|
}
|
|
if ((list = stream_period->period->BaseURLs) != NULL) {
|
|
baseURL = g_list_nth_data (list, stream->baseURL_idx);
|
|
if (!baseURL) {
|
|
baseURL = list->data;
|
|
}
|
|
baseURL_array[1] = baseURL->baseURL;
|
|
}
|
|
GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
|
|
stream->cur_adapt_set->contentType);
|
|
|
|
if ((list = stream->cur_adapt_set->BaseURLs) != NULL) {
|
|
baseURL = g_list_nth_data (list, stream->baseURL_idx);
|
|
if (!baseURL) {
|
|
baseURL = list->data;
|
|
}
|
|
baseURL_array[2] = baseURL->baseURL;
|
|
}
|
|
|
|
if ((list = stream->cur_representation->BaseURLs) != NULL) {
|
|
baseURL = g_list_nth_data (list, stream->baseURL_idx);
|
|
if (!baseURL) {
|
|
baseURL = list->data;
|
|
}
|
|
baseURL_array[3] = baseURL->baseURL;
|
|
}
|
|
|
|
ret = g_strjoinv (NULL, baseURL_array);
|
|
|
|
/* get base URI from MPD file URI, if the "http" scheme is missing */
|
|
if (client->mpd_uri != NULL && strncmp (ret, "http://", 7) != 0) {
|
|
gchar *last_sep, *tmp1, *tmp2;
|
|
|
|
if (ret[0] == '?') {
|
|
if (query)
|
|
*query = g_strdup (ret);
|
|
g_free (ret);
|
|
ret = NULL;
|
|
} else {
|
|
if (query)
|
|
*query = NULL;
|
|
}
|
|
|
|
last_sep = strrchr (client->mpd_uri, '/');
|
|
if (last_sep) {
|
|
tmp1 = g_strndup (client->mpd_uri, last_sep - client->mpd_uri + 1);
|
|
if (ret) {
|
|
tmp2 = ret;
|
|
ret = g_strconcat (tmp1, tmp2, NULL);
|
|
g_free (tmp1);
|
|
g_free (tmp2);
|
|
} else {
|
|
ret = tmp1;
|
|
}
|
|
GST_WARNING ("Got base URI from MPD file URI %s", ret);
|
|
}
|
|
}
|
|
|
|
if (ret && *query == NULL) {
|
|
gchar *params = strchr (ret, '?');
|
|
if (params) {
|
|
*query = g_strdup (params);
|
|
params[0] = '\0'; /* can ignore the rest of the string */
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstClockTime
|
|
gst_mpd_client_get_segment_duration (GstMpdClient * client,
|
|
GstActiveStream * stream)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
GstMultSegmentBaseType *base = NULL;
|
|
GstClockTime duration;
|
|
guint timescale;
|
|
|
|
g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
|
|
|
|
if (stream->cur_segment_list) {
|
|
base = stream->cur_segment_list->MultSegBaseType;
|
|
} else if (stream->cur_seg_template) {
|
|
base = stream->cur_seg_template->MultSegBaseType;
|
|
}
|
|
|
|
if (base == NULL || base->SegBaseType == NULL) {
|
|
/* this may happen when we have a single segment */
|
|
duration = stream_period->duration;
|
|
} else {
|
|
duration = base->duration * GST_SECOND;
|
|
timescale = base->SegBaseType->timescale;
|
|
|
|
if (timescale > 1)
|
|
duration /= timescale;
|
|
}
|
|
|
|
return duration;
|
|
}
|
|
|
|
/*****************************/
|
|
/******* API functions *******/
|
|
/*****************************/
|
|
|
|
GstMpdClient *
|
|
gst_mpd_client_new (void)
|
|
{
|
|
GstMpdClient *client;
|
|
|
|
client = g_new0 (GstMpdClient, 1);
|
|
client->lock = g_mutex_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);
|
|
|
|
if (client->mpd_node)
|
|
gst_mpdparser_free_mpd_node (client->mpd_node);
|
|
|
|
if (client->periods) {
|
|
g_list_foreach (client->periods,
|
|
(GFunc) gst_mpdparser_free_stream_period, NULL);
|
|
g_list_free (client->periods);
|
|
}
|
|
|
|
gst_active_streams_free (client);
|
|
|
|
if (client->lock)
|
|
g_mutex_free (client->lock);
|
|
|
|
g_free (client->mpd_uri);
|
|
|
|
g_free (client);
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
|
|
{
|
|
if (data) {
|
|
xmlDocPtr doc;
|
|
xmlNode *root_element = NULL;
|
|
|
|
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
|
|
* between the version it was compiled for and the actual shared
|
|
* library used
|
|
*/
|
|
LIBXML_TEST_VERSION
|
|
/* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
|
|
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 */
|
|
root_element = xmlDocGetRootElement (doc);
|
|
|
|
if (root_element->type != XML_ELEMENT_NODE
|
|
|| xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
|
|
GST_ERROR
|
|
("can not find the root element MPD, failed to parse the MPD file");
|
|
} else {
|
|
/* now we can parse the MPD root node and all children nodes, recursively */
|
|
gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
|
|
}
|
|
/* free the document */
|
|
xmlFreeDoc (doc);
|
|
}
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const gchar *
|
|
gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream)
|
|
{
|
|
GstActiveStream *stream;
|
|
|
|
g_return_val_if_fail (client != NULL, NULL);
|
|
g_return_val_if_fail (client->active_streams != NULL, NULL);
|
|
stream = g_list_nth_data (client->active_streams, indexStream);
|
|
g_return_val_if_fail (stream != NULL, NULL);
|
|
|
|
return stream->baseURL;
|
|
}
|
|
|
|
GstMediaSegment *
|
|
gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream,
|
|
guint indexChunk)
|
|
{
|
|
GstActiveStream *stream;
|
|
|
|
/* select stream */
|
|
g_return_val_if_fail (client != NULL, NULL);
|
|
g_return_val_if_fail (client->active_streams != NULL, NULL);
|
|
stream = g_list_nth_data (client->active_streams, indexStream);
|
|
g_return_val_if_fail (stream != NULL, NULL);
|
|
|
|
return (GstMediaSegment *) g_list_nth_data (stream->segments, indexChunk);
|
|
}
|
|
|
|
static gboolean
|
|
gst_mpd_client_add_media_segment (GstActiveStream * stream,
|
|
GstSegmentURLNode * url_node, guint number, guint64 start,
|
|
GstClockTime start_time, GstClockTime duration)
|
|
{
|
|
GstMediaSegment *media_segment;
|
|
|
|
media_segment = g_slice_new0 (GstMediaSegment);
|
|
if (media_segment == NULL) {
|
|
GST_WARNING ("Allocation of GstMediaSegment struct failed!");
|
|
return FALSE;
|
|
}
|
|
stream->segments = g_list_append (stream->segments, media_segment);
|
|
media_segment->SegmentURL = url_node;
|
|
media_segment->number = number;
|
|
media_segment->start = start;
|
|
media_segment->start_time = start_time;
|
|
media_segment->duration = duration;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_setup_representation (GstMpdClient * client,
|
|
GstActiveStream * stream, GstRepresentationNode * representation)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
GList *rep_list;
|
|
GstClockTime PeriodStart, PeriodEnd, start_time, duration;
|
|
GstMediaSegment *last_media_segment;
|
|
guint i;
|
|
guint64 start;
|
|
|
|
if (stream->cur_adapt_set == NULL) {
|
|
GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
|
|
return FALSE;
|
|
}
|
|
|
|
rep_list = stream->cur_adapt_set->Representations;
|
|
stream->cur_representation = representation;
|
|
stream->representation_idx = g_list_index (rep_list, representation);
|
|
|
|
/* clean the old segment list, if any */
|
|
if (stream->segments) {
|
|
g_list_foreach (stream->segments,
|
|
(GFunc) gst_mpdparser_free_media_segment, NULL);
|
|
g_list_free (stream->segments);
|
|
stream->segments = NULL;
|
|
}
|
|
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, FALSE);
|
|
g_return_val_if_fail (stream_period->period != NULL, FALSE);
|
|
|
|
PeriodStart = stream_period->start;
|
|
if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
|
|
PeriodEnd = stream_period->start + stream_period->duration;
|
|
else
|
|
PeriodEnd = GST_CLOCK_TIME_NONE;
|
|
|
|
GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
|
|
|
|
if (representation->SegmentBase != NULL
|
|
|| representation->SegmentList != NULL) {
|
|
GList *SegmentURL;
|
|
|
|
/* get the first segment_base of the selected representation */
|
|
if ((stream->cur_segment_base =
|
|
gst_mpdparser_get_segment_base (stream_period->period,
|
|
stream->cur_adapt_set, representation)) == NULL) {
|
|
GST_DEBUG ("No useful SegmentBase node for the current Representation");
|
|
}
|
|
|
|
/* get the first segment_list of the selected representation */
|
|
if ((stream->cur_segment_list =
|
|
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, PeriodStart,
|
|
PeriodEnd)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
/* build the list of GstMediaSegment nodes from the SegmentList node */
|
|
SegmentURL = stream->cur_segment_list->SegmentURL;
|
|
if (SegmentURL == NULL) {
|
|
GST_WARNING
|
|
("No valid list of SegmentURL nodes in the MPD file, aborting...");
|
|
return FALSE;
|
|
}
|
|
|
|
/* build segment list */
|
|
i = stream->cur_segment_list->MultSegBaseType->startNumber;
|
|
start = 0;
|
|
start_time = PeriodStart;
|
|
|
|
GST_LOG ("Building media segment list using a SegmentList node");
|
|
if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
|
|
GstSegmentTimelineNode *timeline;
|
|
GstSNode *S;
|
|
GList *list;
|
|
|
|
timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
|
|
for (list = g_list_first (timeline->S); list; list = g_list_next (list)) {
|
|
guint j, timescale;
|
|
|
|
S = (GstSNode *) list->data;
|
|
GST_LOG ("Processing S node: d=%llu r=%d t=%llu", S->d, S->r, S->t);
|
|
duration = S->d * GST_SECOND;
|
|
timescale =
|
|
stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
|
|
if (timescale > 1)
|
|
duration /= timescale;
|
|
if (S->t > 0) {
|
|
start = S->t;
|
|
start_time = S->t * GST_SECOND;
|
|
if (timescale > 1)
|
|
start_time /= timescale;
|
|
}
|
|
|
|
for (j = 0; j <= S->r && SegmentURL != NULL; j++) {
|
|
if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
|
|
start, start_time, duration)) {
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
start += S->d;
|
|
start_time += duration;
|
|
SegmentURL = g_list_next (SegmentURL);
|
|
}
|
|
}
|
|
} else {
|
|
duration = gst_mpd_client_get_segment_duration (client, stream);
|
|
if (!GST_CLOCK_TIME_IS_VALID (duration))
|
|
return FALSE;
|
|
|
|
while (SegmentURL) {
|
|
if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, 0,
|
|
start_time, duration)) {
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
start_time += duration;
|
|
SegmentURL = g_list_next (SegmentURL);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (representation->SegmentTemplate != NULL) {
|
|
stream->cur_seg_template = representation->SegmentTemplate;
|
|
} else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
|
|
stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
|
|
} else if (stream_period->period->SegmentTemplate != NULL) {
|
|
stream->cur_seg_template = stream_period->period->SegmentTemplate;
|
|
}
|
|
|
|
if (stream->cur_seg_template == NULL
|
|
|| stream->cur_seg_template->MultSegBaseType == NULL) {
|
|
/* 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)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
/* build segment list */
|
|
i = stream->cur_seg_template->MultSegBaseType->startNumber;
|
|
start = 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) {
|
|
GstSegmentTimelineNode *timeline;
|
|
GstSNode *S;
|
|
GList *list;
|
|
|
|
timeline = stream->cur_seg_template->MultSegBaseType->SegmentTimeline;
|
|
for (list = g_list_first (timeline->S); list; list = g_list_next (list)) {
|
|
guint j, timescale;
|
|
|
|
S = (GstSNode *) list->data;
|
|
GST_LOG ("Processing S node: d=%llu r=%u t=%llu", S->d, S->r, S->t);
|
|
duration = S->d * GST_SECOND;
|
|
timescale =
|
|
stream->cur_seg_template->MultSegBaseType->SegBaseType->timescale;
|
|
if (timescale > 1)
|
|
duration /= timescale;
|
|
if (S->t > 0) {
|
|
start = S->t;
|
|
start_time = S->t * GST_SECOND;
|
|
if (timescale > 1)
|
|
start_time /= timescale;
|
|
}
|
|
|
|
for (j = 0; j <= S->r; j++) {
|
|
if (!gst_mpd_client_add_media_segment (stream, NULL, i, start,
|
|
start_time, duration)) {
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
start += S->d;
|
|
start_time += duration;
|
|
}
|
|
}
|
|
} else {
|
|
duration = gst_mpd_client_get_segment_duration (client, stream);
|
|
if (!GST_CLOCK_TIME_IS_VALID (duration)
|
|
|| !GST_CLOCK_TIME_IS_VALID (PeriodEnd)
|
|
|| duration <= 0)
|
|
return FALSE;
|
|
|
|
while (start_time < PeriodEnd) {
|
|
if (!gst_mpd_client_add_media_segment (stream, NULL, i, 0, start_time,
|
|
duration)) {
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
start_time += duration;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check duration of last segment */
|
|
last_media_segment = g_list_last (stream->segments)->data;
|
|
if (last_media_segment && GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
|
|
if (last_media_segment->start_time + last_media_segment->duration >
|
|
PeriodEnd) {
|
|
last_media_segment->duration = PeriodEnd - last_media_segment->start_time;
|
|
GST_LOG ("Fixed duration of last segment: %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (last_media_segment->duration));
|
|
}
|
|
GST_LOG ("Built a list of %d segments", last_media_segment->number);
|
|
}
|
|
|
|
g_free (stream->baseURL);
|
|
g_free (stream->queryURL);
|
|
stream->baseURL =
|
|
gst_mpdparser_parse_baseURL (client, stream, &stream->queryURL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_setup_media_presentation (GstMpdClient * client)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
GstPeriodNode *period_node;
|
|
GstClockTime start, duration;
|
|
GList *list, *next;
|
|
guint idx;
|
|
gboolean ret = FALSE;
|
|
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->mpd_node != NULL, FALSE);
|
|
|
|
GST_DEBUG ("Building the list of Periods in the Media Presentation");
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
/* clean the old period list, if any */
|
|
if (client->periods) {
|
|
g_list_foreach (client->periods,
|
|
(GFunc) gst_mpdparser_free_stream_period, NULL);
|
|
g_list_free (client->periods);
|
|
client->periods = NULL;
|
|
}
|
|
|
|
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;
|
|
if (period_node->start != -1) {
|
|
/* we have a regular period */
|
|
start = period_node->start * GST_MSECOND;
|
|
} else if (duration != GST_CLOCK_TIME_NONE) {
|
|
/* start time inferred from previous period, this is still a regular period */
|
|
start += duration;
|
|
} else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
|
|
/* first period of a static MPD file, start time is 0 */
|
|
start = 0;
|
|
} else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
|
|
/* this should be a live stream, let this pass */
|
|
} else {
|
|
/* this is an 'Early Available Period' */
|
|
goto early;
|
|
}
|
|
|
|
if (period_node->duration != -1) {
|
|
duration = period_node->duration * GST_MSECOND;
|
|
} else if ((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;
|
|
if (next_period_node->start != -1) {
|
|
duration = next_period_node->start * GST_MSECOND - start;
|
|
} else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
|
|
/* might be a live file, ignore unspecified duration */
|
|
} else {
|
|
/* Invalid MPD file! */
|
|
goto syntax_error;
|
|
}
|
|
} else if (client->mpd_node->mediaPresentationDuration != -1) {
|
|
/* last Period of the Media Presentation */
|
|
duration =
|
|
client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
|
|
} else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
|
|
/* might be a live file, ignore unspecified duration */
|
|
} else {
|
|
/* Invalid MPD file! */
|
|
goto syntax_error;
|
|
}
|
|
|
|
stream_period = g_slice_new0 (GstStreamPeriod);
|
|
if (stream_period == NULL) {
|
|
goto no_mem;
|
|
}
|
|
client->periods = g_list_append (client->periods, stream_period);
|
|
stream_period->period = period_node;
|
|
stream_period->number = idx++;
|
|
stream_period->start = start;
|
|
stream_period->duration = duration;
|
|
ret = TRUE;
|
|
GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
|
|
GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
|
|
}
|
|
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
GST_DEBUG ("Found a total of %d valid Periods in the Media Presentation",
|
|
idx);
|
|
return ret;
|
|
|
|
early:
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
GST_WARNING
|
|
("Found an Early Available Period, skipping the rest of the Media Presentation");
|
|
return ret;
|
|
|
|
syntax_error:
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
GST_WARNING
|
|
("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
|
|
idx);
|
|
return ret;
|
|
|
|
no_mem:
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
GST_WARNING ("Allocation of GstStreamPeriod struct failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_setup_streaming (GstMpdClient * client,
|
|
GstStreamMimeType mimeType, const gchar * lang)
|
|
{
|
|
GstActiveStream *stream;
|
|
GstStreamPeriod *stream_period;
|
|
GstAdaptationSetNode *adapt_set;
|
|
GstRepresentationNode *representation;
|
|
GList *rep_list = NULL;
|
|
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
if (stream_period == NULL || stream_period->period == NULL) {
|
|
GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
|
|
return FALSE;
|
|
}
|
|
|
|
switch (mimeType) {
|
|
case GST_STREAM_VIDEO:
|
|
/* 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 (!adapt_set) {
|
|
GST_INFO ("No video adaptation set found");
|
|
return FALSE;
|
|
}
|
|
/* retrive the list of representations */
|
|
rep_list = adapt_set->Representations;
|
|
if (!rep_list) {
|
|
GST_WARNING ("Can not retrieve any representation, aborting...");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case GST_STREAM_AUDIO:
|
|
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 */
|
|
if (!adapt_set)
|
|
adapt_set =
|
|
gst_mpdparser_get_first_adapt_set_with_mimeType
|
|
(stream_period->period->AdaptationSets, "audio");
|
|
if (!adapt_set) {
|
|
GST_INFO ("No audio adaptation set found");
|
|
return FALSE;
|
|
}
|
|
rep_list = adapt_set->Representations;
|
|
if (!rep_list) {
|
|
GST_WARNING ("Can not retrieve any representation, aborting...");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case GST_STREAM_APPLICATION:
|
|
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 */
|
|
if (!adapt_set)
|
|
adapt_set =
|
|
gst_mpdparser_get_first_adapt_set_with_mimeType
|
|
(stream_period->period->AdaptationSets, "application");
|
|
if (!adapt_set) {
|
|
GST_INFO ("No application adaptation set found");
|
|
return FALSE;
|
|
}
|
|
rep_list = adapt_set->Representations;
|
|
if (!rep_list) {
|
|
GST_WARNING ("Can not retrieve any representation, aborting...");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
GST_WARNING ("Unsupported mimeType %d", mimeType);
|
|
return FALSE;
|
|
}
|
|
|
|
stream = g_slice_new0 (GstActiveStream);
|
|
if (stream == NULL) {
|
|
GST_WARNING ("Allocation of active stream struct failed!");
|
|
return FALSE;
|
|
}
|
|
client->active_streams = g_list_append (client->active_streams, stream);
|
|
|
|
stream->baseURL_idx = 0;
|
|
stream->mimeType = mimeType;
|
|
stream->cur_adapt_set = adapt_set;
|
|
|
|
GST_DEBUG ("0. Current stream %p", stream);
|
|
|
|
/* retrive representation list */
|
|
if (stream->cur_adapt_set != NULL)
|
|
rep_list = stream->cur_adapt_set->Representations;
|
|
|
|
#if 0
|
|
/* fast start */
|
|
representation =
|
|
gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
|
|
stream->max_bandwidth);
|
|
|
|
if (!representation) {
|
|
GST_WARNING
|
|
("Can not retrieve a representation with the requested bandwidth");
|
|
representation = gst_mpdparser_get_lowest_representation (rep_list);
|
|
}
|
|
#else
|
|
/* slow start */
|
|
representation = gst_mpdparser_get_lowest_representation (rep_list);
|
|
#endif
|
|
|
|
if (!representation) {
|
|
GST_WARNING ("No valid representation in the MPD file, aborting...");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_mpd_client_setup_representation (client, stream, representation))
|
|
return FALSE;
|
|
|
|
GST_INFO ("Successfully setup the download pipeline for mimeType %d",
|
|
mimeType);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
|
|
GstClockTime ts)
|
|
{
|
|
gint segment_idx = 0;
|
|
GstMediaSegment *selectedChunk = NULL;
|
|
GList *iter;
|
|
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
for (iter = stream->segments; iter; iter = g_list_next (iter), segment_idx++) {
|
|
GstMediaSegment *segment = iter->data;
|
|
GST_DEBUG ("Looking at fragment sequence chunk %d", segment_idx);
|
|
if (segment->start_time >= ts) {
|
|
selectedChunk = segment;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selectedChunk == NULL) {
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_mpd_client_set_segment_index (stream, segment_idx);
|
|
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_get_last_fragment_timestamp (GstMpdClient * client,
|
|
guint stream_idx, GstClockTime * ts)
|
|
{
|
|
GstActiveStream *stream;
|
|
gint segment_idx;
|
|
GstMediaSegment *currentChunk;
|
|
|
|
GST_DEBUG ("Stream index: %i", stream_idx);
|
|
stream = g_list_nth_data (client->active_streams, stream_idx);
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
segment_idx = gst_mpd_client_get_segments_counts (stream) - 1;
|
|
GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
|
|
|
|
currentChunk =
|
|
gst_mpdparser_get_chunk_by_index (client, stream_idx, segment_idx);
|
|
if (currentChunk == NULL) {
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
return FALSE;
|
|
}
|
|
|
|
*ts = currentChunk->start_time;
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
|
|
guint stream_idx, GstClockTime * ts)
|
|
{
|
|
GstActiveStream *stream;
|
|
gint segment_idx;
|
|
GstMediaSegment *currentChunk;
|
|
|
|
GST_DEBUG ("Stream index: %i", stream_idx);
|
|
stream = g_list_nth_data (client->active_streams, stream_idx);
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
segment_idx = gst_mpd_client_get_segment_index (stream);
|
|
GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
|
|
|
|
currentChunk =
|
|
gst_mpdparser_get_chunk_by_index (client, stream_idx, segment_idx);
|
|
if (currentChunk == NULL) {
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
return FALSE;
|
|
}
|
|
|
|
*ts = currentChunk->start_time;
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_get_next_fragment (GstMpdClient * client,
|
|
guint indexStream, gboolean * discontinuity, gchar ** uri,
|
|
GstClockTime * duration, GstClockTime * timestamp)
|
|
{
|
|
GstActiveStream *stream = NULL;
|
|
GstMediaSegment *currentChunk;
|
|
gchar *mediaURL = NULL;
|
|
guint segment_idx;
|
|
|
|
/* select stream */
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->active_streams != NULL, FALSE);
|
|
stream = g_list_nth_data (client->active_streams, indexStream);
|
|
g_return_val_if_fail (stream != NULL, FALSE);
|
|
g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
|
|
g_return_val_if_fail (discontinuity != NULL, FALSE);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
segment_idx = gst_mpd_client_get_segment_index (stream);
|
|
GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
|
|
|
|
currentChunk =
|
|
gst_mpdparser_get_chunk_by_index (client, indexStream, segment_idx);
|
|
if (currentChunk == NULL) {
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
|
|
if (currentChunk->SegmentURL != NULL) {
|
|
mediaURL = gst_mpdparser_get_mediaURL (stream, currentChunk->SegmentURL);
|
|
} else if (stream->cur_seg_template != NULL) {
|
|
mediaURL =
|
|
gst_mpdparser_build_URL_from_template (stream->cur_seg_template->media,
|
|
stream->cur_representation->id, currentChunk->number,
|
|
stream->cur_representation->bandwidth, currentChunk->start);
|
|
}
|
|
GST_DEBUG ("mediaURL = %s", mediaURL);
|
|
|
|
*timestamp = currentChunk->start_time;
|
|
*duration = currentChunk->duration;
|
|
*discontinuity = segment_idx != currentChunk->number;
|
|
if (mediaURL == NULL) {
|
|
/* single segment with URL encoded in the baseURL syntax element */
|
|
*uri = g_strdup (stream->baseURL);
|
|
} else if (strncmp (mediaURL, "http://", 7) != 0) {
|
|
*uri = g_strconcat (stream->baseURL, mediaURL, stream->queryURL, NULL);
|
|
} else {
|
|
*uri = g_strconcat (mediaURL, stream->queryURL, NULL);
|
|
}
|
|
g_free (mediaURL);
|
|
gst_mpd_client_set_segment_index (stream, segment_idx + 1);
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
GST_DEBUG ("Loading chunk with URL %s", *uri);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
|
|
guint stream_idx)
|
|
{
|
|
GstActiveStream *stream;
|
|
GstStreamPeriod *stream_period;
|
|
|
|
stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
|
|
g_return_val_if_fail (stream != NULL, FALSE);
|
|
g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, FALSE);
|
|
g_return_val_if_fail (stream_period->period != NULL, FALSE);
|
|
|
|
GST_DEBUG ("Looking for current representation header");
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
*uri = NULL;
|
|
if (stream->cur_segment_base && stream->cur_segment_base->Initialization) {
|
|
*uri =
|
|
gst_mpdparser_get_initializationURL (stream->
|
|
cur_segment_base->Initialization);
|
|
} else if (stream->cur_seg_template) {
|
|
const gchar *initialization = NULL;
|
|
if (stream->cur_seg_template->initialization) {
|
|
initialization = stream->cur_seg_template->initialization;
|
|
} else if (stream->cur_adapt_set->SegmentTemplate
|
|
&& stream->cur_adapt_set->SegmentTemplate->initialization) {
|
|
initialization = stream->cur_adapt_set->SegmentTemplate->initialization;
|
|
} else if (stream_period->period->SegmentTemplate
|
|
&& stream_period->period->SegmentTemplate->initialization) {
|
|
initialization = stream_period->period->SegmentTemplate->initialization;
|
|
}
|
|
*uri = gst_mpdparser_build_URL_from_template (initialization,
|
|
stream->cur_representation->id, 0,
|
|
stream->cur_representation->bandwidth, 0);
|
|
}
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return *uri == NULL ? FALSE : TRUE;
|
|
}
|
|
|
|
GstClockTime
|
|
gst_mpd_client_get_current_position (GstMpdClient * client)
|
|
{
|
|
GstActiveStream *stream;
|
|
GstMediaSegment *media_segment;
|
|
|
|
stream = g_list_nth_data (client->active_streams, client->stream_idx);
|
|
g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
|
|
|
|
media_segment =
|
|
g_list_nth_data (stream->segments,
|
|
gst_mpd_client_get_segment_index (stream));
|
|
g_return_val_if_fail (media_segment != NULL, GST_CLOCK_TIME_NONE);
|
|
|
|
return media_segment->start_time;
|
|
}
|
|
|
|
GstClockTime
|
|
gst_mpd_client_get_next_fragment_duration (GstMpdClient * client)
|
|
{
|
|
GstActiveStream *stream;
|
|
GstMediaSegment *media_segment;
|
|
|
|
GST_DEBUG ("Stream index: %i", client->stream_idx);
|
|
stream = g_list_nth_data (client->active_streams, client->stream_idx);
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
media_segment =
|
|
g_list_nth_data (stream->segments,
|
|
gst_mpd_client_get_segment_index (stream));
|
|
|
|
return media_segment == NULL ? 0 : media_segment->duration;
|
|
}
|
|
|
|
GstClockTime
|
|
gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
|
|
{
|
|
GstClockTime duration;
|
|
|
|
g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
if (client->mpd_node->mediaPresentationDuration != -1) {
|
|
duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
|
|
} else {
|
|
/* We can only get the duration for on-demand streams */
|
|
duration = GST_CLOCK_TIME_NONE;
|
|
}
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return duration;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
|
|
{
|
|
GstStreamPeriod *next_stream_period;
|
|
gboolean ret = FALSE;
|
|
GList *iter;
|
|
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->periods != NULL, FALSE);
|
|
g_return_val_if_fail (period_id != NULL, FALSE);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
for (iter = client->periods; iter; iter = g_list_next (iter)) {
|
|
next_stream_period = iter->data;
|
|
|
|
if (next_stream_period->period->id
|
|
&& strcmp (next_stream_period->period->id, period_id) == 0) {
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
|
|
{
|
|
GstStreamPeriod *next_stream_period;
|
|
gboolean ret = FALSE;
|
|
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->periods != NULL, FALSE);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
next_stream_period = g_list_nth_data (client->periods, period_idx);
|
|
if (next_stream_period != NULL) {
|
|
client->period_idx = period_idx;
|
|
ret = TRUE;
|
|
}
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return ret;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_period_index (GstMpdClient * client)
|
|
{
|
|
guint period_idx;
|
|
|
|
g_return_val_if_fail (client != NULL, 0);
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
period_idx = client->period_idx;
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return period_idx;
|
|
}
|
|
|
|
const gchar *
|
|
gst_mpd_client_get_period_id (GstMpdClient * client)
|
|
{
|
|
GstStreamPeriod *period;
|
|
gchar *period_id = NULL;
|
|
|
|
g_return_val_if_fail (client != NULL, 0);
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
period = g_list_nth_data (client->periods, client->period_idx);
|
|
if (period && period->period)
|
|
period_id = period->period->id;
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return period_id;
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_has_next_period (GstMpdClient * client)
|
|
{
|
|
GList *next_stream_period;
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->periods != NULL, FALSE);
|
|
|
|
GST_MPD_CLIENT_LOCK (client);
|
|
next_stream_period =
|
|
g_list_nth_data (client->periods, client->period_idx + 1);
|
|
GST_MPD_CLIENT_UNLOCK (client);
|
|
|
|
return next_stream_period != NULL;
|
|
}
|
|
|
|
void
|
|
gst_mpd_client_set_segment_index_for_all_streams (GstMpdClient * client,
|
|
guint segment_idx)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (client != NULL);
|
|
g_return_if_fail (client->active_streams != NULL);
|
|
|
|
/* FIXME: support multiple streams with different segment duration */
|
|
for (list = g_list_first (client->active_streams); list;
|
|
list = g_list_next (list)) {
|
|
GstActiveStream *stream = (GstActiveStream *) list->data;
|
|
if (stream) {
|
|
stream->segment_idx = segment_idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_mpd_client_set_segment_index (GstActiveStream * stream, guint segment_idx)
|
|
{
|
|
g_return_if_fail (stream != NULL);
|
|
|
|
stream->segment_idx = segment_idx;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_segment_index (GstActiveStream * stream)
|
|
{
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
return stream->segment_idx;
|
|
}
|
|
|
|
static guint
|
|
gst_mpd_client_get_segments_counts (GstActiveStream * stream)
|
|
{
|
|
g_return_val_if_fail (stream != NULL, 0);
|
|
|
|
return g_list_length (stream->segments);
|
|
}
|
|
|
|
gboolean
|
|
gst_mpd_client_is_live (GstMpdClient * client)
|
|
{
|
|
g_return_val_if_fail (client != NULL, FALSE);
|
|
g_return_val_if_fail (client->mpd_node != NULL, FALSE);
|
|
|
|
return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
|
|
}
|
|
|
|
guint
|
|
gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
|
|
{
|
|
g_return_val_if_fail (client != NULL, 0);
|
|
|
|
return g_list_length (client->active_streams);
|
|
}
|
|
|
|
guint
|
|
gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, 0);
|
|
g_return_val_if_fail (stream_period->period != NULL, 0);
|
|
|
|
return g_list_length (stream_period->period->AdaptationSets);
|
|
}
|
|
|
|
GstActiveStream *
|
|
gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
|
|
guint stream_idx)
|
|
{
|
|
g_return_val_if_fail (client != NULL, NULL);
|
|
g_return_val_if_fail (client->active_streams != NULL, NULL);
|
|
|
|
return g_list_nth_data (client->active_streams, stream_idx);
|
|
}
|
|
|
|
static const gchar *
|
|
gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
|
|
{
|
|
if (mimeType == NULL)
|
|
return NULL;
|
|
if (strcmp (mimeType, "video/mp2t") == 0) {
|
|
return "video/mpegts";
|
|
} else if (strcmp (mimeType, "video/mp4") == 0) {
|
|
return "video/quicktime";
|
|
} else if (strcmp (mimeType, "audio/mp4") == 0) {
|
|
return "audio/x-m4a";
|
|
} else
|
|
return mimeType;
|
|
}
|
|
|
|
const gchar *
|
|
gst_mpd_client_get_stream_mimeType (GstActiveStream * stream)
|
|
{
|
|
const gchar *mimeType;
|
|
|
|
if (stream == NULL || stream->cur_adapt_set == NULL
|
|
|| stream->cur_representation == NULL)
|
|
return NULL;
|
|
|
|
mimeType = stream->cur_representation->RepresentationBase->mimeType;
|
|
if (mimeType == NULL) {
|
|
mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
|
|
}
|
|
|
|
return gst_mpdparser_mimetype_to_caps (mimeType);
|
|
}
|
|
|
|
const gboolean
|
|
gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
|
|
{
|
|
if (stream == NULL || stream->cur_adapt_set == NULL)
|
|
return FALSE;
|
|
|
|
return stream->cur_adapt_set->bitstreamSwitching;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
|
|
{
|
|
guint width;
|
|
|
|
if (stream == NULL || stream->cur_adapt_set == NULL
|
|
|| stream->cur_representation == NULL)
|
|
return 0;
|
|
|
|
width = stream->cur_representation->RepresentationBase->width;
|
|
if (width == 0) {
|
|
width = stream->cur_adapt_set->RepresentationBase->width;
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
|
|
{
|
|
guint height;
|
|
|
|
if (stream == NULL || stream->cur_adapt_set == NULL
|
|
|| stream->cur_representation == NULL)
|
|
return 0;
|
|
|
|
height = stream->cur_representation->RepresentationBase->height;
|
|
if (height == 0) {
|
|
height = stream->cur_adapt_set->RepresentationBase->height;
|
|
}
|
|
|
|
return height;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
|
|
{
|
|
const gchar *rate;
|
|
|
|
if (stream == NULL || stream->cur_adapt_set == NULL
|
|
|| stream->cur_representation == NULL)
|
|
return 0;
|
|
|
|
rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
|
|
if (rate == NULL) {
|
|
rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
|
|
}
|
|
|
|
return rate ? atoi (rate) : 0;
|
|
}
|
|
|
|
guint
|
|
gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
|
|
{
|
|
if (stream == NULL || stream->cur_adapt_set == NULL
|
|
|| stream->cur_representation == NULL)
|
|
return 0;
|
|
/* TODO: here we have to parse the AudioChannelConfiguration descriptors */
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
|
|
GList ** lang)
|
|
{
|
|
GstStreamPeriod *stream_period;
|
|
GstAdaptationSetNode *adapt_set;
|
|
GList *list;
|
|
const gchar *this_mimeType = "audio";
|
|
gchar *mimeType = NULL;
|
|
guint nb_adapatation_set = 0;
|
|
|
|
stream_period = gst_mpdparser_get_stream_period (client);
|
|
g_return_val_if_fail (stream_period != NULL, 0);
|
|
g_return_val_if_fail (stream_period->period != NULL, 0);
|
|
|
|
for (list = g_list_first (stream_period->period->AdaptationSets); list;
|
|
list = g_list_next (list)) {
|
|
adapt_set = (GstAdaptationSetNode *) list->data;
|
|
if (adapt_set) {
|
|
gchar *this_lang = adapt_set->lang;
|
|
GstRepresentationNode *rep;
|
|
rep =
|
|
gst_mpdparser_get_lowest_representation (adapt_set->Representations);
|
|
if (rep->RepresentationBase)
|
|
mimeType = rep->RepresentationBase->mimeType;
|
|
if (!mimeType && adapt_set->RepresentationBase) {
|
|
mimeType = adapt_set->RepresentationBase->mimeType;
|
|
}
|
|
|
|
if (strncmp_ext (mimeType, this_mimeType) == 0) {
|
|
if (this_lang) {
|
|
nb_adapatation_set++;
|
|
*lang = g_list_append (*lang, this_lang);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nb_adapatation_set;
|
|
}
|