2022-03-11 16:11:50 +00:00
/*
* 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 .
*/
# include <string.h>
# include <gst/pbutils/pbutils.h>
# include "gstmpdparser.h"
# include "gstdash_debug.h"
# define GST_CAT_DEFAULT gst_dash_demux2_debug
/* XML node parsing */
static void gst_mpdparser_parse_baseURL_node ( GList * * list , xmlNode * a_node ) ;
static void gst_mpdparser_parse_descriptor_type ( 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 ( GstMPDURLTypeNode * * pointer ,
xmlNode * a_node ) ;
static void gst_mpdparser_parse_seg_base_type_ext ( GstMPDSegmentBaseNode * *
pointer , xmlNode * a_node , GstMPDSegmentBaseNode * parent ) ;
static void gst_mpdparser_parse_s_node ( GQueue * queue , xmlNode * a_node ) ;
static void gst_mpdparser_parse_segment_timeline_node ( GstMPDSegmentTimelineNode
* * pointer , xmlNode * a_node ) ;
static gboolean
gst_mpdparser_parse_mult_seg_base_node ( GstMPDMultSegmentBaseNode *
pointer , xmlNode * a_node , GstMPDMultSegmentBaseNode * parent ) ;
static gboolean gst_mpdparser_parse_segment_list_node ( GstMPDSegmentListNode * *
pointer , xmlNode * a_node , GstMPDSegmentListNode * parent ) ;
static void
gst_mpdparser_parse_representation_base ( GstMPDRepresentationBaseNode *
pointer , xmlNode * a_node ) ;
static gboolean gst_mpdparser_parse_representation_node ( GList * * list ,
xmlNode * a_node , GstMPDAdaptationSetNode * parent ,
GstMPDPeriodNode * period_node ) ;
static gboolean gst_mpdparser_parse_adaptation_set_node ( GList * * list ,
xmlNode * a_node , GstMPDPeriodNode * parent ) ;
static void gst_mpdparser_parse_subset_node ( GList * * list , xmlNode * a_node ) ;
static gboolean
gst_mpdparser_parse_segment_template_node ( GstMPDSegmentTemplateNode * * pointer ,
xmlNode * a_node , GstMPDSegmentTemplateNode * parent ) ;
static gboolean 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 gboolean gst_mpdparser_parse_root_node ( GstMPDRootNode * * pointer ,
xmlNode * a_node ) ;
static void gst_mpdparser_parse_utctiming_node ( GList * * list ,
xmlNode * a_node ) ;
/*
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
*/
static void
gst_mpdparser_parse_baseURL_node ( GList * * list , xmlNode * a_node )
{
GstMPDBaseURLNode * new_base_url ;
new_base_url = gst_mpd_baseurl_node_new ( ) ;
* list = g_list_append ( * list , new_base_url ) ;
GST_LOG ( " content of BaseURL node: " ) ;
gst_xml_helper_get_node_content ( a_node , & new_base_url - > baseURL ) ;
GST_LOG ( " attributes of BaseURL node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " serviceLocation " ,
& new_base_url - > serviceLocation ) ;
gst_xml_helper_get_prop_string ( a_node , " byteRange " ,
& new_base_url - > byteRange ) ;
}
static void
gst_mpdparser_parse_descriptor_type ( GList * * list , xmlNode * a_node )
{
GstMPDDescriptorTypeNode * new_descriptor ;
new_descriptor =
gst_mpd_descriptor_type_node_new ( ( const gchar * ) a_node - > name ) ;
* list = g_list_append ( * list , new_descriptor ) ;
GST_LOG ( " attributes of %s node: " , a_node - > name ) ;
gst_xml_helper_get_prop_string_stripped ( a_node , " schemeIdUri " ,
& new_descriptor - > schemeIdUri ) ;
if ( ! gst_xml_helper_get_prop_string ( a_node , " value " , & new_descriptor - > value ) ) {
/* if no value attribute, use XML string representation of the node */
gst_xml_helper_get_node_as_string ( a_node , & new_descriptor - > value ) ;
}
}
static void
gst_mpdparser_parse_content_component_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDContentComponentNode * new_content_component ;
new_content_component = gst_mpd_content_component_node_new ( ) ;
* list = g_list_append ( * list , new_content_component ) ;
GST_LOG ( " attributes of ContentComponent node: " ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " id " , 0 ,
& new_content_component - > id ) ;
gst_xml_helper_get_prop_string ( a_node , " lang " , & new_content_component - > lang ) ;
gst_xml_helper_get_prop_string ( a_node , " contentType " ,
& new_content_component - > contentType ) ;
gst_xml_helper_get_prop_ratio ( a_node , " par " , & new_content_component - > 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
( & new_content_component - > Accessibility , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Role " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type ( & new_content_component - > Role ,
cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Rating " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type
( & new_content_component - > Rating , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Viewpoint " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type
( & new_content_component - > Viewpoint , cur_node ) ;
}
}
}
}
static void
gst_mpdparser_parse_location_node ( GList * * list , xmlNode * a_node )
{
gchar * location = NULL ;
GstMPDLocationNode * locationNode ;
GST_LOG ( " content of Location node: " ) ;
if ( gst_xml_helper_get_node_content ( a_node , & location ) ) {
locationNode = gst_mpd_location_node_new ( ) ;
locationNode - > location = location ;
* list = g_list_append ( * list , locationNode ) ;
}
}
static void
gst_mpdparser_parse_subrepresentation_node ( GList * * list , xmlNode * a_node )
{
GstMPDSubRepresentationNode * new_subrep ;
new_subrep = gst_mpd_sub_representation_node_new ( ) ;
* list = g_list_append ( * list , new_subrep ) ;
GST_LOG ( " attributes of SubRepresentation node: " ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " level " , 0 ,
& new_subrep - > level ) ;
gst_xml_helper_get_prop_uint_vector_type ( a_node , " dependencyLevel " ,
& new_subrep - > dependencyLevel , & new_subrep - > dependencyLevel_size ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " bandwidth " , 0 ,
& new_subrep - > bandwidth ) ;
gst_xml_helper_get_prop_string_vector_type ( a_node ,
" contentComponent " , & new_subrep - > contentComponent ) ;
/* RepresentationBase extension */
gst_mpdparser_parse_representation_base ( GST_MPD_REPRESENTATION_BASE_NODE
( new_subrep ) , a_node ) ;
}
static void
gst_mpdparser_parse_segment_url_node ( GList * * list , xmlNode * a_node )
{
GstMPDSegmentURLNode * new_segment_url ;
new_segment_url = gst_mpd_segment_url_node_new ( ) ;
* list = g_list_append ( * list , new_segment_url ) ;
GST_LOG ( " attributes of SegmentURL node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " media " , & new_segment_url - > media ) ;
gst_xml_helper_get_prop_range ( a_node , " mediaRange " ,
& new_segment_url - > mediaRange ) ;
gst_xml_helper_get_prop_string ( a_node , " index " , & new_segment_url - > index ) ;
gst_xml_helper_get_prop_range ( a_node , " indexRange " ,
& new_segment_url - > indexRange ) ;
}
static void
gst_mpdparser_parse_url_type_node ( GstMPDURLTypeNode * * pointer ,
xmlNode * a_node )
{
GstMPDURLTypeNode * new_url_type ;
gst_mpd_url_type_node_free ( * pointer ) ;
* pointer = new_url_type =
gst_mpd_url_type_node_new ( ( const gchar * ) a_node - > name ) ;
GST_LOG ( " attributes of URLType node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " sourceURL " ,
& new_url_type - > sourceURL ) ;
gst_xml_helper_get_prop_range ( a_node , " range " , & new_url_type - > range ) ;
}
static void
gst_mpdparser_parse_seg_base_type_ext ( GstMPDSegmentBaseNode * * pointer ,
xmlNode * a_node , GstMPDSegmentBaseNode * parent )
{
xmlNode * cur_node ;
GstMPDSegmentBaseNode * seg_base_type ;
guint intval ;
guint64 int64val ;
gboolean boolval ;
GstXMLRange * rangeval ;
gst_mpd_segment_base_node_free ( * pointer ) ;
* pointer = seg_base_type = gst_mpd_segment_base_node_new ( ) ;
/* Initialize values that have defaults */
seg_base_type - > indexRangeExact = FALSE ;
seg_base_type - > timescale = 1 ;
/* Inherit attribute values from parent */
if ( parent ) {
seg_base_type - > timescale = parent - > timescale ;
seg_base_type - > presentationTimeOffset = parent - > presentationTimeOffset ;
seg_base_type - > indexRange = gst_xml_helper_clone_range ( parent - > indexRange ) ;
seg_base_type - > indexRangeExact = parent - > indexRangeExact ;
seg_base_type - > Initialization =
gst_mpd_url_type_node_clone ( parent - > Initialization ) ;
seg_base_type - > RepresentationIndex =
gst_mpd_url_type_node_clone ( parent - > RepresentationIndex ) ;
}
/* We must retrieve each value first to see if it exists. If it does not
* exist , we do not want to overwrite an inherited value */
GST_LOG ( " attributes of SegmentBaseType extension: " ) ;
if ( gst_xml_helper_get_prop_unsigned_integer ( a_node , " timescale " , 1 ,
& intval ) ) {
seg_base_type - > timescale = intval ;
}
if ( gst_xml_helper_get_prop_unsigned_integer_64 ( a_node ,
" presentationTimeOffset " , 0 , & int64val ) ) {
seg_base_type - > presentationTimeOffset = int64val ;
}
if ( gst_xml_helper_get_prop_range ( a_node , " indexRange " , & rangeval ) ) {
if ( seg_base_type - > indexRange ) {
g_slice_free ( GstXMLRange , seg_base_type - > indexRange ) ;
}
seg_base_type - > indexRange = rangeval ;
}
if ( gst_xml_helper_get_prop_boolean ( a_node , " indexRangeExact " ,
FALSE , & boolval ) ) {
seg_base_type - > indexRangeExact = boolval ;
}
/* 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 ) {
/* parse will free the previous pointer to create a new one */
gst_mpdparser_parse_url_type_node ( & seg_base_type - > Initialization ,
cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " RepresentationIndex " ) = = 0 ) {
/* parse will free the previous pointer to create a new one */
gst_mpdparser_parse_url_type_node ( & seg_base_type - > RepresentationIndex ,
cur_node ) ;
}
}
}
}
static void
gst_mpdparser_parse_s_node ( GQueue * queue , xmlNode * a_node )
{
GstMPDSNode * new_s_node ;
new_s_node = gst_mpd_s_node_new ( ) ;
g_queue_push_tail ( queue , new_s_node ) ;
GST_LOG ( " attributes of S node: " ) ;
gst_xml_helper_get_prop_unsigned_integer_64 ( a_node , " t " , 0 , & new_s_node - > t ) ;
gst_xml_helper_get_prop_unsigned_integer_64 ( a_node , " d " , 0 , & new_s_node - > d ) ;
gst_xml_helper_get_prop_signed_integer ( a_node , " r " , 0 , & new_s_node - > r ) ;
}
static void
gst_mpdparser_parse_segment_timeline_node ( GstMPDSegmentTimelineNode * * pointer ,
xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDSegmentTimelineNode * new_seg_timeline ;
gst_mpd_segment_timeline_node_free ( * pointer ) ;
* pointer = new_seg_timeline = gst_mpd_segment_timeline_node_new ( ) ;
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 gboolean
gst_mpdparser_parse_mult_seg_base_node ( GstMPDMultSegmentBaseNode *
mult_seg_base_node , xmlNode * a_node , GstMPDMultSegmentBaseNode * parent )
{
xmlNode * cur_node ;
guint intval ;
gboolean has_timeline = FALSE , has_duration = FALSE ;
mult_seg_base_node - > duration = 0 ;
mult_seg_base_node - > startNumber = 1 ;
/* Inherit attribute values from parent */
if ( parent ) {
mult_seg_base_node - > duration = parent - > duration ;
mult_seg_base_node - > startNumber = parent - > startNumber ;
mult_seg_base_node - > SegmentTimeline =
gst_mpd_segment_timeline_node_clone ( parent - > SegmentTimeline ) ;
mult_seg_base_node - > BitstreamSwitching =
gst_mpd_url_type_node_clone ( parent - > BitstreamSwitching ) ;
}
GST_LOG ( " attributes of MultipleSegmentBaseType extension: " ) ;
if ( gst_xml_helper_get_prop_unsigned_integer ( a_node , " duration " , 0 , & intval ) ) {
mult_seg_base_node - > duration = intval ;
}
/* duration might be specified from parent */
if ( mult_seg_base_node - > duration )
has_duration = TRUE ;
if ( gst_xml_helper_get_prop_unsigned_integer ( a_node , " startNumber " , 1 ,
& intval ) ) {
mult_seg_base_node - > startNumber = intval ;
}
GST_LOG ( " extension of MultipleSegmentBaseType extension: " ) ;
gst_mpdparser_parse_seg_base_type_ext ( & mult_seg_base_node - > SegmentBase ,
a_node , ( parent ? parent - > SegmentBase : NULL ) ) ;
/* 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 ) {
/* parse frees the segmenttimeline if any */
gst_mpdparser_parse_segment_timeline_node
( & mult_seg_base_node - > SegmentTimeline , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " BitstreamSwitching " ) = = 0 ) {
/* parse frees the old url before setting the new one */
gst_mpdparser_parse_url_type_node
( & mult_seg_base_node - > BitstreamSwitching , cur_node ) ;
}
}
}
has_timeline = mult_seg_base_node - > SegmentTimeline ! = NULL ;
/* Checking duration and timeline only at Representation's child level */
if ( xmlStrcmp ( a_node - > parent - > name , ( xmlChar * ) " Representation " ) = = 0
& & ! has_duration & & ! has_timeline ) {
GST_ERROR ( " segment has neither duration nor timeline " ) ;
}
return TRUE ;
}
static gboolean
gst_mpdparser_parse_segment_list_node ( GstMPDSegmentListNode * * pointer ,
xmlNode * a_node , GstMPDSegmentListNode * parent )
{
xmlNode * cur_node ;
GstMPDSegmentListNode * new_segment_list ;
gchar * actuate ;
gboolean segment_urls_inherited_from_parent = FALSE ;
gst_mpd_segment_list_node_free ( * pointer ) ;
new_segment_list = gst_mpd_segment_list_node_new ( ) ;
/* Inherit attribute values from parent */
if ( parent ) {
GList * list ;
GstMPDSegmentURLNode * seg_url ;
for ( list = g_list_first ( parent - > SegmentURL ) ; list ;
list = g_list_next ( list ) ) {
seg_url = ( GstMPDSegmentURLNode * ) list - > data ;
new_segment_list - > SegmentURL =
g_list_append ( new_segment_list - > SegmentURL ,
gst_mpd_segment_url_node_clone ( seg_url ) ) ;
segment_urls_inherited_from_parent = TRUE ;
}
}
new_segment_list - > actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_segment_list - > xlink_href )
& & gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , GST_MPD_XLINK_ACTUATE_ON_LOAD_STR ) = = 0 )
new_segment_list - > actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
GST_LOG ( " extension of SegmentList node: " ) ;
if ( ! gst_mpdparser_parse_mult_seg_base_node
( GST_MPD_MULT_SEGMENT_BASE_NODE ( new_segment_list ) , a_node ,
( parent ? GST_MPD_MULT_SEGMENT_BASE_NODE ( parent ) : NULL ) ) )
goto error ;
/* 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 ) {
if ( segment_urls_inherited_from_parent ) {
/*
* SegmentBase , SegmentTemplate and SegmentList shall inherit
* attributes and elements from the same element on a higher level .
* If the same attribute or element is present on both levels ,
* the one on the lower level shall take precedence over the one
* on the higher level .
*/
/* Clear the list of inherited segment URLs */
g_list_free_full ( new_segment_list - > SegmentURL ,
( GDestroyNotify ) gst_mpd_segment_url_node_free ) ;
new_segment_list - > SegmentURL = NULL ;
/* mark the fact that we cleared the list, so that it is not tried again */
segment_urls_inherited_from_parent = FALSE ;
}
gst_mpdparser_parse_segment_url_node ( & new_segment_list - > SegmentURL ,
cur_node ) ;
}
}
}
* pointer = new_segment_list ;
return TRUE ;
error :
gst_mpd_segment_list_node_free ( new_segment_list ) ;
return FALSE ;
}
static void
gst_mpdparser_parse_content_protection_node ( GList * * list , xmlNode * a_node )
{
gchar * value = NULL ;
if ( gst_xml_helper_get_prop_string ( a_node , " value " , & value ) ) {
if ( ! g_strcmp0 ( value , " MSPR 2.0 " ) ) {
xmlNode * cur_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 * ) " pro " ) = = 0 ) {
GstMPDDescriptorTypeNode * new_descriptor ;
new_descriptor = gst_mpd_descriptor_type_node_new ( ( const gchar * )
cur_node - > name ) ;
* list = g_list_append ( * list , new_descriptor ) ;
gst_xml_helper_get_prop_string_stripped ( a_node , " schemeIdUri " ,
& new_descriptor - > schemeIdUri ) ;
gst_xml_helper_get_node_content ( cur_node , & new_descriptor - > value ) ;
goto beach ;
}
}
}
} else {
gst_mpdparser_parse_descriptor_type ( list , a_node ) ;
}
} else {
gst_mpdparser_parse_descriptor_type ( list , a_node ) ;
}
beach :
if ( value )
g_free ( value ) ;
}
static void
gst_mpdparser_parse_representation_base ( GstMPDRepresentationBaseNode *
representation_base , xmlNode * a_node )
{
xmlNode * cur_node ;
GST_LOG ( " attributes of RepresentationBaseType extension: " ) ;
gst_xml_helper_get_prop_string ( a_node , " profiles " ,
& representation_base - > profiles ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " width " , 0 ,
& representation_base - > width ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " height " , 0 ,
& representation_base - > height ) ;
gst_xml_helper_get_prop_ratio ( a_node , " sar " , & representation_base - > sar ) ;
gst_xml_helper_get_prop_framerate ( a_node , " frameRate " ,
& representation_base - > frameRate ) ;
gst_xml_helper_get_prop_framerate ( a_node , " minFrameRate " ,
& representation_base - > minFrameRate ) ;
gst_xml_helper_get_prop_framerate ( a_node , " maxFrameRate " ,
& representation_base - > maxFrameRate ) ;
gst_xml_helper_get_prop_string ( a_node , " audioSamplingRate " ,
& representation_base - > audioSamplingRate ) ;
gst_xml_helper_get_prop_string ( a_node , " mimeType " ,
& representation_base - > mimeType ) ;
gst_xml_helper_get_prop_string ( a_node , " segmentProfiles " ,
& representation_base - > segmentProfiles ) ;
gst_xml_helper_get_prop_string ( a_node , " codecs " ,
& representation_base - > codecs ) ;
if ( representation_base - > codecs ) {
GST_DEBUG ( " Getting caps " ) ;
representation_base - > caps =
gst_codec_utils_caps_from_mime_codec ( representation_base - > codecs ) ;
} else {
representation_base - > caps =
gst_mpd_helper_mimetype_to_codec_caps ( representation_base - > mimeType ) ;
GST_DEBUG ( " Getting caps from mime type gave % " GST_PTR_FORMAT ,
representation_base - > caps ) ;
}
gst_xml_helper_get_prop_double ( a_node , " maximumSAPPeriod " ,
& representation_base - > maximumSAPPeriod ) ;
gst_mpd_helper_get_SAP_type ( a_node , " startWithSAP " ,
& representation_base - > startWithSAP ) ;
gst_xml_helper_get_prop_double ( a_node , " maxPlayoutRate " ,
& representation_base - > maxPlayoutRate ) ;
gst_xml_helper_get_prop_boolean ( a_node , " codingDependency " ,
FALSE , & representation_base - > codingDependency ) ;
gst_xml_helper_get_prop_string ( a_node , " scanType " ,
& representation_base - > 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
( & representation_base - > FramePacking , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " AudioChannelConfiguration " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type
( & representation_base - > AudioChannelConfiguration , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " ContentProtection " ) = = 0 ) {
gst_mpdparser_parse_content_protection_node
( & representation_base - > ContentProtection , cur_node ) ;
}
}
}
}
static gboolean
gst_mpdparser_parse_representation_node ( GList * * list , xmlNode * a_node ,
GstMPDAdaptationSetNode * parent , GstMPDPeriodNode * period_node )
{
xmlNode * cur_node ;
GstMPDRepresentationNode * new_representation ;
new_representation = gst_mpd_representation_node_new ( ) ;
GST_LOG ( " attributes of Representation node: " ) ;
if ( ! gst_xml_helper_get_prop_string ( a_node , " id " , & new_representation - > id ) ) {
GST_ERROR ( " Cannot parse Representation id, invalid manifest " ) ;
goto error ;
}
if ( ! gst_xml_helper_get_prop_unsigned_integer ( a_node , " bandwidth " , 0 ,
& new_representation - > bandwidth ) ) {
GST_ERROR ( " Cannot parse Representation bandwidth, invalid manifest " ) ;
goto error ;
}
gst_xml_helper_get_prop_unsigned_integer ( a_node , " qualityRanking " , 0 ,
& new_representation - > qualityRanking ) ;
gst_xml_helper_get_prop_string_vector_type ( a_node , " dependencyId " ,
& new_representation - > dependencyId ) ;
gst_xml_helper_get_prop_string_vector_type ( a_node ,
" mediaStreamStructureId " , & new_representation - > mediaStreamStructureId ) ;
/* RepresentationBase extension */
gst_mpdparser_parse_representation_base
( GST_MPD_REPRESENTATION_BASE_NODE ( new_representation ) , 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 , parent - > SegmentBase ?
parent - > SegmentBase : period_node - > SegmentBase ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentTemplate " ) = = 0 ) {
if ( ! gst_mpdparser_parse_segment_template_node
( & new_representation - > SegmentTemplate , cur_node ,
parent - > SegmentTemplate ?
parent - > SegmentTemplate : period_node - > SegmentTemplate ) )
goto error ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
if ( ! gst_mpdparser_parse_segment_list_node
( & new_representation - > SegmentList , cur_node ,
parent - > SegmentList ? parent - > SegmentList : period_node - >
SegmentList ) )
goto error ;
} 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 ) ;
}
}
}
/* some sanity checking */
* list = g_list_append ( * list , new_representation ) ;
return TRUE ;
error :
gst_mpd_representation_node_free ( new_representation ) ;
return FALSE ;
}
static gboolean
gst_mpdparser_parse_adaptation_set_node ( GList * * list , xmlNode * a_node ,
GstMPDPeriodNode * parent )
{
xmlNode * cur_node ;
GstMPDAdaptationSetNode * new_adap_set ;
gchar * actuate ;
new_adap_set = gst_mpd_adaptation_set_node_new ( ) ;
GST_LOG ( " attributes of AdaptationSet node: " ) ;
new_adap_set - > actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_adap_set - > xlink_href )
& & gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , " onLoad " ) = = 0 )
new_adap_set - > actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
gst_xml_helper_get_prop_unsigned_integer ( a_node , " id " , 0 , & new_adap_set - > id ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " group " , 0 ,
& new_adap_set - > group ) ;
gst_xml_helper_get_prop_string ( a_node , " lang " , & new_adap_set - > lang ) ;
gst_xml_helper_get_prop_string ( a_node , " contentType " ,
& new_adap_set - > contentType ) ;
gst_xml_helper_get_prop_ratio ( a_node , " par " , & new_adap_set - > par ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " minBandwidth " , 0 ,
& new_adap_set - > minBandwidth ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " maxBandwidth " , 0 ,
& new_adap_set - > maxBandwidth ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " minWidth " , 0 ,
& new_adap_set - > minWidth ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " maxWidth " , 0 ,
& new_adap_set - > maxWidth ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " minHeight " , 0 ,
& new_adap_set - > minHeight ) ;
gst_xml_helper_get_prop_unsigned_integer ( a_node , " maxHeight " , 0 ,
& new_adap_set - > maxHeight ) ;
gst_xml_helper_get_prop_cond_uint ( a_node , " segmentAlignment " ,
& new_adap_set - > segmentAlignment ) ;
gst_xml_helper_get_prop_boolean ( a_node , " bitstreamSwitching " ,
parent - > bitstreamSwitching , & new_adap_set - > bitstreamSwitching ) ;
if ( parent - > bitstreamSwitching & & ! new_adap_set - > bitstreamSwitching ) {
/* according to the standard, if the Period's bitstreamSwitching attribute
* is true , the AdaptationSet should not have the bitstreamSwitching
* attribute set to false .
* We should return a parsing error , but we are generous and ignore the
* standard violation .
*/
new_adap_set - > bitstreamSwitching = parent - > bitstreamSwitching ;
}
gst_xml_helper_get_prop_cond_uint ( a_node , " subsegmentAlignment " ,
& new_adap_set - > subsegmentAlignment ) ;
gst_mpd_helper_get_SAP_type ( a_node , " subsegmentStartsWithSAP " ,
& new_adap_set - > subsegmentStartsWithSAP ) ;
/* RepresentationBase extension */
gst_mpdparser_parse_representation_base
( GST_MPD_REPRESENTATION_BASE_NODE ( new_adap_set ) , 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 ( & new_adap_set - > Accessibility ,
cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Role " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type ( & new_adap_set - > Role , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Rating " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type ( & new_adap_set - > Rating , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Viewpoint " ) = = 0 ) {
gst_mpdparser_parse_descriptor_type ( & new_adap_set - > Viewpoint ,
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 , parent - > SegmentBase ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
if ( ! gst_mpdparser_parse_segment_list_node ( & new_adap_set - > SegmentList ,
cur_node , parent - > SegmentList ) )
goto error ;
} 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 ) {
if ( ! gst_mpdparser_parse_segment_template_node
( & new_adap_set - > SegmentTemplate , cur_node , parent - > SegmentTemplate ) )
goto error ;
}
}
}
/* We must parse Representation after everything else in the AdaptationSet
* has been parsed because certain Representation child elements can inherit
* attributes specified by the same element in the AdaptationSet
*/
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 * ) " Representation " ) = = 0 ) {
if ( ! gst_mpdparser_parse_representation_node
( & new_adap_set - > Representations , cur_node , new_adap_set , parent ) )
goto error ;
}
}
}
* list = g_list_append ( * list , new_adap_set ) ;
return TRUE ;
error :
gst_mpd_adaptation_set_node_free ( new_adap_set ) ;
return FALSE ;
}
static void
gst_mpdparser_parse_subset_node ( GList * * list , xmlNode * a_node )
{
GstMPDSubsetNode * new_subset ;
new_subset = gst_mpd_subset_node_new ( ) ;
* list = g_list_append ( * list , new_subset ) ;
GST_LOG ( " attributes of Subset node: " ) ;
gst_xml_helper_get_prop_uint_vector_type ( a_node , " contains " ,
& new_subset - > contains , & new_subset - > contains_size ) ;
}
static gboolean
gst_mpdparser_parse_segment_template_node ( GstMPDSegmentTemplateNode * * pointer ,
xmlNode * a_node , GstMPDSegmentTemplateNode * parent )
{
GstMPDSegmentTemplateNode * new_segment_template ;
gchar * strval ;
gst_mpd_segment_template_node_free ( * pointer ) ;
new_segment_template = gst_mpd_segment_template_node_new ( ) ;
GST_LOG ( " extension of SegmentTemplate node: " ) ;
if ( ! gst_mpdparser_parse_mult_seg_base_node
( GST_MPD_MULT_SEGMENT_BASE_NODE ( new_segment_template ) , a_node ,
( parent ? GST_MPD_MULT_SEGMENT_BASE_NODE ( parent ) : NULL ) ) )
goto error ;
/* Inherit attribute values from parent when the value isn't found */
GST_LOG ( " attributes of SegmentTemplate node: " ) ;
if ( gst_xml_helper_get_prop_string ( a_node , " media " , & strval ) ) {
new_segment_template - > media = strval ;
} else if ( parent ) {
new_segment_template - > media = xmlMemStrdup ( parent - > media ) ;
}
if ( gst_xml_helper_get_prop_string ( a_node , " index " , & strval ) ) {
new_segment_template - > index = strval ;
} else if ( parent ) {
new_segment_template - > index = xmlMemStrdup ( parent - > index ) ;
}
if ( gst_xml_helper_get_prop_string ( a_node , " initialization " , & strval ) ) {
new_segment_template - > initialization = strval ;
} else if ( parent ) {
new_segment_template - > initialization =
xmlMemStrdup ( parent - > initialization ) ;
}
if ( gst_xml_helper_get_prop_string ( a_node , " bitstreamSwitching " , & strval ) ) {
new_segment_template - > bitstreamSwitching = strval ;
} else if ( parent ) {
new_segment_template - > bitstreamSwitching =
xmlMemStrdup ( parent - > bitstreamSwitching ) ;
}
* pointer = new_segment_template ;
return TRUE ;
error :
gst_mpd_segment_template_node_free ( new_segment_template ) ;
return FALSE ;
}
static gboolean
gst_mpdparser_parse_period_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDPeriodNode * new_period ;
gchar * actuate ;
new_period = gst_mpd_period_node_new ( ) ;
GST_LOG ( " attributes of Period node: " ) ;
new_period - > actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_period - > xlink_href )
& & gst_xml_helper_get_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , " onLoad " ) = = 0 )
new_period - > actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
gst_xml_helper_get_prop_string ( a_node , " id " , & new_period - > id ) ;
gst_xml_helper_get_prop_duration ( a_node , " start " , GST_MPD_DURATION_NONE ,
& new_period - > start ) ;
gst_xml_helper_get_prop_duration ( a_node , " duration " ,
GST_MPD_DURATION_NONE , & new_period - > duration ) ;
gst_xml_helper_get_prop_boolean ( a_node , " bitstreamSwitching " , FALSE ,
& new_period - > 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 * ) " SegmentBase " ) = = 0 ) {
gst_mpdparser_parse_seg_base_type_ext ( & new_period - > SegmentBase ,
cur_node , NULL ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
if ( ! gst_mpdparser_parse_segment_list_node ( & new_period - > SegmentList ,
cur_node , NULL ) )
goto error ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentTemplate " ) = = 0 ) {
if ( ! gst_mpdparser_parse_segment_template_node
( & new_period - > SegmentTemplate , cur_node , NULL ) )
goto error ;
} 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 ) ;
}
}
}
/* We must parse AdaptationSet after everything else in the Period has been
* parsed because certain AdaptationSet child elements can inherit attributes
* specified by the same element in the Period
*/
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 ) {
if ( ! gst_mpdparser_parse_adaptation_set_node
( & new_period - > AdaptationSets , cur_node , new_period ) )
goto error ;
}
}
}
* list = g_list_append ( * list , new_period ) ;
return TRUE ;
error :
gst_mpd_period_node_free ( new_period ) ;
return FALSE ;
}
static void
gst_mpdparser_parse_program_info_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDProgramInformationNode * new_prog_info ;
new_prog_info = gst_mpd_program_information_node_new ( ) ;
* list = g_list_append ( * list , new_prog_info ) ;
GST_LOG ( " attributes of ProgramInformation node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " lang " , & new_prog_info - > lang ) ;
gst_xml_helper_get_prop_string ( a_node , " moreInformationURL " ,
& new_prog_info - > 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 ) {
gst_xml_helper_get_node_content ( cur_node , & new_prog_info - > Title ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Source " ) = = 0 ) {
gst_xml_helper_get_node_content ( cur_node , & new_prog_info - > Source ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Copyright " ) = = 0 ) {
gst_xml_helper_get_node_content ( cur_node , & new_prog_info - > Copyright ) ;
}
}
}
}
static void
gst_mpdparser_parse_metrics_range_node ( GList * * list , xmlNode * a_node )
{
GstMPDMetricsRangeNode * new_metrics_range ;
new_metrics_range = gst_mpd_metrics_range_node_new ( ) ;
* list = g_list_append ( * list , new_metrics_range ) ;
GST_LOG ( " attributes of Metrics Range node: " ) ;
gst_xml_helper_get_prop_duration ( a_node , " starttime " ,
GST_MPD_DURATION_NONE , & new_metrics_range - > starttime ) ;
gst_xml_helper_get_prop_duration ( a_node , " duration " ,
GST_MPD_DURATION_NONE , & new_metrics_range - > duration ) ;
}
static void
gst_mpdparser_parse_metrics_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDMetricsNode * new_metrics ;
new_metrics = gst_mpd_metrics_node_new ( ) ;
* list = g_list_append ( * list , new_metrics ) ;
GST_LOG ( " attributes of Metrics node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " metrics " , & new_metrics - > 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) " ) ;
}
}
}
}
/* The UTCTiming element is defined in
* ISO / IEC 23009 - 1 : 2014 / PDAM 1 " Information technology — Dynamic adaptive streaming over HTTP (DASH) — Part 1: Media presentation description and segment formats / Amendment 1: High Profile and Availability Time Synchronization "
*/
static void
gst_mpdparser_parse_utctiming_node ( GList * * list , xmlNode * a_node )
{
GstMPDUTCTimingNode * new_timing ;
gchar * method = NULL ;
gchar * value = NULL ;
new_timing = gst_mpd_utctiming_node_new ( ) ;
GST_LOG ( " attributes of UTCTiming node: " ) ;
if ( gst_xml_helper_get_prop_string ( a_node , " schemeIdUri " , & method ) ) {
new_timing - > method = gst_mpd_utctiming_get_method ( method ) ;
xmlFree ( method ) ;
}
if ( gst_xml_helper_get_prop_string ( a_node , " value " , & value ) ) {
int max_tokens = 0 ;
if ( GST_MPD_UTCTIMING_TYPE_DIRECT = = new_timing - > method ) {
/* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
* that is not a space separated list .
*/
max_tokens = 1 ;
}
new_timing - > urls = g_strsplit ( value , " " , max_tokens ) ;
xmlFree ( value ) ;
}
/* append to list only if both method and urls were set */
if ( new_timing - > method ! = 0 & & new_timing - > urls ! = NULL & &
g_strv_length ( new_timing - > urls ) ! = 0 ) {
* list = g_list_append ( * list , new_timing ) ;
} else {
gst_mpd_utctiming_node_free ( new_timing ) ;
}
}
static gboolean
gst_mpdparser_parse_root_node ( GstMPDRootNode * * pointer , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMPDRootNode * new_mpd_root ;
gst_mpd_root_node_free ( * pointer ) ;
* pointer = NULL ;
new_mpd_root = gst_mpd_root_node_new ( ) ;
GST_LOG ( " namespaces of root MPD node: " ) ;
new_mpd_root - > default_namespace =
gst_xml_helper_get_node_namespace ( a_node , NULL ) ;
new_mpd_root - > namespace_xsi =
gst_xml_helper_get_node_namespace ( a_node , " xsi " ) ;
new_mpd_root - > namespace_ext =
gst_xml_helper_get_node_namespace ( a_node , " ext " ) ;
GST_LOG ( " attributes of root MPD node: " ) ;
gst_xml_helper_get_prop_string ( a_node , " schemaLocation " ,
& new_mpd_root - > schemaLocation ) ;
gst_xml_helper_get_prop_string ( a_node , " id " , & new_mpd_root - > id ) ;
gst_xml_helper_get_prop_string ( a_node , " profiles " , & new_mpd_root - > profiles ) ;
gst_mpd_helper_get_mpd_type ( a_node , " type " , & new_mpd_root - > type ) ;
gst_xml_helper_get_prop_dateTime ( a_node , " availabilityStartTime " ,
& new_mpd_root - > availabilityStartTime ) ;
gst_xml_helper_get_prop_dateTime ( a_node , " availabilityEndTime " ,
& new_mpd_root - > availabilityEndTime ) ;
gst_xml_helper_get_prop_duration ( a_node , " mediaPresentationDuration " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > mediaPresentationDuration ) ;
gst_xml_helper_get_prop_duration ( a_node , " minimumUpdatePeriod " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > minimumUpdatePeriod ) ;
gst_xml_helper_get_prop_duration ( a_node , " minBufferTime " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > minBufferTime ) ;
gst_xml_helper_get_prop_duration ( a_node , " timeShiftBufferDepth " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > timeShiftBufferDepth ) ;
gst_xml_helper_get_prop_duration ( a_node , " suggestedPresentationDelay " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > suggestedPresentationDelay ) ;
gst_xml_helper_get_prop_duration ( a_node , " maxSegmentDuration " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > maxSegmentDuration ) ;
gst_xml_helper_get_prop_duration ( a_node , " maxSubsegmentDuration " ,
GST_MPD_DURATION_NONE , & new_mpd_root - > 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 ) {
if ( ! gst_mpdparser_parse_period_node ( & new_mpd_root - > Periods , cur_node ) )
goto error ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " ProgramInformation " ) = = 0 ) {
gst_mpdparser_parse_program_info_node ( & new_mpd_root - > ProgramInfos ,
cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " BaseURL " ) = = 0 ) {
gst_mpdparser_parse_baseURL_node ( & new_mpd_root - > BaseURLs , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Location " ) = = 0 ) {
gst_mpdparser_parse_location_node ( & new_mpd_root - > Locations , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Metrics " ) = = 0 ) {
gst_mpdparser_parse_metrics_node ( & new_mpd_root - > Metrics , cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " UTCTiming " ) = = 0 ) {
gst_mpdparser_parse_utctiming_node ( & new_mpd_root - > UTCTimings ,
cur_node ) ;
}
}
}
* pointer = new_mpd_root ;
return TRUE ;
error :
gst_mpd_root_node_free ( new_mpd_root ) ;
return FALSE ;
}
/* internal memory management functions */
/* ISO/IEC 23009-1:2004 5.3.9.4.4 */
static gboolean
validate_format ( const gchar * format )
{
const gchar * p = format ;
/* Check if it starts with % */
if ( ! p | | p [ 0 ] ! = ' % ' )
return FALSE ;
p + + ;
/* the spec mandates a format like %0[width]d */
/* Following the %, we must have a 0 */
if ( p [ 0 ] ! = ' 0 ' )
return FALSE ;
/* Following the % must be a number starting with 0
*/
while ( g_ascii_isdigit ( * p ) )
p + + ;
/* After any 0 and alphanumeric values, there must be a d.
*/
if ( p [ 0 ] ! = ' d ' )
return FALSE ;
p + + ;
/* And then potentially more characters without any
* further % , even if the spec does not mention this
*/
p = strchr ( p , ' % ' ) ;
if ( p )
return FALSE ;
return TRUE ;
}
static gchar *
promote_format_to_uint64 ( const gchar * format )
{
const gchar * p = format ;
gchar * promoted_format ;
/* Must be called with a validated format! */
g_return_val_if_fail ( validate_format ( format ) , NULL ) ;
/* it starts with % */
p + + ;
/* Following the % must be a 0, or any of d, x or u.
* x and u are not part of the spec , but don ' t hurt us
*/
if ( p [ 0 ] = = ' 0 ' ) {
p + + ;
while ( g_ascii_isdigit ( * p ) )
p + + ;
}
/* After any 0 and alphanumeric values, there must be a d.
* Otherwise validation would have failed
*/
g_assert ( p [ 0 ] = = ' d ' ) ;
promoted_format =
g_strdup_printf ( " %.*s " G_GINT64_MODIFIER " %s " , ( gint ) ( p - format ) ,
format , p ) ;
return promoted_format ;
}
static gboolean
gst_mpdparser_validate_rfc1738_url ( const char * s )
{
while ( * s ) {
if ( ! strchr
( " ;:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/ " ,
* s ) )
return FALSE ;
if ( * s = = ' % ' ) {
/* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
operator , so this is safe for strings ending before two hex digits */
if ( ! g_ascii_isxdigit ( s [ 1 ] ) | | ! g_ascii_isxdigit ( s [ 2 ] ) )
return FALSE ;
s + = 2 ;
}
s + + ;
}
return TRUE ;
}
void
gst_mpdparser_media_fragment_info_clear ( GstMediaFragmentInfo * fragment )
{
g_free ( fragment - > uri ) ;
g_free ( fragment - > index_uri ) ;
}
/* API */
gboolean
gst_mpdparser_get_mpd_root_node ( GstMPDRootNode * * mpd_root_node ,
const gchar * data , gint size )
{
gboolean ret = FALSE ;
if ( data ) {
xmlDocPtr doc ;
xmlNode * root_element = NULL ;
GST_DEBUG ( " MPD file fully buffered, start parsing... " ) ;
/* 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 , XML_PARSE_NONET ) ;
if ( doc = = NULL ) {
GST_ERROR ( " failed to parse the MPD file " ) ;
ret = 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 " ) ;
ret = FALSE ; /* used to return TRUE before, but this seems wrong */
} else {
/* now we can parse the MPD root node and all children nodes, recursively */
ret = gst_mpdparser_parse_root_node ( mpd_root_node , root_element ) ;
}
/* free the document */
xmlFreeDoc ( doc ) ;
}
}
return ret ;
}
GstMPDSegmentListNode *
gst_mpdparser_get_external_segment_list ( const gchar * data , gint size ,
GstMPDSegmentListNode * parent )
{
xmlDocPtr doc = NULL ;
GstMPDSegmentListNode * new_segment_list = NULL ;
doc = xmlReadMemory ( data , size , " noname.xml " , NULL , XML_PARSE_NONET ) ;
/* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
* in external xml is allowed , however , multiple SegmentList does not make sense
* because Period / AdaptationSet / Representation allow only one SegmentList */
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type = = XML_ELEMENT_NODE & &
xmlStrcmp ( root_element - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
gst_mpdparser_parse_segment_list_node ( & new_segment_list , root_element ,
parent ) ;
}
}
if ( doc )
xmlFreeDoc ( doc ) ;
return new_segment_list ;
}
GList *
gst_mpdparser_get_external_periods ( const gchar * data , gint size )
{
xmlDocPtr doc = NULL ;
GList * new_periods = NULL ;
doc = xmlReadMemory ( data , size , " noname.xml " , NULL , XML_PARSE_NONET ) ;
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
xmlNode * iter ;
for ( iter = root_element - > children ; iter ; iter = iter - > next ) {
if ( iter - > type = = XML_ELEMENT_NODE ) {
if ( xmlStrcmp ( iter - > name , ( xmlChar * ) " Period " ) = = 0 ) {
gst_mpdparser_parse_period_node ( & new_periods , iter ) ;
} else {
goto error ;
}
}
}
}
done :
if ( doc )
xmlFreeDoc ( doc ) ;
return new_periods ;
error :
GST_ERROR ( " Failed to parse period node XML " ) ;
if ( new_periods ) {
g_list_free_full ( new_periods , ( GDestroyNotify ) gst_mpd_period_node_free ) ;
new_periods = NULL ;
}
goto done ;
}
GList *
gst_mpdparser_get_external_adaptation_sets ( const gchar * data , gint size ,
GstMPDPeriodNode * period )
{
xmlDocPtr doc = NULL ;
GList * new_adaptation_sets = NULL ;
doc = xmlReadMemory ( data , size , " noname.xml " , NULL , XML_PARSE_NONET ) ;
/* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
* in external xml is allowed */
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type = = XML_ELEMENT_NODE & &
xmlStrcmp ( root_element - > name , ( xmlChar * ) " AdaptationSet " ) = = 0 ) {
gst_mpdparser_parse_adaptation_set_node ( & new_adaptation_sets ,
root_element , period ) ;
}
}
if ( doc )
xmlFreeDoc ( doc ) ;
return new_adaptation_sets ;
}
void
gst_mpdparser_free_stream_period ( GstStreamPeriod * stream_period )
{
if ( stream_period ) {
g_slice_free ( GstStreamPeriod , stream_period ) ;
}
}
void
gst_mpdparser_free_media_segment ( GstMediaSegment * media_segment )
{
if ( media_segment ) {
g_slice_free ( GstMediaSegment , media_segment ) ;
}
}
void
gst_mpdparser_init_active_stream_segments ( GstActiveStream * stream )
{
g_assert ( stream - > segments = = NULL ) ;
stream - > segments = g_ptr_array_new ( ) ;
g_ptr_array_set_free_func ( stream - > segments ,
( GDestroyNotify ) gst_mpdparser_free_media_segment ) ;
}
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 ;
if ( active_stream - > segments )
g_ptr_array_unref ( active_stream - > segments ) ;
g_slice_free ( GstActiveStream , active_stream ) ;
}
}
2021-10-14 08:12:51 +00:00
static gchar *
get_base_url_with_query ( GstActiveStream * stream )
{
GstUri * uri ;
gchar * uri_str ;
if ( ! stream - > queryURL )
return g_strdup ( stream - > baseURL ) ;
uri = gst_uri_from_string ( stream - > baseURL ) ;
gst_uri_set_query_string ( uri , stream - > queryURL ) ;
uri_str = gst_uri_to_string ( uri ) ;
gst_uri_unref ( uri ) ;
return uri_str ;
}
2021-10-14 08:09:31 +00:00
/*
* gst_mpdparser_get_initializationURL :
*
* Returns : ( transfer full ) : stream initializationURL if available ,
2021-10-14 08:12:51 +00:00
* baseURL combined with queryURL otherwise .
2021-10-14 08:09:31 +00:00
*/
gchar *
2022-03-11 16:11:50 +00:00
gst_mpdparser_get_initializationURL ( GstActiveStream * stream ,
GstMPDURLTypeNode * InitializationURL )
{
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
2021-10-14 08:12:51 +00:00
return ( InitializationURL & & InitializationURL - > sourceURL )
? g_strdup ( InitializationURL - > sourceURL )
: get_base_url_with_query ( stream ) ;
2022-03-11 16:11:50 +00:00
}
2021-10-14 08:09:31 +00:00
/*
* gst_mpdparser_get_mediaURL :
*
* Returns : ( transfer full ) : stream mediaURL if available ,
2021-10-14 08:12:51 +00:00
* baseURL combined with queryURL otherwise .
2021-10-14 08:09:31 +00:00
*/
2022-03-11 16:11:50 +00:00
gchar *
gst_mpdparser_get_mediaURL ( GstActiveStream * stream ,
GstMPDSegmentURLNode * segmentURL )
{
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
g_return_val_if_fail ( segmentURL ! = NULL , NULL ) ;
2021-10-14 08:12:51 +00:00
return ( segmentURL - > media )
? g_strdup ( segmentURL - > media )
: get_base_url_with_query ( stream ) ;
2022-03-11 16:11:50 +00:00
}
/* navigation functions */
GstStreamMimeType
gst_mpdparser_representation_get_mimetype ( GstMPDAdaptationSetNode * adapt_set ,
GstMPDRepresentationNode * rep )
{
gchar * mime = NULL ;
if ( rep )
mime = GST_MPD_REPRESENTATION_BASE_NODE ( rep ) - > mimeType ;
if ( mime = = NULL ) {
mime = GST_MPD_REPRESENTATION_BASE_NODE ( adapt_set ) - > mimeType ;
}
if ( gst_mpd_helper_strncmp_ext ( mime , " audio " ) = = 0 )
return GST_STREAM_AUDIO ;
if ( gst_mpd_helper_strncmp_ext ( mime , " video " ) = = 0 )
return GST_STREAM_VIDEO ;
if ( gst_mpd_helper_strncmp_ext ( mime , " application " ) = = 0
| | gst_mpd_helper_strncmp_ext ( mime , " text " ) = = 0 )
return GST_STREAM_APPLICATION ;
return GST_STREAM_UNKNOWN ;
}
/* Helper methods */
gchar *
gst_mpdparser_build_URL_from_template ( const gchar * url_template ,
const gchar * id , guint number , guint bandwidth , guint64 time )
{
static const gchar default_format [ ] = " %01d " ;
gchar * * tokens , * token , * ret ;
const gchar * format ;
gint i , num_tokens ;
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 ) ;
/*
* each identifier is guarded by 2 $ , which means that we must have an odd number of tokens
* An even number of tokens means the string is not valid .
*/
if ( ( num_tokens & 1 ) = = 0 ) {
GST_ERROR ( " Invalid number of tokens (%d). url_template is '%s' " ,
num_tokens , url_template ) ;
g_strfreev ( tokens ) ;
return NULL ;
}
for ( i = 0 ; i < num_tokens ; i + + ) {
token = tokens [ i ] ;
format = default_format ;
/* the tokens to replace must be provided between $ characters, eg $token$
* For a string like token0 $ token1 $ token2 $ token3 $ token4 , only the odd number
* tokens ( 1 , 3 , . . . ) must be parsed .
*
* Skip even tokens
*/
if ( ( i & 1 ) = = 0 )
continue ;
if ( ! g_strcmp0 ( token , " RepresentationID " ) ) {
if ( ! gst_mpdparser_validate_rfc1738_url ( id ) )
goto invalid_representation_id ;
tokens [ i ] = g_strdup_printf ( " %s " , id ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Number " , 6 ) ) {
if ( strlen ( token ) > 6 ) {
format = token + 6 ; /* format tag */
}
if ( ! validate_format ( format ) )
goto invalid_format ;
tokens [ i ] = g_strdup_printf ( format , number ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Bandwidth " , 9 ) ) {
if ( strlen ( token ) > 9 ) {
format = token + 9 ; /* format tag */
}
if ( ! validate_format ( format ) )
goto invalid_format ;
tokens [ i ] = g_strdup_printf ( format , bandwidth ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Time " , 4 ) ) {
gchar * promoted_format ;
if ( strlen ( token ) > 4 ) {
format = token + 4 ; /* format tag */
}
if ( ! validate_format ( format ) )
goto invalid_format ;
promoted_format = promote_format_to_uint64 ( format ) ;
tokens [ i ] = g_strdup_printf ( promoted_format , time ) ;
g_free ( promoted_format ) ;
g_free ( token ) ;
} else if ( ! g_strcmp0 ( token , " " ) ) {
tokens [ i ] = g_strdup_printf ( " %s " , " $ " ) ;
g_free ( token ) ;
} else {
/* unexpected identifier found between $ signs
*
* " If the URL contains unescaped $ symbols which do not enclose a valid
* identifier then the result of URL formation is undefined "
*/
goto invalid_format ;
}
}
ret = g_strjoinv ( NULL , tokens ) ;
g_strfreev ( tokens ) ;
return ret ;
invalid_format :
{
GST_ERROR ( " Invalid format '%s' in '%s' " , format , token ) ;
g_strfreev ( tokens ) ;
return NULL ;
}
invalid_representation_id :
{
GST_ERROR
( " Representation ID string '%s' has characters invalid in an RFC 1738 URL " ,
id ) ;
g_strfreev ( tokens ) ;
return NULL ;
}
}