2013-05-08 14:13:32 +00:00
/*
2012-10-08 08:24:29 +00:00
* DASH MPD parsing library
2013-05-08 14:13:32 +00:00
*
2012-10-08 08:24:29 +00:00
* gstmpdparser . c
2013-05-08 14:13:32 +00:00
*
2012-10-08 08:24:29 +00:00
* Copyright ( C ) 2012 STMicroelectronics
2013-05-08 14:13:32 +00:00
*
2012-10-08 08:24:29 +00:00
* Authors :
* Gianluca Gennari < gennarone @ gmail . com >
2013-05-08 14:13:32 +00:00
*
* 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
2012-10-08 08:24:29 +00:00
* version 2.1 of the License , or ( at your option ) any later version .
2013-05-08 14:13:32 +00:00
*
* 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
2012-10-08 08:24:29 +00:00
* License along with this library ( COPYING ) ; if not , write to the
2013-05-08 14:13:32 +00:00
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
# include <string.h>
2013-01-25 12:36:35 +00:00
# include <libxml/parser.h>
# include <libxml/tree.h>
2013-05-08 14:13:32 +00:00
# include "gstmpdparser.h"
2013-06-25 21:34:13 +00:00
# include "gstdash_debug.h"
# define GST_CAT_DEFAULT gst_dash_demux_debug
2013-05-08 14:13:32 +00:00
/* Property parsing */
2015-09-29 15:17:03 +00:00
static gboolean gst_mpdparser_get_xml_prop_validated_string ( xmlNode * a_node ,
const gchar * property_name , gchar * * property_value ,
gboolean ( * validator ) ( const char * ) ) ;
2013-06-21 21:51:46 +00:00
static gboolean gst_mpdparser_get_xml_prop_string ( xmlNode * a_node ,
const gchar * property_name , gchar * * property_value ) ;
2015-07-10 15:56:29 +00:00
static gboolean gst_mpdparser_get_xml_ns_prop_string ( xmlNode * a_node ,
const gchar * ns_name , const gchar * property_name ,
gchar * * property_value ) ;
2013-06-21 21:51:46 +00:00
static gboolean gst_mpdparser_get_xml_prop_string_vector_type ( xmlNode * a_node ,
const gchar * property_name , gchar * * * property_value ) ;
2015-09-08 14:14:13 +00:00
static gboolean gst_mpdparser_get_xml_prop_signed_integer ( xmlNode * a_node ,
const gchar * property_name , gint default_val , gint * property_value ) ;
2013-06-21 21:51:46 +00:00
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer ( xmlNode * a_node ,
const gchar * property_name , guint default_val , guint * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 ( xmlNode *
a_node , const gchar * property_name , guint64 default_val ,
guint64 * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_uint_vector_type ( xmlNode * a_node ,
const gchar * property_name , guint * * property_value , guint * value_size ) ;
static gboolean gst_mpdparser_get_xml_prop_double ( xmlNode * a_node ,
const gchar * property_name , gdouble * property_value ) ;
2012-12-20 08:04:28 +00:00
static gboolean gst_mpdparser_get_xml_prop_boolean ( xmlNode * a_node ,
2013-06-21 21:51:46 +00:00
const gchar * property_name , gboolean default_val ,
gboolean * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_type ( xmlNode * a_node ,
const gchar * property_name , GstMPDFileType * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_SAP_type ( xmlNode * a_node ,
const gchar * property_name , GstSAPType * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_range ( xmlNode * a_node ,
const gchar * property_name , GstRange * * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_ratio ( xmlNode * a_node ,
const gchar * property_name , GstRatio * * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_framerate ( xmlNode * a_node ,
const gchar * property_name , GstFrameRate * * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_cond_uint ( xmlNode * a_node ,
const gchar * property_name , GstConditionalUintType * * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_dateTime ( xmlNode * a_node ,
const gchar * property_name , GstDateTime * * property_value ) ;
static gboolean gst_mpdparser_get_xml_prop_duration ( xmlNode * a_node ,
2015-10-29 11:38:35 +00:00
const gchar * property_name , guint64 default_value ,
guint64 * property_value ) ;
2013-06-21 21:51:46 +00:00
static gboolean gst_mpdparser_get_xml_node_content ( xmlNode * a_node ,
gchar * * content ) ;
2012-12-20 08:04:28 +00:00
static gchar * gst_mpdparser_get_xml_node_namespace ( xmlNode * a_node ,
const gchar * prefix ) ;
2015-02-06 13:22:14 +00:00
static gboolean gst_mpdparser_get_xml_node_as_string ( xmlNode * a_node ,
gchar * * content ) ;
2013-05-08 14:13:32 +00:00
/* XML node parsing */
static void gst_mpdparser_parse_baseURL_node ( GList * * list , xmlNode * a_node ) ;
2012-12-20 08:04:28 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_parse_location_node ( GList * * list , xmlNode * a_node ) ;
2012-12-20 08:04:28 +00:00
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 * *
2013-06-21 23:09:30 +00:00
pointer , xmlNode * a_node , GstSegmentBaseType * parent ) ;
2013-07-05 02:42:23 +00:00
static void gst_mpdparser_parse_s_node ( GQueue * queue , xmlNode * a_node ) ;
2012-12-20 08:04:28 +00:00
static void gst_mpdparser_parse_segment_timeline_node ( GstSegmentTimelineNode * *
pointer , xmlNode * a_node ) ;
2015-10-28 15:34:29 +00:00
static gboolean
gst_mpdparser_parse_mult_seg_base_type_ext ( GstMultSegmentBaseType * * pointer ,
xmlNode * a_node , GstMultSegmentBaseType * parent ) ;
static gboolean gst_mpdparser_parse_segment_list_node ( GstSegmentListNode * *
2013-06-21 23:09:30 +00:00
pointer , xmlNode * a_node , GstSegmentListNode * parent ) ;
2012-12-20 08:04:28 +00:00
static void
gst_mpdparser_parse_representation_base_type ( GstRepresentationBaseType * *
pointer , xmlNode * a_node ) ;
2015-10-28 15:34:29 +00:00
static gboolean gst_mpdparser_parse_representation_node ( GList * * list ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstAdaptationSetNode * parent ) ;
2015-10-28 15:34:29 +00:00
static gboolean gst_mpdparser_parse_adaptation_set_node ( GList * * list ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstPeriodNode * parent ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_parse_subset_node ( GList * * list , xmlNode * a_node ) ;
2015-10-28 15:34:29 +00:00
static gboolean
gst_mpdparser_parse_segment_template_node ( GstSegmentTemplateNode * * pointer ,
xmlNode * a_node , GstSegmentTemplateNode * parent ) ;
static gboolean gst_mpdparser_parse_period_node ( GList * * list ,
xmlNode * a_node ) ;
2012-12-20 08:04:28 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_parse_metrics_node ( GList * * list , xmlNode * a_node ) ;
2015-10-28 15:34:29 +00:00
static gboolean gst_mpdparser_parse_root_node ( GstMPDNode * * pointer ,
2012-12-20 08:04:28 +00:00
xmlNode * a_node ) ;
2015-07-15 10:56:13 +00:00
static void gst_mpdparser_parse_utctiming_node ( GList * * list ,
xmlNode * a_node ) ;
2013-05-08 14:13:32 +00:00
/* Helper functions */
2015-10-29 11:38:35 +00:00
static guint convert_to_millisecs ( guint decimals , gint pos ) ;
2013-05-08 14:13:32 +00:00
static int strncmp_ext ( const char * s1 , const char * s2 ) ;
2012-10-22 16:12:30 +00:00
static GstStreamPeriod * gst_mpdparser_get_stream_period ( GstMpdClient * client ) ;
2013-06-21 23:09:30 +00:00
static GstSNode * gst_mpdparser_clone_s_node ( GstSNode * pointer ) ;
static GstSegmentTimelineNode
* gst_mpdparser_clone_segment_timeline ( GstSegmentTimelineNode * pointer ) ;
static GstRange * gst_mpdparser_clone_range ( GstRange * range ) ;
static GstURLType * gst_mpdparser_clone_URL ( GstURLType * url ) ;
2013-01-16 18:58:52 +00:00
static gchar * gst_mpdparser_parse_baseURL ( GstMpdClient * client ,
2013-02-13 00:54:32 +00:00
GstActiveStream * stream , gchar * * query ) ;
2013-06-21 23:09:30 +00:00
static GstSegmentURLNode * gst_mpdparser_clone_segment_url ( GstSegmentURLNode *
seg_url ) ;
2013-01-16 18:58:52 +00:00
static gchar * gst_mpdparser_get_mediaURL ( GstActiveStream * stream ,
2012-12-20 08:04:28 +00:00
GstSegmentURLNode * segmentURL ) ;
2013-12-02 20:41:01 +00:00
static const gchar * gst_mpdparser_get_initializationURL ( GstActiveStream *
stream , GstURLType * InitializationURL ) ;
2012-12-20 08:04:28 +00:00
static gchar * gst_mpdparser_build_URL_from_template ( const gchar * url_template ,
2013-02-19 22:35:34 +00:00
const gchar * id , guint number , guint bandwidth , guint64 time ) ;
2012-12-20 08:04:28 +00:00
static gboolean gst_mpd_client_add_media_segment ( GstActiveStream * stream ,
2015-10-29 11:38:35 +00:00
GstSegmentURLNode * url_node , guint number , gint repeat ,
guint64 scale_start , guint64 scale_duration , GstClockTime start ,
GstClockTime duration ) ;
2012-10-19 18:12:09 +00:00
static const gchar * gst_mpdparser_mimetype_to_caps ( const gchar * mimeType ) ;
2013-01-29 14:22:52 +00:00
static GstClockTime gst_mpd_client_get_segment_duration ( GstMpdClient * client ,
2015-10-29 11:38:35 +00:00
GstActiveStream * stream , guint64 * scale_duration ) ;
2013-06-11 13:28:53 +00:00
static GstDateTime * gst_mpd_client_get_availability_start_time ( GstMpdClient *
client ) ;
2013-05-08 14:13:32 +00:00
/* Representation */
2012-12-20 08:04:28 +00:00
static GstRepresentationNode * gst_mpdparser_get_lowest_representation ( GList *
Representations ) ;
2013-05-08 14:13:32 +00:00
#if 0
2012-12-20 08:04:28 +00:00
static GstRepresentationNode * gst_mpdparser_get_highest_representation ( GList *
Representations ) ;
static GstRepresentationNode
2013-01-25 12:36:35 +00:00
* gst_mpdparser_get_representation_with_max_bandwidth ( GList *
2012-12-20 08:04:28 +00:00
Representations , gint max_bandwidth ) ;
2013-05-08 14:13:32 +00:00
# endif
2012-12-20 08:04:28 +00:00
static GstSegmentBaseType * gst_mpdparser_get_segment_base ( GstPeriodNode *
Period , GstAdaptationSetNode * AdaptationSet ,
GstRepresentationNode * Representation ) ;
2015-09-08 10:36:23 +00:00
static GstSegmentListNode * gst_mpdparser_get_segment_list ( GstMpdClient *
client , GstPeriodNode * Period , GstAdaptationSetNode * AdaptationSet ,
2012-12-20 08:04:28 +00:00
GstRepresentationNode * Representation ) ;
2013-05-08 14:13:32 +00:00
2013-02-22 19:40:36 +00:00
/* Segments */
2015-06-05 12:10:43 +00:00
static guint gst_mpd_client_get_segments_counts ( GstMpdClient * client ,
GstActiveStream * stream ) ;
2013-02-22 19:40:36 +00:00
2013-05-08 14:13:32 +00:00
/* Memory management */
2013-12-02 20:41:01 +00:00
static GstSegmentTimelineNode * gst_mpdparser_segment_timeline_node_new ( void ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_mpd_node ( GstMPDNode * mpd_node ) ;
2012-12-20 08:04:28 +00:00
static void gst_mpdparser_free_prog_info_node ( GstProgramInformationNode *
prog_info_node ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_metrics_node ( GstMetricsNode * metrics_node ) ;
2012-12-20 08:04:28 +00:00
static void gst_mpdparser_free_metrics_range_node ( GstMetricsRangeNode *
metrics_range_node ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_period_node ( GstPeriodNode * period_node ) ;
static void gst_mpdparser_free_subset_node ( GstSubsetNode * subset_node ) ;
2012-12-20 08:04:28 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_s_node ( GstSNode * s_node ) ;
2012-12-20 08:04:28 +00:00
static void gst_mpdparser_free_segment_timeline_node ( GstSegmentTimelineNode *
seg_timeline ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_url_type_node ( GstURLType * url_type_node ) ;
2012-12-20 08:04:28 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_base_url_node ( GstBaseURL * base_url_node ) ;
2012-12-20 08:04:28 +00:00
static void gst_mpdparser_free_descriptor_type_node ( GstDescriptorType *
descriptor_type ) ;
static void gst_mpdparser_free_content_component_node ( GstContentComponentNode *
content_component_node ) ;
2015-07-15 10:56:13 +00:00
static void gst_mpdparser_free_utctiming_node ( GstUTCTimingNode * timing_type ) ;
2012-10-22 16:12:30 +00:00
static void gst_mpdparser_free_stream_period ( GstStreamPeriod * stream_period ) ;
2013-05-08 14:13:32 +00:00
static void gst_mpdparser_free_media_segment ( GstMediaSegment * media_segment ) ;
static void gst_mpdparser_free_active_stream ( GstActiveStream * active_stream ) ;
2015-09-08 10:36:23 +00:00
static GstUri * combine_urls ( GstUri * base , GList * list , gchar * * query ,
guint idx ) ;
2015-09-21 19:05:03 +00:00
static GList * gst_mpd_client_fetch_external_period ( GstMpdClient * client ,
GstPeriodNode * period_node , gboolean * error ) ;
static GList * gst_mpd_client_fetch_external_adaptation_set ( GstMpdClient *
client , GstPeriodNode * period , GstAdaptationSetNode * adapt_set ,
gboolean * error ) ;
2015-07-15 10:56:13 +00:00
struct GstMpdParserUtcTimingMethod
{
const gchar * name ;
GstMPDUTCTimingType method ;
} ;
static const struct GstMpdParserUtcTimingMethod
gst_mpdparser_utc_timing_methods [ ] = {
{ " urn:mpeg:dash:utc:ntp:2014 " , GST_MPD_UTCTIMING_TYPE_NTP } ,
{ " urn:mpeg:dash:utc:sntp:2014 " , GST_MPD_UTCTIMING_TYPE_SNTP } ,
{ " urn:mpeg:dash:utc:http-head:2014 " , GST_MPD_UTCTIMING_TYPE_HTTP_HEAD } ,
{ " urn:mpeg:dash:utc:http-xsdate:2014 " , GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE } ,
{ " urn:mpeg:dash:utc:http-iso:2014 " , GST_MPD_UTCTIMING_TYPE_HTTP_ISO } ,
{ " urn:mpeg:dash:utc:http-ntp:2014 " , GST_MPD_UTCTIMING_TYPE_HTTP_NTP } ,
{ " urn:mpeg:dash:utc:direct:2014 " , GST_MPD_UTCTIMING_TYPE_DIRECT } ,
/*
* Early working drafts used the : 2012 namespace and this namespace is
* used by some DASH packagers . To work - around these packagers , we also
* accept the early draft scheme names .
*/
{ " urn:mpeg:dash:utc:ntp:2012 " , GST_MPD_UTCTIMING_TYPE_NTP } ,
{ " urn:mpeg:dash:utc:sntp:2012 " , GST_MPD_UTCTIMING_TYPE_SNTP } ,
{ " urn:mpeg:dash:utc:http-head:2012 " , GST_MPD_UTCTIMING_TYPE_HTTP_HEAD } ,
{ " urn:mpeg:dash:utc:http-xsdate:2012 " , GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE } ,
{ " urn:mpeg:dash:utc:http-iso:2012 " , GST_MPD_UTCTIMING_TYPE_HTTP_ISO } ,
{ " urn:mpeg:dash:utc:http-ntp:2012 " , GST_MPD_UTCTIMING_TYPE_HTTP_NTP } ,
{ " urn:mpeg:dash:utc:direct:2012 " , GST_MPD_UTCTIMING_TYPE_DIRECT } ,
{ NULL , 0 }
} ;
2013-05-08 14:13:32 +00:00
/* functions to parse node namespaces, content and properties */
2013-06-21 21:51:46 +00:00
static gboolean
2015-09-29 15:17:03 +00:00
gst_mpdparser_get_xml_prop_validated_string ( xmlNode * a_node ,
const gchar * property_name , gchar * * property_value ,
gboolean ( * validate ) ( const char * ) )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
2015-09-29 15:17:03 +00:00
if ( validate & & ! ( * validate ) ( ( const char * ) prop_string ) ) {
GST_WARNING ( " Validation failure: %s " , prop_string ) ;
xmlFree ( prop_string ) ;
return FALSE ;
}
2013-06-21 21:51:46 +00:00
* property_value = ( gchar * ) prop_string ;
exists = TRUE ;
GST_LOG ( " - %s: %s " , property_name , prop_string ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2015-07-10 15:56:29 +00:00
static gboolean
gst_mpdparser_get_xml_ns_prop_string ( xmlNode * a_node ,
const gchar * ns_name , const gchar * property_name , gchar * * property_value )
{
xmlChar * prop_string ;
gboolean exists = FALSE ;
prop_string =
xmlGetNsProp ( a_node , ( const xmlChar * ) property_name ,
( const xmlChar * ) ns_name ) ;
if ( prop_string ) {
* property_value = ( gchar * ) prop_string ;
exists = TRUE ;
GST_LOG ( " - %s:%s: %s " , ns_name , property_name , prop_string ) ;
}
return exists ;
}
2015-09-29 15:17:03 +00:00
static gboolean
gst_mpdparser_get_xml_prop_string ( xmlNode * a_node ,
const gchar * property_name , gchar * * property_value )
{
return gst_mpdparser_get_xml_prop_validated_string ( a_node , property_name ,
property_value , NULL ) ;
}
static gboolean
gst_mpdparser_validate_no_whitespace ( const char * s )
{
return ! strpbrk ( s , " \r \n \t " ) ;
}
static gboolean
gst_mpdparser_get_xml_prop_string_no_whitespace ( xmlNode * a_node ,
const gchar * property_name , gchar * * property_value )
{
return gst_mpdparser_get_xml_prop_validated_string ( a_node , property_name ,
property_value , gst_mpdparser_validate_no_whitespace ) ;
}
2013-06-21 21:51:46 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_xml_prop_string_vector_type ( xmlNode * a_node ,
2013-06-21 21:51:46 +00:00
const gchar * property_name , gchar * * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
gchar * * prop_string_vector = NULL ;
guint i = 0 ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
prop_string_vector = g_strsplit ( ( gchar * ) prop_string , " " , - 1 ) ;
2013-06-21 21:51:46 +00:00
if ( prop_string_vector ) {
exists = TRUE ;
* property_value = prop_string_vector ;
GST_LOG ( " - %s: " , property_name ) ;
while ( prop_string_vector [ i ] ) {
GST_LOG ( " %s " , prop_string_vector [ i ] ) ;
i + + ;
}
} else {
2013-05-08 14:13:32 +00:00
GST_WARNING ( " Scan of string vector property failed! " ) ;
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2015-09-08 14:14:13 +00:00
static gboolean
gst_mpdparser_get_xml_prop_signed_integer ( xmlNode * a_node ,
const gchar * property_name , gint default_val , gint * property_value )
{
xmlChar * prop_string ;
gboolean exists = FALSE ;
* property_value = default_val ;
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
if ( prop_string ) {
if ( sscanf ( ( const gchar * ) prop_string , " %d " , property_value ) = = 1 ) {
exists = TRUE ;
GST_LOG ( " - %s: %d " , property_name , * property_value ) ;
} else {
GST_WARNING
( " failed to parse signed integer property %s from xml string %s " ,
property_name , prop_string ) ;
}
xmlFree ( prop_string ) ;
}
return exists ;
}
2013-06-21 21:51:46 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer ( xmlNode * a_node ,
2013-06-21 21:51:46 +00:00
const gchar * property_name , guint default_val , guint * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
* property_value = default_val ;
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
2015-10-28 16:24:01 +00:00
if ( sscanf ( ( gchar * ) prop_string , " %u " , property_value ) = = 1 & &
strstr ( ( gchar * ) prop_string , " - " ) = = NULL ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
GST_LOG ( " - %s: %u " , property_name , * property_value ) ;
2013-05-08 14:13:32 +00:00
} else {
2012-12-20 08:04:28 +00:00
GST_WARNING
( " failed to parse unsigned integer property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2015-10-28 16:24:01 +00:00
/* sscanf might have written to *property_value. Restore to default */
* property_value = default_val ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
2013-02-19 22:35:34 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer_64 ( xmlNode * a_node ,
2013-06-21 21:51:46 +00:00
const gchar * property_name , guint64 default_val , guint64 * property_value )
2013-02-19 22:35:34 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-02-19 22:35:34 +00:00
2013-06-21 21:51:46 +00:00
* property_value = default_val ;
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-02-19 22:35:34 +00:00
if ( prop_string ) {
2015-09-09 10:05:35 +00:00
if ( sscanf ( ( gchar * ) prop_string , " % " G_GUINT64_FORMAT ,
2015-10-28 16:24:01 +00:00
property_value ) = = 1 & &
strstr ( ( gchar * ) prop_string , " - " ) = = NULL ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
GST_LOG ( " - %s: % " G_GUINT64_FORMAT , property_name , * property_value ) ;
2013-02-19 22:35:34 +00:00
} else {
GST_WARNING
( " failed to parse unsigned integer property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2015-10-28 16:24:01 +00:00
/* sscanf might have written to *property_value. Restore to default */
* property_value = default_val ;
2013-02-19 22:35:34 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-02-19 22:35:34 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_xml_prop_uint_vector_type ( xmlNode * a_node ,
2013-06-21 21:51:46 +00:00
const gchar * property_name , guint * * property_value , guint * value_size )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
gchar * * str_vector ;
guint * prop_uint_vector = NULL , i ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
str_vector = g_strsplit ( ( gchar * ) prop_string , " " , - 1 ) ;
2013-06-21 21:51:46 +00:00
if ( str_vector ) {
* value_size = g_strv_length ( str_vector ) ;
prop_uint_vector = g_malloc ( * value_size * sizeof ( guint ) ) ;
if ( prop_uint_vector ) {
exists = TRUE ;
GST_LOG ( " - %s: " , property_name ) ;
for ( i = 0 ; i < * value_size ; i + + ) {
2015-10-28 16:24:01 +00:00
if ( sscanf ( ( gchar * ) str_vector [ i ] , " %u " , & prop_uint_vector [ i ] ) = = 1
& & strstr ( str_vector [ i ] , " - " ) = = NULL ) {
2013-06-21 21:51:46 +00:00
GST_LOG ( " %u " , prop_uint_vector [ i ] ) ;
} else {
GST_WARNING
( " failed to parse uint vector type property %s from xml string %s " ,
property_name , str_vector [ i ] ) ;
2015-10-28 16:24:01 +00:00
/* there is no special value to put in prop_uint_vector[i] to
* signal it is invalid , so we just clean everything and return
* FALSE
*/
g_free ( prop_uint_vector ) ;
prop_uint_vector = NULL ;
exists = FALSE ;
break ;
2013-06-21 21:51:46 +00:00
}
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
* property_value = prop_uint_vector ;
} else {
GST_WARNING ( " Array allocation failed! " ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
} else {
GST_WARNING ( " Scan of uint vector property failed! " ) ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
g_strfreev ( str_vector ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_double ( xmlNode * a_node ,
const gchar * property_name , gdouble * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
2015-09-09 10:05:35 +00:00
if ( sscanf ( ( gchar * ) prop_string , " %lf " , property_value ) = = 1 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
GST_LOG ( " - %s: %lf " , property_name , * property_value ) ;
2013-05-08 14:13:32 +00:00
} else {
GST_WARNING ( " failed to parse double property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
static gboolean
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_boolean ( xmlNode * a_node ,
const gchar * property_name , gboolean default_val ,
gboolean * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
* property_value = default_val ;
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
if ( xmlStrcmp ( prop_string , ( xmlChar * ) " false " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value = FALSE ;
GST_LOG ( " - %s: false " , property_name ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( prop_string , ( xmlChar * ) " true " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value = TRUE ;
GST_LOG ( " - %s: true " , property_name ) ;
2013-05-08 14:13:32 +00:00
} else {
GST_WARNING ( " failed to parse boolean property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_type ( xmlNode * a_node ,
const gchar * property_name , GstMPDFileType * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
* property_value = GST_MPD_FILE_TYPE_STATIC ; /* default */
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
if ( xmlStrcmp ( prop_string , ( xmlChar * ) " OnDemand " ) = = 0
| | xmlStrcmp ( prop_string , ( xmlChar * ) " static " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value = GST_MPD_FILE_TYPE_STATIC ;
GST_LOG ( " - %s: static " , property_name ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( prop_string , ( xmlChar * ) " Live " ) = = 0
| | xmlStrcmp ( prop_string , ( xmlChar * ) " dynamic " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value = GST_MPD_FILE_TYPE_DYNAMIC ;
GST_LOG ( " - %s: dynamic " , property_name ) ;
2013-05-08 14:13:32 +00:00
} else {
GST_WARNING ( " failed to parse MPD type property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_SAP_type ( xmlNode * a_node ,
const gchar * property_name , GstSAPType * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
guint prop_SAP_type = 0 ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
2015-09-09 10:05:35 +00:00
if ( sscanf ( ( gchar * ) prop_string , " %u " , & prop_SAP_type ) = = 1
2013-05-08 14:13:32 +00:00
& & prop_SAP_type < = 6 ) {
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value = ( GstSAPType ) prop_SAP_type ;
GST_LOG ( " - %s: %u " , property_name , prop_SAP_type ) ;
2013-05-08 14:13:32 +00:00
} else {
2012-12-20 08:04:28 +00:00
GST_WARNING
( " failed to parse unsigned integer property %s from xml string %s " ,
2013-06-21 21:51:46 +00:00
property_name , prop_string ) ;
2013-05-08 14:13:32 +00:00
}
xmlFree ( prop_string ) ;
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_range ( xmlNode * a_node , const gchar * property_name ,
GstRange * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
2015-10-14 07:31:23 +00:00
guint64 first_byte_pos = 0 , last_byte_pos = - 1 ;
2013-05-08 14:13:32 +00:00
guint len , pos ;
gchar * str ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
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 ) {
2015-10-28 16:24:01 +00:00
/* replace str[pos] with '\0' to allow sscanf to not be confused by
* the minus sign ( eg " -1 " ( observe the space before - ) would otherwise
* be interpreted as range - 1 to 1 )
*/
str [ pos ] = 0 ;
if ( sscanf ( str , " % " G_GUINT64_FORMAT , & first_byte_pos ) ! = 1 | |
strstr ( str , " - " ) ! = NULL ) {
/* sscanf failed or it found a negative number */
/* restore the '-' sign */
str [ pos ] = ' - ' ;
2013-05-08 14:13:32 +00:00
goto error ;
}
2015-10-28 16:24:01 +00:00
/* restore the '-' sign */
str [ pos ] = ' - ' ;
2013-05-08 14:13:32 +00:00
}
/* read last_byte_pos */
if ( pos < ( len - 1 ) ) {
2015-10-28 16:24:01 +00:00
if ( sscanf ( str + pos + 1 , " % " G_GUINT64_FORMAT , & last_byte_pos ) ! = 1 | |
strstr ( str + pos + 1 , " - " ) ! = NULL ) {
2013-05-08 14:13:32 +00:00
goto error ;
}
}
/* malloc return data structure */
2013-06-21 21:51:46 +00:00
* property_value = g_slice_new0 ( GstRange ) ;
exists = TRUE ;
( * property_value ) - > first_byte_pos = first_byte_pos ;
( * property_value ) - > last_byte_pos = last_byte_pos ;
2013-05-08 14:13:32 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: % " G_GUINT64_FORMAT " -% " G_GUINT64_FORMAT ,
property_name , first_byte_pos , last_byte_pos ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
error :
2013-06-21 21:51:46 +00:00
GST_WARNING ( " failed to parse property %s from xml string %s " , property_name ,
2013-05-08 14:13:32 +00:00
prop_string ) ;
2015-07-15 12:41:37 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_ratio ( xmlNode * a_node ,
const gchar * property_name , GstRatio * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
guint num = 0 , den = 1 ;
guint len , pos ;
gchar * str ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
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 ;
}
2015-10-28 16:24:01 +00:00
/* search for negative sign */
if ( strstr ( str , " - " ) ! = NULL ) {
goto error ;
}
2013-05-08 14:13:32 +00:00
/* 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 */
2013-06-21 21:51:46 +00:00
* property_value = g_slice_new0 ( GstRatio ) ;
exists = TRUE ;
( * property_value ) - > num = num ;
( * property_value ) - > den = den ;
2013-05-08 14:13:32 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: %u:%u " , property_name , num , den ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
error :
2013-06-21 21:51:46 +00:00
GST_WARNING ( " failed to parse property %s from xml string %s " , property_name ,
2013-05-08 14:13:32 +00:00
prop_string ) ;
2015-07-15 12:41:37 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_framerate ( xmlNode * a_node ,
const gchar * property_name , GstFrameRate * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
guint num = 0 , den = 1 ;
guint len , pos ;
gchar * str ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
len = xmlStrlen ( prop_string ) ;
str = ( gchar * ) prop_string ;
GST_TRACE ( " framerate: %s, len %d " , str , len ) ;
2015-10-28 16:24:01 +00:00
/* search for negative sign */
if ( strstr ( str , " - " ) ! = NULL ) {
goto error ;
}
2013-05-08 14:13:32 +00:00
/* 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 */
2013-06-21 21:51:46 +00:00
* property_value = g_slice_new0 ( GstFrameRate ) ;
exists = TRUE ;
( * property_value ) - > num = num ;
( * property_value ) - > den = den ;
xmlFree ( prop_string ) ;
2013-05-08 14:13:32 +00:00
if ( den = = 1 )
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: %u " , property_name , num ) ;
2013-05-08 14:13:32 +00:00
else
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: %u/%u " , property_name , num , den ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
error :
2013-06-21 21:51:46 +00:00
GST_WARNING ( " failed to parse property %s from xml string %s " , property_name ,
2013-05-08 14:13:32 +00:00
prop_string ) ;
2015-07-15 12:41:37 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_cond_uint ( xmlNode * a_node ,
const gchar * property_name , GstConditionalUintType * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
gchar * str ;
gboolean flag ;
guint val ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
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 ;
2015-10-28 16:24:01 +00:00
if ( sscanf ( str , " %u " , & val ) ! = 1 | | strstr ( str , " - " ) ! = NULL )
2013-05-08 14:13:32 +00:00
goto error ;
}
/* alloc return data structure */
2013-06-21 21:51:46 +00:00
* property_value = g_slice_new0 ( GstConditionalUintType ) ;
exists = TRUE ;
( * property_value ) - > flag = flag ;
( * property_value ) - > value = val ;
2013-05-08 14:13:32 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: flag=%s val=%u " , property_name , flag ? " true " : " false " ,
val ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
error :
2013-06-21 21:51:46 +00:00
GST_WARNING ( " failed to parse property %s from xml string %s " , property_name ,
2013-05-08 14:13:32 +00:00
prop_string ) ;
2015-07-15 12:41:37 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
/*
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 !
*/
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_prop_dateTime ( xmlNode * a_node ,
const gchar * property_name , GstDateTime * * property_value )
2013-05-08 14:13:32 +00:00
{
xmlChar * prop_string ;
gchar * str ;
2013-07-29 07:35:08 +00:00
gint ret , pos ;
2013-05-08 14:13:32 +00:00
gint year , month , day , hour , minute , second ;
2013-06-21 21:51:46 +00:00
gboolean exists = FALSE ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
2013-05-08 14:13:32 +00:00
if ( prop_string ) {
str = ( gchar * ) prop_string ;
2013-07-29 07:35:08 +00:00
GST_TRACE ( " dateTime: %s, len %d " , str , xmlStrlen ( prop_string ) ) ;
2013-05-08 14:13:32 +00:00
/* parse year */
ret = sscanf ( str , " %d " , & year ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | year < = 0 )
2013-05-08 14:13:32 +00:00
goto error ;
pos = strcspn ( str , " - " ) ;
str + = ( pos + 1 ) ;
GST_TRACE ( " - year %d " , year ) ;
/* parse month */
ret = sscanf ( str , " %d " , & month ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | month < = 0 )
2013-05-08 14:13:32 +00:00
goto error ;
pos = strcspn ( str , " - " ) ;
str + = ( pos + 1 ) ;
GST_TRACE ( " - month %d " , month ) ;
/* parse day */
ret = sscanf ( str , " %d " , & day ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | day < = 0 )
2013-05-08 14:13:32 +00:00
goto error ;
pos = strcspn ( str , " T " ) ;
str + = ( pos + 1 ) ;
GST_TRACE ( " - day %d " , day ) ;
/* parse hour */
ret = sscanf ( str , " %d " , & hour ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | hour < 0 )
2013-05-08 14:13:32 +00:00
goto error ;
pos = strcspn ( str , " : " ) ;
str + = ( pos + 1 ) ;
GST_TRACE ( " - hour %d " , hour ) ;
/* parse minute */
ret = sscanf ( str , " %d " , & minute ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | minute < 0 )
2013-05-08 14:13:32 +00:00
goto error ;
pos = strcspn ( str , " : " ) ;
str + = ( pos + 1 ) ;
GST_TRACE ( " - minute %d " , minute ) ;
/* parse second */
ret = sscanf ( str , " %d " , & second ) ;
2015-10-28 16:24:01 +00:00
if ( ret ! = 1 | | second < 0 )
2013-05-08 14:13:32 +00:00
goto error ;
GST_TRACE ( " - second %d " , second ) ;
2013-06-21 21:51:46 +00:00
GST_LOG ( " - %s: %4d/%02d/%02d %02d:%02d:%02d " , property_name ,
2013-05-08 14:13:32 +00:00
year , month , day , hour , minute , second ) ;
2013-06-21 21:51:46 +00:00
exists = TRUE ;
* property_value =
gst_date_time_new ( 0 , year , month , day , hour , minute , second ) ;
xmlFree ( prop_string ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
error :
2013-06-21 21:51:46 +00:00
GST_WARNING ( " failed to parse property %s from xml string %s " , property_name ,
2013-05-08 14:13:32 +00:00
prop_string ) ;
2015-07-15 12:41:37 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
/*
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) */
2015-10-29 11:38:35 +00:00
static guint
convert_to_millisecs ( guint decimals , gint pos )
2013-05-08 14:13:32 +00:00
{
2015-10-29 11:38:35 +00:00
guint num = 1 , den = 1 ;
gint i = 3 - pos ;
2013-05-08 14:13:32 +00:00
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 ;
}
2013-06-21 21:51:46 +00:00
static gboolean
2015-11-02 10:25:38 +00:00
accumulate ( guint64 * v , guint64 mul , guint64 add )
{
guint64 tmp ;
if ( * v > G_MAXUINT64 / mul )
return FALSE ;
tmp = * v * mul ;
if ( tmp > G_MAXUINT64 - add )
return FALSE ;
* v = tmp + add ;
return TRUE ;
}
static gboolean
gst_mpdparser_parse_duration ( const char * str , guint64 * value )
2013-05-08 14:13:32 +00:00
{
2015-10-29 11:38:35 +00:00
gint ret , len , pos , posT ;
2015-09-29 08:32:02 +00:00
gint years = - 1 , months = - 1 , days = - 1 , hours = - 1 , minutes = - 1 , seconds =
- 1 , decimals = - 1 , read ;
2013-05-08 14:13:32 +00:00
gboolean have_ms = FALSE ;
2015-11-02 10:25:38 +00:00
guint64 tmp_value ;
2013-05-08 14:13:32 +00:00
2015-11-02 10:25:38 +00:00
len = strlen ( str ) ;
GST_TRACE ( " duration: %s, len %d " , str , len ) ;
if ( strspn ( str , " PT0123456789., \t HMDSY " ) < len ) {
GST_WARNING ( " Invalid character found: '%s' " , str ) ;
goto error ;
}
/* skip leading/trailing whitespace */
while ( strchr ( " \t " , str [ 0 ] ) ) {
2013-05-08 14:13:32 +00:00
str + + ;
len - - ;
2015-11-02 10:25:38 +00:00
}
while ( len > 0 & & strchr ( " \t " , str [ len - 1 ] ) )
- - len ;
2015-09-29 08:32:02 +00:00
2015-11-02 10:25:38 +00:00
/* read "P" for period */
if ( str [ 0 ] ! = ' P ' ) {
GST_WARNING ( " P not found at the beginning of the string! " ) ;
goto error ;
}
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 , " %u " , & read ) ;
if ( ret ! = 1 ) {
GST_WARNING ( " can not read integer value from string %s! " , str ) ;
goto error ;
}
switch ( str [ pos ] ) {
case ' Y ' :
if ( years ! = - 1 | | months ! = - 1 | | days ! = - 1 ) {
GST_WARNING ( " year, month or day was already set " ) ;
goto error ;
}
years = read ;
break ;
case ' M ' :
if ( months ! = - 1 | | days ! = - 1 ) {
GST_WARNING ( " month or day was already set " ) ;
goto error ;
}
months = read ;
if ( months > = 12 ) {
GST_WARNING ( " Month out of range " ) ;
goto error ;
}
break ;
case ' D ' :
if ( days ! = - 1 ) {
GST_WARNING ( " day was already set " ) ;
goto error ;
}
days = read ;
if ( days > = 31 ) {
GST_WARNING ( " Day out of range " ) ;
goto error ;
}
break ;
default :
GST_WARNING ( " unexpected char %c! " , str [ pos ] ) ;
2013-06-21 21:51:46 +00:00
goto error ;
2015-11-02 10:25:38 +00:00
break ;
}
GST_TRACE ( " read number %u type %c " , read , str [ pos ] ) ;
str + = ( pos + 1 ) ;
posT - = ( pos + 1 ) ;
} while ( posT > 0 ) ;
}
if ( years = = - 1 )
years = 0 ;
if ( months = = - 1 )
months = 0 ;
if ( days = = - 1 )
days = 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, hundredths of second */
do {
GST_TRACE ( " parsing substring %s " , str ) ;
pos = strcspn ( str , " HMS,. " ) ;
ret = sscanf ( str , " %u " , & read ) ;
if ( ret ! = 1 ) {
GST_WARNING ( " can not read integer value from string %s! " , str ) ;
goto error ;
}
switch ( str [ pos ] ) {
case ' H ' :
if ( hours ! = - 1 | | minutes ! = - 1 | | seconds ! = - 1 ) {
GST_WARNING ( " hour, minute or second was already set " ) ;
goto error ;
}
hours = read ;
if ( hours > = 24 ) {
GST_WARNING ( " Hour out of range " ) ;
goto error ;
}
break ;
case ' M ' :
if ( minutes ! = - 1 | | seconds ! = - 1 ) {
GST_WARNING ( " minute or second was already set " ) ;
goto error ;
}
minutes = read ;
if ( minutes > = 60 ) {
GST_WARNING ( " Minute out of range " ) ;
goto error ;
}
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 %u (%d digits) -> %d ms " , read , pos ,
decimals ) ;
} else {
2015-09-29 08:32:02 +00:00
if ( seconds ! = - 1 ) {
GST_WARNING ( " second was already set " ) ;
goto error ;
}
2015-11-02 10:25:38 +00:00
/* no decimals */
2013-05-08 14:13:32 +00:00
seconds = read ;
2015-11-02 10:25:38 +00:00
}
break ;
case ' . ' :
case ' , ' :
/* we have read the integer part of a decimal number in seconds */
if ( seconds ! = - 1 ) {
GST_WARNING ( " second was already set " ) ;
2013-06-21 21:51:46 +00:00
goto error ;
2015-11-02 10:25:38 +00:00
}
seconds = read ;
have_ms = TRUE ;
break ;
default :
GST_WARNING ( " unexpected char %c! " , str [ pos ] ) ;
goto error ;
break ;
}
GST_TRACE ( " read number %u type %c " , read , str [ pos ] ) ;
str + = pos + 1 ;
len - = ( pos + 1 ) ;
} while ( len > 0 ) ;
}
if ( hours = = - 1 )
hours = 0 ;
if ( minutes = = - 1 )
minutes = 0 ;
if ( seconds = = - 1 )
seconds = 0 ;
if ( decimals = = - 1 )
decimals = 0 ;
GST_TRACE ( " H:M:S.MS=%d:%d:%d.%03d " , hours , minutes , seconds , decimals ) ;
tmp_value = 0 ;
if ( ! accumulate ( & tmp_value , 1 , years )
| | ! accumulate ( & tmp_value , 365 , months * 30 )
| | ! accumulate ( & tmp_value , 1 , days )
| | ! accumulate ( & tmp_value , 24 , hours )
| | ! accumulate ( & tmp_value , 60 , minutes )
| | ! accumulate ( & tmp_value , 60 , seconds )
| | ! accumulate ( & tmp_value , 1000 , decimals ) )
goto error ;
/* ensure it can be converted from milliseconds to nanoseconds */
if ( tmp_value > G_MAXUINT64 / 1000000 )
goto error ;
* value = tmp_value ;
return TRUE ;
2013-05-08 14:13:32 +00:00
2015-11-02 10:25:38 +00:00
error :
return FALSE ;
}
2015-09-29 08:32:02 +00:00
2015-11-02 10:25:38 +00:00
static gboolean
gst_mpdparser_get_xml_prop_duration ( xmlNode * a_node ,
const gchar * property_name , guint64 default_value ,
guint64 * property_value )
{
xmlChar * prop_string ;
gchar * str ;
gboolean exists = FALSE ;
* property_value = default_value ;
prop_string = xmlGetProp ( a_node , ( const xmlChar * ) property_name ) ;
if ( prop_string ) {
str = ( gchar * ) prop_string ;
if ( ! gst_mpdparser_parse_duration ( str , property_value ) )
goto error ;
GST_LOG ( " - %s: % " G_GUINT64_FORMAT , property_name , * property_value ) ;
2013-05-08 14:13:32 +00:00
xmlFree ( prop_string ) ;
2013-06-21 21:51:46 +00:00
exists = TRUE ;
}
return exists ;
2013-05-08 14:13:32 +00:00
2013-06-21 21:51:46 +00:00
error :
xmlFree ( prop_string ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
static gboolean
gst_mpdparser_get_xml_node_content ( xmlNode * a_node , gchar * * content )
2013-05-08 14:13:32 +00:00
{
2013-06-21 21:51:46 +00:00
xmlChar * node_content = NULL ;
2014-04-03 09:48:07 +00:00
gboolean exists = FALSE ;
2013-06-21 21:51:46 +00:00
node_content = xmlNodeGetContent ( a_node ) ;
if ( node_content ) {
exists = TRUE ;
* content = ( gchar * ) node_content ;
GST_LOG ( " - %s: %s " , a_node - > name , * content ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 21:51:46 +00:00
return exists ;
2013-05-08 14:13:32 +00:00
}
2015-02-06 13:22:14 +00:00
static gboolean
gst_mpdparser_get_xml_node_as_string ( xmlNode * a_node , gchar * * content )
{
gboolean exists = FALSE ;
2015-08-19 10:29:43 +00:00
const char * txt_encoding ;
xmlOutputBufferPtr out_buf ;
txt_encoding = ( const char * ) a_node - > doc - > encoding ;
2015-08-19 18:33:09 +00:00
out_buf = xmlAllocOutputBuffer ( NULL ) ;
2015-08-19 10:29:43 +00:00
g_assert ( out_buf ! = NULL ) ;
xmlNodeDumpOutput ( out_buf , a_node - > doc , a_node , 0 , 0 , txt_encoding ) ;
xmlOutputBufferFlush ( out_buf ) ;
# ifdef LIBXML2_NEW_BUFFER
if ( xmlOutputBufferGetSize ( out_buf ) > 0 ) {
* content =
( gchar * ) xmlStrndup ( xmlOutputBufferGetContent ( out_buf ) ,
xmlOutputBufferGetSize ( out_buf ) ) ;
exists = TRUE ;
}
# else
if ( out_buf - > conv & & out_buf - > conv - > use > 0 ) {
* content =
( gchar * ) xmlStrndup ( out_buf - > conv - > content , out_buf - > conv - > use ) ;
exists = TRUE ;
} else if ( out_buf - > buffer & & out_buf - > buffer - > use > 0 ) {
* content =
( gchar * ) xmlStrndup ( out_buf - > buffer - > content , out_buf - > buffer - > use ) ;
2015-02-06 13:22:14 +00:00
exists = TRUE ;
2015-08-19 10:29:43 +00:00
}
# endif // LIBXML2_NEW_BUFFER
( void ) xmlOutputBufferClose ( out_buf ) ;
if ( exists ) {
2015-02-06 13:22:14 +00:00
GST_LOG ( " - %s: %s " , a_node - > name , * content ) ;
}
return exists ;
}
2013-05-08 14:13:32 +00:00
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 */
2015-06-12 15:44:55 +00:00
if ( a_node - > ns ) {
namespace = xmlMemStrdup ( ( const gchar * ) a_node - > ns - > href ) ;
if ( namespace ) {
GST_LOG ( " - default namespace: %s " , namespace ) ;
}
2013-05-08 14:13:32 +00:00
}
} 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 ) {
2013-06-25 16:26:24 +00:00
namespace = xmlMemStrdup ( ( const gchar * ) curr_ns - > href ) ;
2013-05-08 14:13:32 +00:00
if ( namespace ) {
2012-12-20 08:04:28 +00:00
GST_LOG ( " - %s namespace: %s " , curr_ns - > prefix , curr_ns - > href ) ;
2013-05-08 14:13:32 +00:00
}
}
}
}
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 ) ;
* list = g_list_append ( * list , new_base_url ) ;
GST_LOG ( " content of BaseURL node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_node_content ( a_node , & new_base_url - > baseURL ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of BaseURL node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " serviceLocation " ,
& new_base_url - > serviceLocation ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " byteRange " ,
& new_base_url - > byteRange ) ;
2013-05-08 14:13:32 +00:00
}
static void
gst_mpdparser_parse_descriptor_type_node ( GList * * list , xmlNode * a_node )
{
GstDescriptorType * new_descriptor ;
new_descriptor = g_slice_new0 ( GstDescriptorType ) ;
* list = g_list_append ( * list , new_descriptor ) ;
GST_LOG ( " attributes of %s node: " , a_node - > name ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " schemeIdUri " ,
& new_descriptor - > schemeIdUri ) ;
2015-02-06 13:22:14 +00:00
if ( ! gst_mpdparser_get_xml_prop_string ( a_node , " value " ,
& new_descriptor - > value ) ) {
/* if no value attribute, use XML string representation of the node */
gst_mpdparser_get_xml_node_as_string ( a_node , & new_descriptor - > value ) ;
}
2013-05-08 14:13:32 +00:00
}
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 ) ;
* list = g_list_append ( * list , new_content_component ) ;
GST_LOG ( " attributes of ContentComponent node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " id " , 0 ,
& new_content_component - > id ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " lang " ,
& new_content_component - > lang ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " contentType " ,
& new_content_component - > contentType ) ;
gst_mpdparser_get_xml_prop_ratio ( a_node , " par " , & new_content_component - > par ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & new_content_component - > Accessibility , cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Role " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_descriptor_type_node ( & new_content_component - > Role ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Rating " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & new_content_component - > Rating , cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Viewpoint " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & new_content_component - > Viewpoint , cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
}
static void
gst_mpdparser_parse_location_node ( GList * * list , xmlNode * a_node )
{
2014-04-03 09:48:07 +00:00
gchar * location = NULL ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " content of Location node: " ) ;
2014-04-03 09:48:07 +00:00
if ( gst_mpdparser_get_xml_node_content ( a_node , & location ) )
* list = g_list_append ( * list , location ) ;
2013-05-08 14:13:32 +00:00
}
static void
gst_mpdparser_parse_subrepresentation_node ( GList * * list , xmlNode * a_node )
{
GstSubRepresentationNode * new_subrep ;
new_subrep = g_slice_new0 ( GstSubRepresentationNode ) ;
* list = g_list_append ( * list , new_subrep ) ;
GST_LOG ( " attributes of SubRepresentation node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " level " , 0 ,
& new_subrep - > level ) ;
gst_mpdparser_get_xml_prop_uint_vector_type ( a_node , " dependencyLevel " ,
& new_subrep - > dependencyLevel , & new_subrep - > size ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " bandwidth " , 0 ,
& new_subrep - > bandwidth ) ;
gst_mpdparser_get_xml_prop_string_vector_type ( a_node ,
" contentComponent " , & new_subrep - > contentComponent ) ;
2013-05-08 14:13:32 +00:00
/* RepresentationBase extension */
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_representation_base_type ( & new_subrep - > RepresentationBase ,
a_node ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 23:09:30 +00:00
static GstSegmentURLNode *
gst_mpdparser_clone_segment_url ( GstSegmentURLNode * seg_url )
{
GstSegmentURLNode * clone = NULL ;
if ( seg_url ) {
clone = g_slice_new0 ( GstSegmentURLNode ) ;
2015-07-10 10:26:51 +00:00
clone - > media = xmlMemStrdup ( seg_url - > media ) ;
clone - > mediaRange = gst_mpdparser_clone_range ( seg_url - > mediaRange ) ;
clone - > index = xmlMemStrdup ( seg_url - > index ) ;
clone - > indexRange = gst_mpdparser_clone_range ( seg_url - > indexRange ) ;
2013-06-21 23:09:30 +00:00
}
return clone ;
}
2013-05-08 14:13:32 +00:00
static void
gst_mpdparser_parse_segment_url_node ( GList * * list , xmlNode * a_node )
{
GstSegmentURLNode * new_segment_url ;
new_segment_url = g_slice_new0 ( GstSegmentURLNode ) ;
* list = g_list_append ( * list , new_segment_url ) ;
GST_LOG ( " attributes of SegmentURL node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " media " , & new_segment_url - > media ) ;
gst_mpdparser_get_xml_prop_range ( a_node , " mediaRange " ,
& new_segment_url - > mediaRange ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " index " , & new_segment_url - > index ) ;
gst_mpdparser_get_xml_prop_range ( a_node , " indexRange " ,
& new_segment_url - > indexRange ) ;
2013-05-08 14:13:32 +00:00
}
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 ) ;
GST_LOG ( " attributes of URLType node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " sourceURL " ,
& new_url_type - > sourceURL ) ;
gst_mpdparser_get_xml_prop_range ( a_node , " range " , & new_url_type - > range ) ;
2013-05-08 14:13:32 +00:00
}
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_seg_base_type_ext ( GstSegmentBaseType * * pointer ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstSegmentBaseType * parent )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstSegmentBaseType * seg_base_type ;
2013-06-21 23:09:30 +00:00
guint intval ;
2015-06-11 16:25:49 +00:00
guint64 int64val ;
2013-06-21 23:09:30 +00:00
gboolean boolval ;
GstRange * rangeval ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_seg_base_type_ext ( * pointer ) ;
* pointer = seg_base_type = g_slice_new0 ( GstSegmentBaseType ) ;
2013-06-21 23:09:30 +00:00
/* Initialize values that have defaults */
seg_base_type - > indexRangeExact = FALSE ;
2015-07-15 12:02:54 +00:00
seg_base_type - > timescale = 1 ;
2013-06-21 23:09:30 +00:00
/* Inherit attribute values from parent */
if ( parent ) {
seg_base_type - > timescale = parent - > timescale ;
seg_base_type - > presentationTimeOffset = parent - > presentationTimeOffset ;
seg_base_type - > indexRange = gst_mpdparser_clone_range ( parent - > indexRange ) ;
seg_base_type - > indexRangeExact = parent - > indexRangeExact ;
seg_base_type - > Initialization =
gst_mpdparser_clone_URL ( parent - > Initialization ) ;
seg_base_type - > RepresentationIndex =
gst_mpdparser_clone_URL ( 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 */
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of SegmentBaseType extension: " ) ;
2015-07-15 12:02:54 +00:00
if ( gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " timescale " , 1 ,
2013-06-21 23:09:30 +00:00
& intval ) ) {
seg_base_type - > timescale = intval ;
}
2015-06-11 16:25:49 +00:00
if ( gst_mpdparser_get_xml_prop_unsigned_integer_64 ( a_node ,
" presentationTimeOffset " , 0 , & int64val ) ) {
seg_base_type - > presentationTimeOffset = int64val ;
2013-06-21 23:09:30 +00:00
}
if ( gst_mpdparser_get_xml_prop_range ( a_node , " indexRange " , & rangeval ) ) {
if ( seg_base_type - > indexRange ) {
g_slice_free ( GstRange , seg_base_type - > indexRange ) ;
}
seg_base_type - > indexRange = rangeval ;
}
if ( gst_mpdparser_get_xml_prop_boolean ( a_node , " indexRangeExact " ,
FALSE , & boolval ) ) {
seg_base_type - > indexRangeExact = boolval ;
}
2013-05-08 14:13:32 +00:00
/* 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 ) {
2014-05-13 13:13:37 +00:00
/* parse will free the previous pointer to create a new one */
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_url_type_node ( & seg_base_type - > Initialization ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " RepresentationIndex " ) = = 0 ) {
2014-05-13 13:13:37 +00:00
/* parse will free the previous pointer to create a new one */
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_url_type_node ( & seg_base_type - > RepresentationIndex ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
}
2013-06-21 23:09:30 +00:00
static GstSNode *
gst_mpdparser_clone_s_node ( GstSNode * pointer )
{
GstSNode * clone = NULL ;
if ( pointer ) {
clone = g_slice_new0 ( GstSNode ) ;
2015-07-10 10:26:51 +00:00
clone - > t = pointer - > t ;
clone - > d = pointer - > d ;
clone - > r = pointer - > r ;
2013-06-21 23:09:30 +00:00
}
return clone ;
}
2013-05-08 14:13:32 +00:00
static void
2013-07-05 02:42:23 +00:00
gst_mpdparser_parse_s_node ( GQueue * queue , xmlNode * a_node )
2013-05-08 14:13:32 +00:00
{
GstSNode * new_s_node ;
new_s_node = g_slice_new0 ( GstSNode ) ;
2013-07-05 02:42:23 +00:00
g_queue_push_tail ( queue , new_s_node ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of S node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer_64 ( a_node , " t " , 0 ,
& new_s_node - > t ) ;
gst_mpdparser_get_xml_prop_unsigned_integer_64 ( a_node , " d " , 0 ,
& new_s_node - > d ) ;
2015-09-08 14:14:13 +00:00
gst_mpdparser_get_xml_prop_signed_integer ( a_node , " r " , 0 , & new_s_node - > r ) ;
2013-05-08 14:13:32 +00:00
}
2013-06-21 23:09:30 +00:00
static GstSegmentTimelineNode *
gst_mpdparser_clone_segment_timeline ( GstSegmentTimelineNode * pointer )
{
GstSegmentTimelineNode * clone = NULL ;
if ( pointer ) {
2013-07-05 02:42:23 +00:00
clone = gst_mpdparser_segment_timeline_node_new ( ) ;
2013-06-21 23:09:30 +00:00
if ( clone ) {
GList * list ;
2013-12-02 20:41:01 +00:00
for ( list = g_queue_peek_head_link ( & pointer - > S ) ; list ;
list = g_list_next ( list ) ) {
2013-06-21 23:09:30 +00:00
GstSNode * s_node ;
s_node = ( GstSNode * ) list - > data ;
if ( s_node ) {
2013-12-02 20:41:01 +00:00
g_queue_push_tail ( & clone - > S , gst_mpdparser_clone_s_node ( s_node ) ) ;
2013-06-21 23:09:30 +00:00
}
}
} else {
GST_WARNING ( " Allocation of SegmentTimeline node failed! " ) ;
}
}
return clone ;
}
2013-05-08 14:13:32 +00:00
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_segment_timeline_node ( GstSegmentTimelineNode * * pointer ,
xmlNode * a_node )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstSegmentTimelineNode * new_seg_timeline ;
gst_mpdparser_free_segment_timeline_node ( * pointer ) ;
2013-07-05 02:42:23 +00:00
* pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ( ) ;
2013-05-08 14:13:32 +00:00
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 ) ;
}
}
}
}
2015-10-28 15:34:29 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_mult_seg_base_type_ext ( GstMultSegmentBaseType * * pointer ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstMultSegmentBaseType * parent )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstMultSegmentBaseType * mult_seg_base_type ;
2013-06-21 23:09:30 +00:00
guint intval ;
2015-10-28 15:34:29 +00:00
gboolean has_timeline = FALSE , has_duration = FALSE ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_mult_seg_base_type_ext ( * pointer ) ;
2015-10-28 15:34:29 +00:00
mult_seg_base_type = g_slice_new0 ( GstMultSegmentBaseType ) ;
2013-05-08 14:13:32 +00:00
2015-03-17 13:49:40 +00:00
mult_seg_base_type - > duration = 0 ;
mult_seg_base_type - > startNumber = 1 ;
2013-06-21 23:09:30 +00:00
/* Inherit attribute values from parent */
if ( parent ) {
mult_seg_base_type - > duration = parent - > duration ;
mult_seg_base_type - > startNumber = parent - > startNumber ;
mult_seg_base_type - > SegmentTimeline =
gst_mpdparser_clone_segment_timeline ( parent - > SegmentTimeline ) ;
mult_seg_base_type - > BitstreamSwitching =
gst_mpdparser_clone_URL ( parent - > BitstreamSwitching ) ;
}
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of MultipleSegmentBaseType extension: " ) ;
2013-06-21 23:09:30 +00:00
if ( gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " duration " , 0 ,
& intval ) ) {
mult_seg_base_type - > duration = intval ;
2015-10-28 15:34:29 +00:00
has_duration = TRUE ;
2013-06-21 23:09:30 +00:00
}
2015-03-06 11:24:44 +00:00
2015-03-17 13:49:40 +00:00
if ( gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " startNumber " , 1 ,
& intval ) ) {
mult_seg_base_type - > startNumber = intval ;
}
2013-05-08 14:13:32 +00:00
GST_LOG ( " extension of MultipleSegmentBaseType extension: " ) ;
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_seg_base_type_ext ( & mult_seg_base_type - > SegBaseType ,
2013-06-21 23:09:30 +00:00
a_node , ( parent ? parent - > SegBaseType : NULL ) ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2014-05-13 13:24:51 +00:00
/* parse frees the segmenttimeline if any */
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_segment_timeline_node
( & mult_seg_base_type - > SegmentTimeline , cur_node ) ;
2015-10-28 15:34:29 +00:00
has_timeline = TRUE ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " BitstreamSwitching " ) = = 0 ) {
2014-05-13 13:13:37 +00:00
/* parse frees the old url before setting the new one */
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_url_type_node
( & mult_seg_base_type - > BitstreamSwitching , cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
if ( ! has_duration & & ! has_timeline ) {
GST_ERROR ( " segment has neither duration nor timeline " ) ;
goto error ;
}
* pointer = mult_seg_base_type ;
return TRUE ;
error :
gst_mpdparser_free_mult_seg_base_type_ext ( mult_seg_base_type ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2015-10-28 15:34:29 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_segment_list_node ( GstSegmentListNode * * pointer ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstSegmentListNode * parent )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstSegmentListNode * new_segment_list ;
2015-07-10 15:56:29 +00:00
gchar * actuate ;
2015-10-30 14:31:21 +00:00
gboolean segment_urls_inherited_from_parent = FALSE ;
2013-05-08 14:13:32 +00:00
2012-10-08 09:16:09 +00:00
gst_mpdparser_free_segment_list_node ( * pointer ) ;
2015-10-28 15:34:29 +00:00
new_segment_list = g_slice_new0 ( GstSegmentListNode ) ;
2013-05-08 14:13:32 +00:00
2013-06-21 23:09:30 +00:00
/* Inherit attribute values from parent */
if ( parent ) {
GList * list ;
GstSegmentURLNode * seg_url ;
for ( list = g_list_first ( parent - > SegmentURL ) ; list ;
list = g_list_next ( list ) ) {
seg_url = ( GstSegmentURLNode * ) list - > data ;
new_segment_list - > SegmentURL =
g_list_append ( new_segment_list - > SegmentURL ,
gst_mpdparser_clone_segment_url ( seg_url ) ) ;
2015-10-30 14:31:21 +00:00
segment_urls_inherited_from_parent = TRUE ;
2013-06-21 23:09:30 +00:00
}
}
2015-07-10 15:56:29 +00:00
new_segment_list - > actuate = GST_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_segment_list - > xlink_href )
& & gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , " onLoad " ) = = 0 )
new_segment_list - > actuate = GST_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
2013-05-08 14:13:32 +00:00
GST_LOG ( " extension of SegmentList node: " ) ;
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_mult_seg_base_type_ext
2013-06-21 23:09:30 +00:00
( & new_segment_list - > MultSegBaseType , a_node ,
2015-10-28 15:34:29 +00:00
( parent ? parent - > MultSegBaseType : NULL ) ) )
goto error ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2015-10-30 14:31:21 +00:00
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_mpdparser_free_segment_url_node ) ;
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 ;
}
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_segment_url_node ( & new_segment_list - > SegmentURL ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
* pointer = new_segment_list ;
return TRUE ;
error :
gst_mpdparser_free_segment_list_node ( new_segment_list ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_representation_base_type ( GstRepresentationBaseType * *
pointer , xmlNode * a_node )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstRepresentationBaseType * representation_base ;
gst_mpdparser_free_representation_base_type ( * pointer ) ;
* pointer = representation_base = g_slice_new0 ( GstRepresentationBaseType ) ;
GST_LOG ( " attributes of RepresentationBaseType extension: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " profiles " ,
& representation_base - > profiles ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " width " , 0 ,
& representation_base - > width ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " height " , 0 ,
& representation_base - > height ) ;
gst_mpdparser_get_xml_prop_ratio ( a_node , " sar " , & representation_base - > sar ) ;
gst_mpdparser_get_xml_prop_framerate ( a_node , " frameRate " ,
& representation_base - > frameRate ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " audioSamplingRate " ,
& representation_base - > audioSamplingRate ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " mimeType " ,
& representation_base - > mimeType ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " segmentProfiles " ,
& representation_base - > segmentProfiles ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " codecs " ,
& representation_base - > codecs ) ;
gst_mpdparser_get_xml_prop_double ( a_node , " maximumSAPPeriod " ,
& representation_base - > maximumSAPPeriod ) ;
gst_mpdparser_get_xml_prop_SAP_type ( a_node , " startWithSAP " ,
& representation_base - > startWithSAP ) ;
gst_mpdparser_get_xml_prop_double ( a_node , " maxPlayoutRate " ,
& representation_base - > maxPlayoutRate ) ;
gst_mpdparser_get_xml_prop_boolean ( a_node , " codingDependency " ,
FALSE , & representation_base - > codingDependency ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " scanType " ,
& representation_base - > scanType ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & representation_base - > FramePacking , cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " AudioChannelConfiguration " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & representation_base - > AudioChannelConfiguration , cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " ContentProtection " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_descriptor_type_node
( & representation_base - > ContentProtection , cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
}
2015-10-28 15:34:29 +00:00
static gboolean
2013-06-21 23:09:30 +00:00
gst_mpdparser_parse_representation_node ( GList * * list , xmlNode * a_node ,
GstAdaptationSetNode * parent )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstRepresentationNode * new_representation ;
new_representation = g_slice_new0 ( GstRepresentationNode ) ;
GST_LOG ( " attributes of Representation node: " ) ;
2015-09-29 15:17:03 +00:00
gst_mpdparser_get_xml_prop_string_no_whitespace ( a_node , " id " ,
& new_representation - > id ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " bandwidth " , 0 ,
& new_representation - > bandwidth ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " qualityRanking " , 0 ,
& new_representation - > qualityRanking ) ;
gst_mpdparser_get_xml_prop_string_vector_type ( a_node , " dependencyId " ,
& new_representation - > dependencyId ) ;
gst_mpdparser_get_xml_prop_string_vector_type ( a_node ,
" mediaStreamStructureId " , & new_representation - > mediaStreamStructureId ) ;
2013-05-08 14:13:32 +00:00
/* RepresentationBase extension */
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_representation_base_type
( & new_representation - > RepresentationBase , a_node ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_seg_base_type_ext ( & new_representation - > SegmentBase ,
2013-06-21 23:09:30 +00:00
cur_node , parent - > SegmentBase ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentTemplate " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_template_node
2013-06-21 23:09:30 +00:00
( & new_representation - > SegmentTemplate , cur_node ,
2015-10-28 15:34:29 +00:00
parent - > SegmentTemplate ) )
goto error ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_list_node
( & new_representation - > SegmentList , cur_node , parent - > SegmentList ) )
goto error ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " BaseURL " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_baseURL_node ( & new_representation - > BaseURLs ,
cur_node ) ;
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " SubRepresentation " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_subrepresentation_node
( & new_representation - > SubRepresentations , cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
/* some sanity checking */
* list = g_list_append ( * list , new_representation ) ;
return TRUE ;
error :
gst_mpdparser_free_representation_node ( new_representation ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2015-10-28 15:34:29 +00:00
static gboolean
2013-06-21 23:09:30 +00:00
gst_mpdparser_parse_adaptation_set_node ( GList * * list , xmlNode * a_node ,
GstPeriodNode * parent )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstAdaptationSetNode * new_adap_set ;
2015-07-10 15:56:29 +00:00
gchar * actuate ;
2013-05-08 14:13:32 +00:00
new_adap_set = g_slice_new0 ( GstAdaptationSetNode ) ;
GST_LOG ( " attributes of AdaptationSet node: " ) ;
2015-07-10 15:56:29 +00:00
new_adap_set - > actuate = GST_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_adap_set - > xlink_href )
& & gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , " onLoad " ) = = 0 )
new_adap_set - > actuate = GST_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " id " , 0 ,
& new_adap_set - > id ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " group " , 0 ,
& new_adap_set - > group ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " lang " , & new_adap_set - > lang ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " contentType " ,
& new_adap_set - > contentType ) ;
gst_mpdparser_get_xml_prop_ratio ( a_node , " par " , & new_adap_set - > par ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " minBandwidth " , 0 ,
& new_adap_set - > minBandwidth ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " maxBandwidth " , 0 ,
& new_adap_set - > maxBandwidth ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " minWidth " , 0 ,
& new_adap_set - > minWidth ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " maxWidth " , 0 ,
& new_adap_set - > maxWidth ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " minHeight " , 0 ,
& new_adap_set - > minHeight ) ;
gst_mpdparser_get_xml_prop_unsigned_integer ( a_node , " maxHeight " , 0 ,
& new_adap_set - > maxHeight ) ;
gst_mpdparser_get_xml_prop_framerate ( a_node , " minFrameRate " ,
& new_adap_set - > minFrameRate ) ;
gst_mpdparser_get_xml_prop_framerate ( a_node , " maxFrameRate " ,
& new_adap_set - > maxFrameRate ) ;
gst_mpdparser_get_xml_prop_cond_uint ( a_node , " segmentAlignment " ,
& new_adap_set - > segmentAlignment ) ;
2015-07-03 15:17:58 +00:00
gst_mpdparser_get_xml_prop_boolean ( a_node , " bitstreamSwitching " ,
2015-10-30 15:55:19 +00:00
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 ;
}
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_cond_uint ( a_node , " subsegmentAlignment " ,
& new_adap_set - > subsegmentAlignment ) ;
gst_mpdparser_get_xml_prop_SAP_type ( a_node , " subsegmentStartsWithSAP " ,
& new_adap_set - > subsegmentStartsWithSAP ) ;
2013-05-08 14:13:32 +00:00
/* RepresentationBase extension */
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_representation_base_type
( & new_adap_set - > RepresentationBase , a_node ) ;
2013-05-08 14:13:32 +00:00
/* explore children nodes */
for ( cur_node = a_node - > children ; cur_node ; cur_node = cur_node - > next ) {
if ( cur_node - > type = = XML_ELEMENT_NODE ) {
2012-10-08 09:47:45 +00:00
if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Accessibility " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_descriptor_type_node ( & new_adap_set - > Accessibility ,
cur_node ) ;
2012-10-08 09:47:45 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Role " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_descriptor_type_node ( & new_adap_set - > Role ,
cur_node ) ;
2012-10-08 09:47:45 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Rating " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_descriptor_type_node ( & new_adap_set - > Rating ,
cur_node ) ;
2012-10-08 09:47:45 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Viewpoint " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_descriptor_type_node ( & new_adap_set - > Viewpoint ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
} 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 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_seg_base_type_ext ( & new_adap_set - > SegmentBase ,
2013-06-21 23:09:30 +00:00
cur_node , parent - > SegmentBase ) ;
2012-10-08 09:16:09 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_list_node ( & new_adap_set - > SegmentList ,
cur_node , parent - > SegmentList ) )
goto error ;
2012-12-20 08:04:28 +00:00
} else if ( xmlStrcmp ( cur_node - > name ,
( xmlChar * ) " ContentComponent " ) = = 0 ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_parse_content_component_node
( & new_adap_set - > ContentComponents , cur_node ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentTemplate " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_template_node
( & new_adap_set - > SegmentTemplate , cur_node , parent - > SegmentTemplate ) )
goto error ;
2013-06-21 23:09:30 +00:00
}
}
}
/* 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 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_representation_node
( & new_adap_set - > Representations , cur_node , new_adap_set ) )
goto error ;
2013-05-08 14:13:32 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
* list = g_list_append ( * list , new_adap_set ) ;
return TRUE ;
error :
gst_mpdparser_free_adaptation_set_node ( new_adap_set ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
static void
gst_mpdparser_parse_subset_node ( GList * * list , xmlNode * a_node )
{
GstSubsetNode * new_subset ;
new_subset = g_slice_new0 ( GstSubsetNode ) ;
* list = g_list_append ( * list , new_subset ) ;
GST_LOG ( " attributes of Subset node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_uint_vector_type ( a_node , " contains " ,
& new_subset - > contains , & new_subset - > size ) ;
2013-05-08 14:13:32 +00:00
}
2015-10-28 15:34:29 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_segment_template_node ( GstSegmentTemplateNode * * pointer ,
2013-06-21 23:09:30 +00:00
xmlNode * a_node , GstSegmentTemplateNode * parent )
2013-05-08 14:13:32 +00:00
{
GstSegmentTemplateNode * new_segment_template ;
2013-06-21 23:09:30 +00:00
gchar * strval ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_segment_template_node ( * pointer ) ;
2015-10-28 15:34:29 +00:00
new_segment_template = g_slice_new0 ( GstSegmentTemplateNode ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " extension of SegmentTemplate node: " ) ;
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_mult_seg_base_type_ext
2013-06-21 23:09:30 +00:00
( & new_segment_template - > MultSegBaseType , a_node ,
2015-10-28 15:34:29 +00:00
( parent ? parent - > MultSegBaseType : NULL ) ) )
goto error ;
2013-05-08 14:13:32 +00:00
2015-08-01 18:52:28 +00:00
/* Inherit attribute values from parent when the value isn't found */
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of SegmentTemplate node: " ) ;
2013-06-21 23:09:30 +00:00
if ( gst_mpdparser_get_xml_prop_string ( a_node , " media " , & strval ) ) {
new_segment_template - > media = strval ;
2015-08-01 18:52:28 +00:00
} else if ( parent ) {
new_segment_template - > media = xmlMemStrdup ( parent - > media ) ;
2013-06-21 23:09:30 +00:00
}
2015-08-01 18:52:28 +00:00
2013-06-21 23:09:30 +00:00
if ( gst_mpdparser_get_xml_prop_string ( a_node , " index " , & strval ) ) {
new_segment_template - > index = strval ;
2015-08-01 18:52:28 +00:00
} else if ( parent ) {
new_segment_template - > index = xmlMemStrdup ( parent - > index ) ;
2013-06-21 23:09:30 +00:00
}
2015-08-01 18:52:28 +00:00
2013-06-21 23:09:30 +00:00
if ( gst_mpdparser_get_xml_prop_string ( a_node , " initialization " , & strval ) ) {
new_segment_template - > initialization = strval ;
2015-08-01 18:52:28 +00:00
} else if ( parent ) {
new_segment_template - > initialization =
xmlMemStrdup ( parent - > initialization ) ;
2013-06-21 23:09:30 +00:00
}
2015-08-01 18:52:28 +00:00
2013-06-21 23:09:30 +00:00
if ( gst_mpdparser_get_xml_prop_string ( a_node , " bitstreamSwitching " , & strval ) ) {
new_segment_template - > bitstreamSwitching = strval ;
2015-08-01 18:52:28 +00:00
} else if ( parent ) {
new_segment_template - > bitstreamSwitching =
xmlMemStrdup ( parent - > bitstreamSwitching ) ;
2013-06-21 23:09:30 +00:00
}
2015-10-28 15:34:29 +00:00
* pointer = new_segment_template ;
return TRUE ;
error :
gst_mpdparser_free_segment_template_node ( new_segment_template ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
2015-10-28 15:34:29 +00:00
static gboolean
2013-05-08 14:13:32 +00:00
gst_mpdparser_parse_period_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstPeriodNode * new_period ;
2015-07-10 15:56:29 +00:00
gchar * actuate ;
2013-05-08 14:13:32 +00:00
new_period = g_slice_new0 ( GstPeriodNode ) ;
GST_LOG ( " attributes of Period node: " ) ;
2015-07-10 15:56:29 +00:00
new_period - > actuate = GST_XLINK_ACTUATE_ON_REQUEST ;
if ( gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " href " , & new_period - > xlink_href )
& & gst_mpdparser_get_xml_ns_prop_string ( a_node ,
" http://www.w3.org/1999/xlink " , " actuate " , & actuate ) ) {
if ( strcmp ( actuate , " onLoad " ) = = 0 )
new_period - > actuate = GST_XLINK_ACTUATE_ON_LOAD ;
xmlFree ( actuate ) ;
}
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " id " , & new_period - > id ) ;
2015-10-29 11:38:35 +00:00
gst_mpdparser_get_xml_prop_duration ( a_node , " start " , GST_MPD_DURATION_NONE ,
& new_period - > start ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " duration " ,
GST_MPD_DURATION_NONE , & new_period - > duration ) ;
gst_mpdparser_get_xml_prop_boolean ( a_node , " bitstreamSwitching " , FALSE ,
& new_period - > bitstreamSwitching ) ;
2013-05-08 14:13:32 +00:00
/* explore children nodes */
for ( cur_node = a_node - > children ; cur_node ; cur_node = cur_node - > next ) {
if ( cur_node - > type = = XML_ELEMENT_NODE ) {
2013-06-21 23:09:30 +00:00
if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentBase " ) = = 0 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_seg_base_type_ext ( & new_period - > SegmentBase ,
2013-06-21 23:09:30 +00:00
cur_node , NULL ) ;
2012-12-20 08:04:28 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentList " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_list_node ( & new_period - > SegmentList ,
cur_node , NULL ) )
goto error ;
2012-12-20 08:04:28 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " SegmentTemplate " ) = = 0 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_segment_template_node
( & new_period - > SegmentTemplate , cur_node , NULL ) )
goto error ;
2013-05-08 14:13:32 +00:00
} 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 ) ;
}
}
}
2013-06-21 23:09:30 +00:00
/* 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 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_adaptation_set_node
( & new_period - > AdaptationSets , cur_node , new_period ) )
goto error ;
2013-06-21 23:09:30 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
* list = g_list_append ( * list , new_period ) ;
return TRUE ;
error :
gst_mpdparser_free_period_node ( new_period ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
static void
2012-10-08 09:30:40 +00:00
gst_mpdparser_parse_program_info_node ( GList * * list , xmlNode * a_node )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstProgramInformationNode * new_prog_info ;
2012-10-08 09:30:40 +00:00
new_prog_info = g_slice_new0 ( GstProgramInformationNode ) ;
* list = g_list_append ( * list , new_prog_info ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of ProgramInformation node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " lang " , & new_prog_info - > lang ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " moreInformationURL " ,
& new_prog_info - > moreInformationURL ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_node_content ( cur_node , & new_prog_info - > Title ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Source " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_node_content ( cur_node , & new_prog_info - > Source ) ;
2013-05-08 14:13:32 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " Copyright " ) = = 0 ) {
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_node_content ( cur_node ,
& new_prog_info - > Copyright ) ;
2013-05-08 14:13:32 +00:00
}
}
}
}
static void
gst_mpdparser_parse_metrics_range_node ( GList * * list , xmlNode * a_node )
{
GstMetricsRangeNode * new_metrics_range ;
new_metrics_range = g_slice_new0 ( GstMetricsRangeNode ) ;
* list = g_list_append ( * list , new_metrics_range ) ;
GST_LOG ( " attributes of Metrics Range node: " ) ;
2015-10-29 11:38:35 +00:00
gst_mpdparser_get_xml_prop_duration ( a_node , " starttime " ,
GST_MPD_DURATION_NONE , & new_metrics_range - > starttime ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " duration " ,
GST_MPD_DURATION_NONE , & new_metrics_range - > duration ) ;
2013-05-08 14:13:32 +00:00
}
static void
gst_mpdparser_parse_metrics_node ( GList * * list , xmlNode * a_node )
{
xmlNode * cur_node ;
GstMetricsNode * new_metrics ;
new_metrics = g_slice_new0 ( GstMetricsNode ) ;
* list = g_list_append ( * list , new_metrics ) ;
GST_LOG ( " attributes of Metrics node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " metrics " , & new_metrics - > metrics ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2012-12-20 08:04:28 +00:00
gst_mpdparser_parse_metrics_range_node ( & new_metrics - > MetricsRanges ,
cur_node ) ;
2013-05-08 14:13:32 +00:00
} 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) " ) ;
}
}
}
}
2015-07-15 10:56:13 +00:00
/* 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 )
{
GstUTCTimingNode * new_timing ;
gchar * method = NULL ;
gchar * value = NULL ;
new_timing = g_slice_new0 ( GstUTCTimingNode ) ;
GST_LOG ( " attributes of UTCTiming node: " ) ;
if ( gst_mpdparser_get_xml_prop_string ( a_node , " schemeIdUri " , & method ) ) {
for ( int i = 0 ; gst_mpdparser_utc_timing_methods [ i ] . name ; + + i ) {
if ( g_ascii_strncasecmp ( gst_mpdparser_utc_timing_methods [ i ] . name ,
method , strlen ( gst_mpdparser_utc_timing_methods [ i ] . name ) ) = = 0 ) {
new_timing - > method = gst_mpdparser_utc_timing_methods [ i ] . method ;
break ;
}
}
xmlFree ( method ) ;
}
2015-08-16 10:52:09 +00:00
2015-07-15 10:56:13 +00:00
if ( gst_mpdparser_get_xml_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 ) ;
* list = g_list_append ( * list , new_timing ) ;
2015-08-16 10:52:09 +00:00
} else {
gst_mpdparser_free_utctiming_node ( new_timing ) ;
2015-07-15 10:56:13 +00:00
}
}
2015-10-28 15:34:29 +00:00
static gboolean
2012-12-17 14:19:33 +00:00
gst_mpdparser_parse_root_node ( GstMPDNode * * pointer , xmlNode * a_node )
2013-05-08 14:13:32 +00:00
{
xmlNode * cur_node ;
GstMPDNode * new_mpd ;
2012-12-17 14:19:33 +00:00
gst_mpdparser_free_mpd_node ( * pointer ) ;
2015-10-28 15:34:29 +00:00
* pointer = NULL ;
new_mpd = g_slice_new0 ( GstMPDNode ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " namespaces of root MPD node: " ) ;
new_mpd - > default_namespace =
gst_mpdparser_get_xml_node_namespace ( a_node , NULL ) ;
2012-12-20 08:04:28 +00:00
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 " ) ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " attributes of root MPD node: " ) ;
2013-06-21 21:51:46 +00:00
gst_mpdparser_get_xml_prop_string ( a_node , " schemaLocation " ,
& new_mpd - > schemaLocation ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " id " , & new_mpd - > id ) ;
gst_mpdparser_get_xml_prop_string ( a_node , " profiles " , & new_mpd - > profiles ) ;
gst_mpdparser_get_xml_prop_type ( a_node , " type " , & new_mpd - > type ) ;
gst_mpdparser_get_xml_prop_dateTime ( a_node , " availabilityStartTime " ,
& new_mpd - > availabilityStartTime ) ;
gst_mpdparser_get_xml_prop_dateTime ( a_node , " availabilityEndTime " ,
& new_mpd - > availabilityEndTime ) ;
2015-10-29 11:38:35 +00:00
gst_mpdparser_get_xml_prop_duration ( a_node , " mediaPresentationDuration " ,
GST_MPD_DURATION_NONE , & new_mpd - > mediaPresentationDuration ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " minimumUpdatePeriod " ,
GST_MPD_DURATION_NONE , & new_mpd - > minimumUpdatePeriod ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " minBufferTime " ,
GST_MPD_DURATION_NONE , & new_mpd - > minBufferTime ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " timeShiftBufferDepth " ,
GST_MPD_DURATION_NONE , & new_mpd - > timeShiftBufferDepth ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " suggestedPresentationDelay " ,
GST_MPD_DURATION_NONE , & new_mpd - > suggestedPresentationDelay ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " maxSegmentDuration " ,
GST_MPD_DURATION_NONE , & new_mpd - > maxSegmentDuration ) ;
gst_mpdparser_get_xml_prop_duration ( a_node , " maxSubsegmentDuration " ,
GST_MPD_DURATION_NONE , & new_mpd - > maxSubsegmentDuration ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2015-10-28 15:34:29 +00:00
if ( ! gst_mpdparser_parse_period_node ( & new_mpd - > Periods , cur_node ) )
goto error ;
2013-05-08 14:13:32 +00:00
} 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 ) ;
2015-07-15 10:56:13 +00:00
} else if ( xmlStrcmp ( cur_node - > name , ( xmlChar * ) " UTCTiming " ) = = 0 ) {
gst_mpdparser_parse_utctiming_node ( & new_mpd - > UTCTiming , cur_node ) ;
2013-05-08 14:13:32 +00:00
}
}
}
2015-10-28 15:34:29 +00:00
gst_mpdparser_free_mpd_node ( * pointer ) ;
* pointer = new_mpd ;
return TRUE ;
error :
2015-11-02 11:21:14 +00:00
gst_mpdparser_free_mpd_node ( new_mpd ) ;
2015-10-28 15:34:29 +00:00
return FALSE ;
2013-05-08 14:13:32 +00:00
}
/* 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 */
2013-12-02 20:31:41 +00:00
static GstStreamMimeType
gst_mpdparser_representation_get_mimetype ( GstAdaptationSetNode * adapt_set ,
GstRepresentationNode * rep )
2013-05-08 14:13:32 +00:00
{
2013-12-02 20:31:41 +00:00
gchar * mime = NULL ;
if ( rep - > RepresentationBase )
mime = rep - > RepresentationBase - > mimeType ;
if ( mime = = NULL & & adapt_set - > RepresentationBase ) {
mime = adapt_set - > RepresentationBase - > mimeType ;
2013-05-08 14:13:32 +00:00
}
2015-02-12 14:15:31 +00:00
if ( strncmp_ext ( mime , " audio " ) = = 0 )
2013-12-02 20:31:41 +00:00
return GST_STREAM_AUDIO ;
2015-02-12 14:15:31 +00:00
if ( strncmp_ext ( mime , " video " ) = = 0 )
2013-12-02 20:31:41 +00:00
return GST_STREAM_VIDEO ;
2015-02-12 14:15:31 +00:00
if ( strncmp_ext ( mime , " application " ) = = 0 )
2013-12-02 20:31:41 +00:00
return GST_STREAM_APPLICATION ;
2013-05-08 14:13:32 +00:00
2013-12-02 20:31:41 +00:00
return GST_STREAM_UNKNOWN ;
2013-05-08 14:13:32 +00:00
}
static GstRepresentationNode *
gst_mpdparser_get_lowest_representation ( GList * Representations )
{
GList * list = NULL ;
2013-01-16 18:58:52 +00:00
GstRepresentationNode * rep = NULL ;
GstRepresentationNode * lowest = NULL ;
2013-05-08 14:13:32 +00:00
if ( Representations = = NULL )
return NULL ;
2013-01-16 18:58:52 +00:00
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 ;
}
}
2013-05-08 14:13:32 +00:00
2013-01-16 18:58:52 +00:00
return lowest ;
2013-05-08 14:13:32 +00:00
}
#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 *
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_segment_base ( GstPeriodNode * Period ,
GstAdaptationSetNode * AdaptationSet ,
GstRepresentationNode * Representation )
2013-05-08 14:13:32 +00:00
{
GstSegmentBaseType * SegmentBase = NULL ;
2015-09-16 21:12:54 +00:00
if ( Representation & & Representation - > SegmentBase ) {
2013-05-08 14:13:32 +00:00
SegmentBase = Representation - > SegmentBase ;
2015-09-16 21:12:54 +00:00
} else if ( AdaptationSet & & AdaptationSet - > SegmentBase ) {
2013-05-08 14:13:32 +00:00
SegmentBase = AdaptationSet - > SegmentBase ;
2015-09-16 21:12:54 +00:00
} else if ( Period & & Period - > SegmentBase ) {
2013-05-08 14:13:32 +00:00
SegmentBase = Period - > SegmentBase ;
}
2012-10-09 11:24:23 +00:00
/* the SegmentBase element could be encoded also inside a SegmentList element */
if ( SegmentBase = = NULL ) {
2012-12-20 08:04:28 +00:00
if ( Representation & & Representation - > SegmentList
& & Representation - > SegmentList - > MultSegBaseType
2015-09-16 21:12:54 +00:00
& & Representation - > SegmentList - > MultSegBaseType - > SegBaseType ) {
2012-10-09 11:24:23 +00:00
SegmentBase = Representation - > SegmentList - > MultSegBaseType - > SegBaseType ;
2012-12-20 08:04:28 +00:00
} else if ( AdaptationSet & & AdaptationSet - > SegmentList
& & AdaptationSet - > SegmentList - > MultSegBaseType
2015-09-16 21:12:54 +00:00
& & AdaptationSet - > SegmentList - > MultSegBaseType - > SegBaseType ) {
2012-10-09 11:24:23 +00:00
SegmentBase = AdaptationSet - > SegmentList - > MultSegBaseType - > SegBaseType ;
2012-12-20 08:04:28 +00:00
} else if ( Period & & Period - > SegmentList
& & Period - > SegmentList - > MultSegBaseType
2015-09-16 21:12:54 +00:00
& & Period - > SegmentList - > MultSegBaseType - > SegBaseType ) {
2012-10-09 11:24:23 +00:00
SegmentBase = Period - > SegmentList - > MultSegBaseType - > SegBaseType ;
}
}
2013-05-08 14:13:32 +00:00
return SegmentBase ;
}
2013-01-16 18:58:52 +00:00
gint
gst_mpdparser_get_rep_idx_with_min_bandwidth ( GList * Representations )
{
2013-01-25 12:36:35 +00:00
GList * list = NULL , * lowest = NULL ;
2013-01-16 18:58:52 +00:00
GstRepresentationNode * rep = NULL ;
gint lowest_bandwidth = - 1 ;
if ( Representations = = NULL )
2013-01-25 12:36:35 +00:00
return - 1 ;
2013-01-16 18:58:52 +00:00
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 ;
}
2013-05-08 14:13:32 +00:00
gint
gst_mpdparser_get_rep_idx_with_max_bandwidth ( GList * Representations ,
gint max_bandwidth )
{
GList * list = NULL , * best = NULL ;
GstRepresentationNode * representation ;
2013-01-16 18:58:52 +00:00
gint best_bandwidth = 0 ;
GST_DEBUG ( " max_bandwidth = %i " , max_bandwidth ) ;
2013-05-08 14:13:32 +00:00
if ( Representations = = NULL )
return - 1 ;
2012-12-20 08:04:28 +00:00
if ( max_bandwidth < = 0 ) /* 0 => get lowest representation available */
2015-06-18 12:08:25 +00:00
return gst_mpdparser_get_rep_idx_with_min_bandwidth ( Representations ) ;
2013-05-08 14:13:32 +00:00
for ( list = g_list_first ( Representations ) ; list ; list = g_list_next ( list ) ) {
representation = ( GstRepresentationNode * ) list - > data ;
2013-01-16 18:58:52 +00:00
if ( representation & & representation - > bandwidth < = max_bandwidth & &
representation - > bandwidth > best_bandwidth ) {
2013-05-08 14:13:32 +00:00
best = list ;
2013-01-16 18:58:52 +00:00
best_bandwidth = representation - > bandwidth ;
2013-05-08 14:13:32 +00:00
}
}
return best ? g_list_position ( Representations , best ) : - 1 ;
}
static GstSegmentListNode *
2015-09-08 10:36:23 +00:00
gst_mpd_client_fetch_external_segment_list ( GstMpdClient * client ,
GstPeriodNode * Period ,
GstAdaptationSetNode * AdaptationSet ,
GstRepresentationNode * Representation ,
GstSegmentListNode * parent , GstSegmentListNode * segment_list ,
gboolean * error )
{
GstFragment * download ;
GstBuffer * segment_list_buffer ;
GstMapInfo map ;
GError * err = NULL ;
xmlDocPtr doc ;
GstUri * base_uri , * uri ;
gchar * query = NULL ;
gchar * uri_string ;
GstSegmentListNode * new_segment_list = NULL ;
* error = FALSE ;
/* ISO/IEC 23009-1:2014 5.5.3 4)
* Remove nodes that resolve to nothing when resolving
*/
if ( strcmp ( segment_list - > xlink_href ,
" urn:mpeg:dash:resolve-to-zero:2013 " ) = = 0 ) {
return NULL ;
}
if ( ! client - > downloader ) {
* error = TRUE ;
return NULL ;
}
/* Build absolute URI */
/* Get base URI at the MPD level */
base_uri =
gst_uri_from_string ( client - >
mpd_base_uri ? client - > mpd_base_uri : client - > mpd_uri ) ;
/* combine a BaseURL at the MPD level with the current base url */
base_uri = combine_urls ( base_uri , client - > mpd_node - > BaseURLs , & query , 0 ) ;
/* combine a BaseURL at the Period level with the current base url */
base_uri = combine_urls ( base_uri , Period - > BaseURLs , & query , 0 ) ;
if ( AdaptationSet ) {
/* combine a BaseURL at the AdaptationSet level with the current base url */
base_uri = combine_urls ( base_uri , AdaptationSet - > BaseURLs , & query , 0 ) ;
if ( Representation ) {
/* combine a BaseURL at the Representation level with the current base url */
base_uri = combine_urls ( base_uri , Representation - > BaseURLs , & query , 0 ) ;
}
}
uri = gst_uri_from_string_with_base ( base_uri , segment_list - > xlink_href ) ;
if ( query )
gst_uri_set_query_string ( uri , query ) ;
g_free ( query ) ;
uri_string = gst_uri_to_string ( uri ) ;
gst_uri_unref ( base_uri ) ;
gst_uri_unref ( uri ) ;
download =
gst_uri_downloader_fetch_uri ( client - > downloader ,
uri_string , client - > mpd_uri , TRUE , FALSE , TRUE , & err ) ;
g_free ( uri_string ) ;
if ( ! download ) {
GST_ERROR ( " Failed to download external SegmentList node at '%s': %s " ,
segment_list - > xlink_href , err - > message ) ;
g_clear_error ( & err ) ;
* error = TRUE ;
return NULL ;
}
segment_list_buffer = gst_fragment_get_buffer ( download ) ;
g_object_unref ( download ) ;
gst_buffer_map ( segment_list_buffer , & map , GST_MAP_READ ) ;
doc =
xmlReadMemory ( ( const gchar * ) map . data , map . size , " noname.xml " , NULL ,
XML_PARSE_NONET ) ;
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type ! = XML_ELEMENT_NODE | |
xmlStrcmp ( root_element - > name , ( xmlChar * ) " SegmentList " ) ! = 0 ) {
xmlFreeDoc ( doc ) ;
gst_buffer_unmap ( segment_list_buffer , & map ) ;
gst_buffer_unref ( segment_list_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_mpdparser_parse_segment_list_node ( & new_segment_list , root_element ,
parent ) ;
} else {
GST_ERROR ( " Failed to parse adaptation set node XML " ) ;
gst_buffer_unmap ( segment_list_buffer , & map ) ;
gst_buffer_unref ( segment_list_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_buffer_unmap ( segment_list_buffer , & map ) ;
gst_buffer_unref ( segment_list_buffer ) ;
return new_segment_list ;
}
static GstSegmentListNode *
gst_mpdparser_get_segment_list ( GstMpdClient * client , GstPeriodNode * Period ,
2012-12-20 08:04:28 +00:00
GstAdaptationSetNode * AdaptationSet ,
GstRepresentationNode * Representation )
2013-05-08 14:13:32 +00:00
{
2015-09-08 10:36:23 +00:00
GstSegmentListNode * * SegmentList ;
GstSegmentListNode * ParentSegmentList = NULL ;
2013-05-08 14:13:32 +00:00
2012-10-09 11:24:23 +00:00
if ( Representation & & Representation - > SegmentList ) {
2015-09-08 10:36:23 +00:00
SegmentList = & Representation - > SegmentList ;
ParentSegmentList = AdaptationSet - > SegmentList ;
2012-10-09 11:24:23 +00:00
} else if ( AdaptationSet & & AdaptationSet - > SegmentList ) {
2015-09-08 10:36:23 +00:00
SegmentList = & AdaptationSet - > SegmentList ;
ParentSegmentList = Period - > SegmentList ;
Representation = NULL ;
2012-10-08 09:16:09 +00:00
} else {
2015-09-08 10:36:23 +00:00
Representation = NULL ;
AdaptationSet = NULL ;
SegmentList = & Period - > SegmentList ;
}
/* Resolve external segment list here. */
if ( * SegmentList & & ( * SegmentList ) - > xlink_href ) {
GstSegmentListNode * new_segment_list ;
gboolean error ;
new_segment_list =
gst_mpd_client_fetch_external_segment_list ( client , Period ,
AdaptationSet , Representation , ParentSegmentList , * SegmentList , & error ) ;
gst_mpdparser_free_segment_list_node ( * SegmentList ) ;
* SegmentList = new_segment_list ;
2012-10-08 09:16:09 +00:00
}
2013-05-08 14:13:32 +00:00
2015-09-08 10:36:23 +00:00
return * SegmentList ;
2013-05-08 14:13:32 +00:00
}
/* memory management functions */
static void
gst_mpdparser_free_mpd_node ( GstMPDNode * mpd_node )
{
if ( mpd_node ) {
2013-06-25 16:26:24 +00:00
if ( mpd_node - > default_namespace )
xmlFree ( mpd_node - > default_namespace ) ;
if ( mpd_node - > namespace_xsi )
xmlFree ( mpd_node - > namespace_xsi ) ;
if ( mpd_node - > namespace_ext )
xmlFree ( mpd_node - > namespace_ext ) ;
if ( mpd_node - > schemaLocation )
xmlFree ( mpd_node - > schemaLocation ) ;
if ( mpd_node - > id )
xmlFree ( mpd_node - > id ) ;
if ( mpd_node - > profiles )
xmlFree ( mpd_node - > profiles ) ;
2013-05-08 14:13:32 +00:00
if ( mpd_node - > availabilityStartTime )
gst_date_time_unref ( mpd_node - > availabilityStartTime ) ;
if ( mpd_node - > availabilityEndTime )
gst_date_time_unref ( mpd_node - > availabilityEndTime ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( mpd_node - > ProgramInfo ,
( GDestroyNotify ) gst_mpdparser_free_prog_info_node ) ;
g_list_free_full ( mpd_node - > BaseURLs ,
( GDestroyNotify ) gst_mpdparser_free_base_url_node ) ;
g_list_free_full ( mpd_node - > Locations , ( GDestroyNotify ) xmlFree ) ;
g_list_free_full ( mpd_node - > Periods ,
( GDestroyNotify ) gst_mpdparser_free_period_node ) ;
g_list_free_full ( mpd_node - > Metrics ,
( GDestroyNotify ) gst_mpdparser_free_metrics_node ) ;
2015-07-15 10:56:13 +00:00
g_list_free_full ( mpd_node - > UTCTiming ,
( GDestroyNotify ) gst_mpdparser_free_utctiming_node ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstMPDNode , mpd_node ) ;
}
}
static void
gst_mpdparser_free_prog_info_node ( GstProgramInformationNode * prog_info_node )
{
if ( prog_info_node ) {
2013-06-25 16:26:24 +00:00
if ( prog_info_node - > lang )
xmlFree ( prog_info_node - > lang ) ;
if ( prog_info_node - > moreInformationURL )
xmlFree ( prog_info_node - > moreInformationURL ) ;
if ( prog_info_node - > Title )
xmlFree ( prog_info_node - > Title ) ;
if ( prog_info_node - > Source )
xmlFree ( prog_info_node - > Source ) ;
if ( prog_info_node - > Copyright )
xmlFree ( prog_info_node - > Copyright ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstProgramInformationNode , prog_info_node ) ;
}
}
static void
gst_mpdparser_free_metrics_node ( GstMetricsNode * metrics_node )
{
if ( metrics_node ) {
2013-06-25 16:26:24 +00:00
if ( metrics_node - > metrics )
xmlFree ( metrics_node - > metrics ) ;
g_list_free_full ( metrics_node - > MetricsRanges ,
( GDestroyNotify ) gst_mpdparser_free_metrics_range_node ) ;
g_slice_free ( GstMetricsNode , metrics_node ) ;
2013-05-08 14:13:32 +00:00
}
}
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 ) {
2013-06-25 16:26:24 +00:00
if ( period_node - > id )
xmlFree ( period_node - > id ) ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_seg_base_type_ext ( period_node - > SegmentBase ) ;
2012-10-08 09:16:09 +00:00
gst_mpdparser_free_segment_list_node ( period_node - > SegmentList ) ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_segment_template_node ( period_node - > SegmentTemplate ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( period_node - > AdaptationSets ,
( GDestroyNotify ) gst_mpdparser_free_adaptation_set_node ) ;
g_list_free_full ( period_node - > Subsets ,
( GDestroyNotify ) gst_mpdparser_free_subset_node ) ;
g_list_free_full ( period_node - > BaseURLs ,
( GDestroyNotify ) gst_mpdparser_free_base_url_node ) ;
2015-07-10 15:56:29 +00:00
if ( period_node - > xlink_href )
xmlFree ( period_node - > xlink_href ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstPeriodNode , period_node ) ;
}
}
static void
gst_mpdparser_free_subset_node ( GstSubsetNode * subset_node )
{
if ( subset_node ) {
2013-06-25 16:26:24 +00:00
if ( subset_node - > contains )
xmlFree ( subset_node - > contains ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstSubsetNode , subset_node ) ;
}
}
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_free_segment_template_node ( GstSegmentTemplateNode *
segment_template_node )
2013-05-08 14:13:32 +00:00
{
if ( segment_template_node ) {
2013-06-25 16:26:24 +00:00
if ( segment_template_node - > media )
xmlFree ( segment_template_node - > media ) ;
if ( segment_template_node - > index )
xmlFree ( segment_template_node - > index ) ;
if ( segment_template_node - > initialization )
xmlFree ( segment_template_node - > initialization ) ;
if ( segment_template_node - > bitstreamSwitching )
xmlFree ( segment_template_node - > bitstreamSwitching ) ;
2013-05-08 14:13:32 +00:00
/* 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 ) {
2013-06-25 16:26:24 +00:00
if ( representation_base - > profiles )
xmlFree ( representation_base - > profiles ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstRatio , representation_base - > sar ) ;
g_slice_free ( GstFrameRate , representation_base - > frameRate ) ;
2013-06-25 16:26:24 +00:00
if ( representation_base - > audioSamplingRate )
xmlFree ( representation_base - > audioSamplingRate ) ;
if ( representation_base - > mimeType )
xmlFree ( representation_base - > mimeType ) ;
if ( representation_base - > segmentProfiles )
xmlFree ( representation_base - > segmentProfiles ) ;
if ( representation_base - > codecs )
xmlFree ( representation_base - > codecs ) ;
if ( representation_base - > scanType )
xmlFree ( representation_base - > scanType ) ;
g_list_free_full ( representation_base - > FramePacking ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( representation_base - > AudioChannelConfiguration ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( representation_base - > ContentProtection ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstRepresentationBaseType , representation_base ) ;
}
}
static void
gst_mpdparser_free_adaptation_set_node ( GstAdaptationSetNode *
adaptation_set_node )
{
if ( adaptation_set_node ) {
2013-06-25 16:26:24 +00:00
if ( adaptation_set_node - > lang )
xmlFree ( adaptation_set_node - > lang ) ;
if ( adaptation_set_node - > contentType )
xmlFree ( adaptation_set_node - > contentType ) ;
2013-05-08 14:13:32 +00:00
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 ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( adaptation_set_node - > Accessibility ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( adaptation_set_node - > Role ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( adaptation_set_node - > Rating ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( adaptation_set_node - > Viewpoint ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_representation_base_type
( adaptation_set_node - > RepresentationBase ) ;
gst_mpdparser_free_seg_base_type_ext ( adaptation_set_node - > SegmentBase ) ;
2012-10-08 09:16:09 +00:00
gst_mpdparser_free_segment_list_node ( adaptation_set_node - > SegmentList ) ;
2013-01-25 12:36:35 +00:00
gst_mpdparser_free_segment_template_node
( adaptation_set_node - > SegmentTemplate ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( adaptation_set_node - > BaseURLs ,
( GDestroyNotify ) gst_mpdparser_free_base_url_node ) ;
g_list_free_full ( adaptation_set_node - > Representations ,
( GDestroyNotify ) gst_mpdparser_free_representation_node ) ;
g_list_free_full ( adaptation_set_node - > ContentComponents ,
( GDestroyNotify ) gst_mpdparser_free_content_component_node ) ;
2015-07-10 15:56:29 +00:00
if ( adaptation_set_node - > xlink_href )
xmlFree ( adaptation_set_node - > xlink_href ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstAdaptationSetNode , adaptation_set_node ) ;
}
}
static void
gst_mpdparser_free_representation_node ( GstRepresentationNode *
representation_node )
{
if ( representation_node ) {
2013-06-25 16:26:24 +00:00
if ( representation_node - > id )
xmlFree ( representation_node - > id ) ;
2013-05-08 14:13:32 +00:00
g_strfreev ( representation_node - > dependencyId ) ;
g_strfreev ( representation_node - > mediaStreamStructureId ) ;
gst_mpdparser_free_representation_base_type
( representation_node - > RepresentationBase ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( representation_node - > SubRepresentations ,
( GDestroyNotify ) gst_mpdparser_free_subrepresentation_node ) ;
2013-05-08 14:13:32 +00:00
gst_mpdparser_free_seg_base_type_ext ( representation_node - > SegmentBase ) ;
2013-01-25 12:36:35 +00:00
gst_mpdparser_free_segment_template_node
( representation_node - > SegmentTemplate ) ;
2012-10-08 09:16:09 +00:00
gst_mpdparser_free_segment_list_node ( representation_node - > SegmentList ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( representation_node - > BaseURLs ,
( GDestroyNotify ) gst_mpdparser_free_base_url_node ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstRepresentationNode , representation_node ) ;
}
}
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_free_subrepresentation_node ( GstSubRepresentationNode *
subrep_node )
2013-05-08 14:13:32 +00:00
{
if ( subrep_node ) {
2013-01-25 12:36:35 +00:00
gst_mpdparser_free_representation_base_type
( subrep_node - > RepresentationBase ) ;
2013-06-25 16:26:24 +00:00
if ( subrep_node - > dependencyLevel )
xmlFree ( subrep_node - > dependencyLevel ) ;
2013-05-08 14:13:32 +00:00
g_strfreev ( subrep_node - > contentComponent ) ;
2013-06-25 16:26:24 +00:00
g_slice_free ( GstSubRepresentationNode , subrep_node ) ;
2013-05-08 14:13:32 +00:00
}
}
static void
gst_mpdparser_free_s_node ( GstSNode * s_node )
{
if ( s_node ) {
g_slice_free ( GstSNode , s_node ) ;
}
}
2013-07-05 02:42:23 +00:00
static GstSegmentTimelineNode *
gst_mpdparser_segment_timeline_node_new ( void )
{
GstSegmentTimelineNode * node = g_slice_new0 ( GstSegmentTimelineNode ) ;
g_queue_init ( & node - > S ) ;
return node ;
}
2013-05-08 14:13:32 +00:00
static void
gst_mpdparser_free_segment_timeline_node ( GstSegmentTimelineNode * seg_timeline )
{
if ( seg_timeline ) {
2013-12-02 20:41:01 +00:00
g_queue_foreach ( & seg_timeline - > S , ( GFunc ) gst_mpdparser_free_s_node , NULL ) ;
2013-07-05 02:42:23 +00:00
g_queue_clear ( & seg_timeline - > S ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstSegmentTimelineNode , seg_timeline ) ;
}
}
static void
gst_mpdparser_free_url_type_node ( GstURLType * url_type_node )
{
if ( url_type_node ) {
2013-06-25 16:26:24 +00:00
if ( url_type_node - > sourceURL )
xmlFree ( url_type_node - > sourceURL ) ;
2013-05-08 14:13:32 +00:00
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 ) {
2013-06-25 16:26:24 +00:00
if ( seg_base_type - > indexRange )
2013-07-01 16:19:15 +00:00
g_slice_free ( GstRange , seg_base_type - > indexRange ) ;
2013-05-08 14:13:32 +00:00
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 ) {
2013-06-25 16:26:24 +00:00
g_list_free_full ( segment_list_node - > SegmentURL ,
( GDestroyNotify ) gst_mpdparser_free_segment_url_node ) ;
2013-05-08 14:13:32 +00:00
/* MultipleSegmentBaseType extension */
gst_mpdparser_free_mult_seg_base_type_ext
( segment_list_node - > MultSegBaseType ) ;
2015-07-10 15:56:29 +00:00
if ( segment_list_node - > xlink_href )
xmlFree ( segment_list_node - > xlink_href ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstSegmentListNode , segment_list_node ) ;
}
}
static void
gst_mpdparser_free_segment_url_node ( GstSegmentURLNode * segment_url )
{
if ( segment_url ) {
2013-06-25 16:26:24 +00:00
if ( segment_url - > media )
xmlFree ( segment_url - > media ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstRange , segment_url - > mediaRange ) ;
2013-06-25 16:26:24 +00:00
if ( segment_url - > index )
xmlFree ( segment_url - > index ) ;
2013-05-08 14:13:32 +00:00
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 ) {
2013-06-25 16:26:24 +00:00
if ( base_url_node - > baseURL )
xmlFree ( base_url_node - > baseURL ) ;
if ( base_url_node - > serviceLocation )
xmlFree ( base_url_node - > serviceLocation ) ;
if ( base_url_node - > byteRange )
xmlFree ( base_url_node - > byteRange ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstBaseURL , base_url_node ) ;
}
}
static void
gst_mpdparser_free_descriptor_type_node ( GstDescriptorType * descriptor_type )
{
if ( descriptor_type ) {
2013-06-25 16:26:24 +00:00
if ( descriptor_type - > schemeIdUri )
xmlFree ( descriptor_type - > schemeIdUri ) ;
if ( descriptor_type - > value )
xmlFree ( descriptor_type - > value ) ;
g_slice_free ( GstDescriptorType , descriptor_type ) ;
2013-05-08 14:13:32 +00:00
}
}
static void
2012-12-20 08:04:28 +00:00
gst_mpdparser_free_content_component_node ( GstContentComponentNode *
content_component_node )
2013-05-08 14:13:32 +00:00
{
if ( content_component_node ) {
2013-06-25 16:26:24 +00:00
if ( content_component_node - > lang )
xmlFree ( content_component_node - > lang ) ;
if ( content_component_node - > contentType )
xmlFree ( content_component_node - > contentType ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstRatio , content_component_node - > par ) ;
2013-06-25 16:26:24 +00:00
g_list_free_full ( content_component_node - > Accessibility ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( content_component_node - > Role ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( content_component_node - > Rating ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
g_list_free_full ( content_component_node - > Viewpoint ,
( GDestroyNotify ) gst_mpdparser_free_descriptor_type_node ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstContentComponentNode , content_component_node ) ;
}
}
2015-07-15 10:56:13 +00:00
static void
gst_mpdparser_free_utctiming_node ( GstUTCTimingNode * timing_type )
{
if ( timing_type ) {
if ( timing_type - > urls )
g_strfreev ( timing_type - > urls ) ;
g_slice_free ( GstUTCTimingNode , timing_type ) ;
}
}
2012-10-22 16:12:30 +00:00
static void
gst_mpdparser_free_stream_period ( GstStreamPeriod * stream_period )
{
if ( stream_period ) {
g_slice_free ( GstStreamPeriod , stream_period ) ;
}
}
2013-05-08 14:13:32 +00:00
static void
gst_mpdparser_free_media_segment ( GstMediaSegment * media_segment )
{
if ( media_segment ) {
g_slice_free ( GstMediaSegment , media_segment ) ;
}
}
2013-07-05 02:42:23 +00:00
static void
gst_mpdparser_init_active_stream_segments ( GstActiveStream * stream )
{
g_assert ( stream - > segments = = NULL ) ;
stream - > segments = g_ptr_array_new ( ) ;
2013-12-02 20:41:01 +00:00
g_ptr_array_set_free_func ( stream - > segments ,
( GDestroyNotify ) gst_mpdparser_free_media_segment ) ;
2013-07-05 02:42:23 +00:00
}
2013-05-08 14:13:32 +00:00
static void
gst_mpdparser_free_active_stream ( GstActiveStream * active_stream )
{
if ( active_stream ) {
2013-02-13 00:54:32 +00:00
g_free ( active_stream - > baseURL ) ;
active_stream - > baseURL = NULL ;
g_free ( active_stream - > queryURL ) ;
active_stream - > queryURL = NULL ;
2013-07-05 16:22:17 +00:00
if ( active_stream - > segments )
g_ptr_array_unref ( active_stream - > segments ) ;
2013-05-08 14:13:32 +00:00
g_slice_free ( GstActiveStream , active_stream ) ;
}
}
static gchar *
2013-01-25 12:36:35 +00:00
gst_mpdparser_get_mediaURL ( GstActiveStream * stream ,
2012-12-20 08:04:28 +00:00
GstSegmentURLNode * segmentURL )
2013-05-08 14:13:32 +00:00
{
2012-10-25 15:53:53 +00:00
const gchar * url_prefix ;
2012-10-24 16:10:47 +00:00
2013-01-16 18:58:52 +00:00
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
2012-10-24 16:04:07 +00:00
g_return_val_if_fail ( segmentURL ! = NULL , NULL ) ;
2012-10-24 16:10:47 +00:00
2013-01-16 18:58:52 +00:00
url_prefix = segmentURL - > media ? segmentURL - > media : stream - > baseURL ;
2012-10-24 16:10:47 +00:00
g_return_val_if_fail ( url_prefix ! = NULL , NULL ) ;
2013-05-08 14:13:32 +00:00
2013-06-20 06:52:31 +00:00
return segmentURL - > media ;
2012-10-24 16:04:07 +00:00
}
2013-05-06 21:08:54 +00:00
static const gchar *
gst_mpdparser_get_initializationURL ( GstActiveStream * stream ,
GstURLType * InitializationURL )
2012-10-24 16:04:07 +00:00
{
2013-05-06 21:08:54 +00:00
const gchar * url_prefix ;
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
2013-05-08 14:13:32 +00:00
2015-09-16 21:12:54 +00:00
url_prefix = ( InitializationURL
& & InitializationURL - > sourceURL ) ? InitializationURL - >
sourceURL : stream - > baseURL ;
2013-05-06 21:08:54 +00:00
return url_prefix ;
2013-05-08 14:13:32 +00:00
}
2015-06-04 08:47:07 +00:00
/* ISO/IEC 23009-1:2004 5.3.9.4.4 */
static gboolean
validate_format ( const gchar * format )
{
2015-10-29 11:54:34 +00:00
const gchar * p = format ;
2015-06-04 08:47:07 +00:00
2015-10-29 11:54:34 +00:00
/* Check if it starts with % */
if ( ! p | | p [ 0 ] ! = ' % ' )
return FALSE ;
2015-06-04 08:47:07 +00:00
p + + ;
2015-10-29 12:04:31 +00:00
/* the spec mandates a format like %0[width]d */
/* Following the %, we must have a 0 */
if ( p [ 0 ] ! = ' 0 ' )
2015-09-09 11:36:10 +00:00
return FALSE ;
2015-10-29 12:04:31 +00:00
/* Following the % must be a number starting with 0
2015-06-04 08:47:07 +00:00
*/
2015-09-09 11:36:10 +00:00
while ( g_ascii_isdigit ( * p ) )
2015-06-04 08:47:07 +00:00
p + + ;
2015-10-29 12:04:31 +00:00
/* After any 0 and alphanumeric values, there must be a d.
2015-06-04 08:47:07 +00:00
*/
2015-10-29 12:04:31 +00:00
if ( p [ 0 ] ! = ' d ' )
2015-06-04 08:47:07 +00:00
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 ;
}
2015-06-04 09:05:07 +00:00
static gchar *
promote_format_to_uint64 ( const gchar * format )
{
2015-10-29 11:54:34 +00:00
const gchar * p = format ;
2015-06-04 09:05:07 +00:00
gchar * promoted_format ;
/* Must be called with a validated format! */
g_return_val_if_fail ( validate_format ( format ) , NULL ) ;
2015-10-29 11:54:34 +00:00
/* it starts with % */
2015-06-04 09:05:07 +00:00
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 + + ;
}
2015-10-29 12:04:31 +00:00
/* After any 0 and alphanumeric values, there must be a d.
* Otherwise validation would have failed
2015-06-04 09:05:07 +00:00
*/
2015-10-29 12:04:31 +00:00
g_assert ( p [ 0 ] = = ' d ' ) ;
2015-06-04 09:05:07 +00:00
promoted_format =
g_strdup_printf ( " %.*s " G_GINT64_MODIFIER " %s " , ( gint ) ( p - format ) ,
format , p ) ;
return promoted_format ;
}
2015-09-29 15:17:03 +00:00
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 ;
}
2013-05-08 14:13:32 +00:00
static gchar *
2012-12-20 08:04:28 +00:00
gst_mpdparser_build_URL_from_template ( const gchar * url_template ,
2013-02-19 22:35:34 +00:00
const gchar * id , guint number , guint bandwidth , guint64 time )
2013-05-08 14:13:32 +00:00
{
2015-01-21 09:17:04 +00:00
static const gchar default_format [ ] = " %01d " ;
2013-02-19 22:35:34 +00:00
gchar * * tokens , * token , * ret ;
const gchar * format ;
2013-05-08 14:13:32 +00:00
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 ) ;
2015-10-29 11:54:34 +00:00
/*
* 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 ;
}
2013-05-08 14:13:32 +00:00
for ( i = 0 ; i < num_tokens ; i + + ) {
token = tokens [ i ] ;
format = default_format ;
2015-10-29 11:54:34 +00:00
/* 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 ;
2013-05-08 14:13:32 +00:00
if ( ! g_strcmp0 ( token , " RepresentationID " ) ) {
2015-09-29 15:17:03 +00:00
if ( ! gst_mpdparser_validate_rfc1738_url ( id ) ) {
GST_WARNING ( " String '%s' has characters invalid in an RFC 1738 URL " ,
id ) ;
goto invalid_format ;
}
2013-05-08 14:13:32 +00:00
tokens [ i ] = g_strdup_printf ( " %s " , id ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Number " , 6 ) ) {
if ( strlen ( token ) > 6 ) {
2012-12-20 08:04:28 +00:00
format = token + 6 ; /* format tag */
2013-05-08 14:13:32 +00:00
}
2015-06-04 08:47:07 +00:00
if ( ! validate_format ( format ) )
goto invalid_format ;
2013-05-08 14:13:32 +00:00
tokens [ i ] = g_strdup_printf ( format , number ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Bandwidth " , 9 ) ) {
if ( strlen ( token ) > 9 ) {
2012-12-20 08:04:28 +00:00
format = token + 9 ; /* format tag */
2013-05-08 14:13:32 +00:00
}
2015-06-04 08:47:07 +00:00
if ( ! validate_format ( format ) )
goto invalid_format ;
2013-05-08 14:13:32 +00:00
tokens [ i ] = g_strdup_printf ( format , bandwidth ) ;
g_free ( token ) ;
} else if ( ! strncmp ( token , " Time " , 4 ) ) {
2015-06-04 09:05:07 +00:00
gchar * promoted_format ;
2013-05-08 14:13:32 +00:00
if ( strlen ( token ) > 4 ) {
2012-12-20 08:04:28 +00:00
format = token + 4 ; /* format tag */
2013-05-08 14:13:32 +00:00
}
2015-06-04 08:47:07 +00:00
if ( ! validate_format ( format ) )
goto invalid_format ;
2015-06-04 09:05:07 +00:00
promoted_format = promote_format_to_uint64 ( format ) ;
tokens [ i ] = g_strdup_printf ( promoted_format , time ) ;
g_free ( promoted_format ) ;
2013-05-08 14:13:32 +00:00
g_free ( token ) ;
} else if ( ! g_strcmp0 ( token , " " ) ) {
2015-10-29 11:54:34 +00:00
tokens [ i ] = g_strdup_printf ( " %s " , " $ " ) ;
g_free ( token ) ;
2012-12-17 14:46:32 +00:00
} else {
2015-10-29 11:54:34 +00:00
/* 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 ;
2013-05-08 14:13:32 +00:00
}
}
ret = g_strjoinv ( NULL , tokens ) ;
2015-06-04 08:47:07 +00:00
2013-05-08 14:13:32 +00:00
g_strfreev ( tokens ) ;
return ret ;
2015-06-04 08:47:07 +00:00
invalid_format :
{
GST_ERROR ( " Invalid format '%s' in '%s' " , format , token ) ;
g_strfreev ( tokens ) ;
return NULL ;
}
2013-05-08 14:13:32 +00:00
}
2014-10-10 10:24:08 +00:00
guint
gst_mpd_client_get_period_index_at_time ( GstMpdClient * client ,
2014-12-29 13:44:19 +00:00
GstDateTime * time )
2014-10-10 10:24:08 +00:00
{
GList * iter ;
guint period_idx = G_MAXUINT ;
guint idx ;
gint64 time_offset ;
GstDateTime * avail_start =
gst_mpd_client_get_availability_start_time ( client ) ;
GstStreamPeriod * stream_period ;
if ( avail_start = = NULL )
return 0 ;
time_offset = gst_mpd_client_calculate_time_difference ( avail_start , time ) ;
gst_date_time_unref ( avail_start ) ;
if ( time_offset < 0 )
return 0 ;
2015-09-22 14:17:38 +00:00
if ( ! gst_mpd_client_setup_media_presentation ( client , time_offset , - 1 , NULL ) )
return 0 ;
2014-12-29 13:44:19 +00:00
for ( idx = 0 , iter = client - > periods ; iter ; idx + + , iter = g_list_next ( iter ) ) {
2014-10-10 10:24:08 +00:00
stream_period = iter - > data ;
if ( stream_period - > start < = time_offset
& & stream_period - > start + stream_period - > duration > time_offset ) {
period_idx = idx ;
break ;
}
}
return period_idx ;
}
2012-10-22 16:12:30 +00:00
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 ) ;
2012-12-17 14:19:33 +00:00
return g_list_nth_data ( client - > periods , client - > period_idx ) ;
2012-10-22 16:12:30 +00:00
}
2013-06-21 23:09:30 +00:00
static GstRange *
gst_mpdparser_clone_range ( GstRange * range )
{
GstRange * clone = NULL ;
if ( range ) {
clone = g_slice_new0 ( GstRange ) ;
2015-07-10 10:26:51 +00:00
clone - > first_byte_pos = range - > first_byte_pos ;
clone - > last_byte_pos = range - > last_byte_pos ;
2013-06-21 23:09:30 +00:00
}
return clone ;
}
static GstURLType *
gst_mpdparser_clone_URL ( GstURLType * url )
{
GstURLType * clone = NULL ;
if ( url ) {
clone = g_slice_new0 ( GstURLType ) ;
2015-07-10 10:26:51 +00:00
if ( url - > sourceURL ) {
clone - > sourceURL = xmlMemStrdup ( url - > sourceURL ) ;
2013-06-21 23:09:30 +00:00
}
2015-07-10 10:26:51 +00:00
clone - > range = gst_mpdparser_clone_range ( url - > range ) ;
2013-06-21 23:09:30 +00:00
}
return clone ;
}
2014-07-31 21:57:40 +00:00
/*
* Combine a base url with the current stream base url from the list of
* baseURLs . Takes ownership of base and returns a new base .
*/
static GstUri *
2015-08-21 13:40:10 +00:00
combine_urls ( GstUri * base , GList * list , gchar * * query , guint idx )
2014-07-31 21:57:40 +00:00
{
GstBaseURL * baseURL ;
GstUri * ret = base ;
if ( list ! = NULL ) {
2015-08-21 13:40:10 +00:00
baseURL = g_list_nth_data ( list , idx ) ;
2014-07-31 21:57:40 +00:00
if ( ! baseURL ) {
baseURL = list - > data ;
}
ret = gst_uri_from_string_with_base ( base , baseURL - > baseURL ) ;
gst_uri_unref ( base ) ;
if ( ret & & query ) {
if ( * query )
g_free ( * query ) ;
* query = gst_uri_get_query_string ( ret ) ;
if ( * query ) {
ret = gst_uri_make_writable ( ret ) ;
gst_uri_set_query_table ( ret , NULL ) ;
}
}
}
return ret ;
}
2013-05-08 14:13:32 +00:00
/* select a stream and extract the baseURL (if present) */
static gchar *
2013-02-13 00:54:32 +00:00
gst_mpdparser_parse_baseURL ( GstMpdClient * client , GstActiveStream * stream ,
gchar * * query )
2013-05-08 14:13:32 +00:00
{
2012-12-20 08:04:28 +00:00
GstStreamPeriod * stream_period ;
2015-01-21 09:17:04 +00:00
static const gchar empty [ ] = " " ;
2013-05-08 14:13:32 +00:00
gchar * ret = NULL ;
2014-07-31 21:57:40 +00:00
GstUri * abs_url ;
2013-05-08 14:13:32 +00:00
2015-01-21 09:17:04 +00:00
g_return_val_if_fail ( stream ! = NULL , g_strdup ( empty ) ) ;
2012-10-22 16:12:30 +00:00
stream_period = gst_mpdparser_get_stream_period ( client ) ;
2015-01-21 09:17:04 +00:00
g_return_val_if_fail ( stream_period ! = NULL , g_strdup ( empty ) ) ;
g_return_val_if_fail ( stream_period - > period ! = NULL , g_strdup ( empty ) ) ;
2013-05-08 14:13:32 +00:00
2014-07-31 21:57:40 +00:00
/* NULLify query return before we start */
if ( query )
* query = NULL ;
2013-01-16 18:58:52 +00:00
2014-07-31 21:57:40 +00:00
/* initialise base url */
abs_url =
gst_uri_from_string ( client - >
mpd_base_uri ? client - > mpd_base_uri : client - > mpd_uri ) ;
2013-01-16 18:58:52 +00:00
2014-07-31 21:57:40 +00:00
/* combine a BaseURL at the MPD level with the current base url */
2015-08-21 13:40:10 +00:00
abs_url =
combine_urls ( abs_url , client - > mpd_node - > BaseURLs , query ,
stream - > baseURL_idx ) ;
2013-05-08 14:13:32 +00:00
2014-07-31 21:57:40 +00:00
/* combine a BaseURL at the Period level with the current base url */
abs_url =
2015-08-21 13:40:10 +00:00
combine_urls ( abs_url , stream_period - > period - > BaseURLs , query ,
stream - > baseURL_idx ) ;
2013-02-19 04:26:25 +00:00
2014-07-31 21:57:40 +00:00
GST_DEBUG ( " Current adaptation set id %i (%s) " , stream - > cur_adapt_set - > id ,
stream - > cur_adapt_set - > contentType ) ;
/* combine a BaseURL at the AdaptationSet level with the current base url */
abs_url =
2015-08-21 13:40:10 +00:00
combine_urls ( abs_url , stream - > cur_adapt_set - > BaseURLs , query ,
stream - > baseURL_idx ) ;
2013-02-13 00:54:32 +00:00
2014-07-31 21:57:40 +00:00
/* combine a BaseURL at the Representation level with the current base url */
abs_url =
combine_urls ( abs_url , stream - > cur_representation - > BaseURLs , query ,
2015-08-21 13:40:10 +00:00
stream - > baseURL_idx ) ;
2013-05-08 14:13:32 +00:00
2014-07-31 21:57:40 +00:00
ret = gst_uri_to_string ( abs_url ) ;
gst_uri_unref ( abs_url ) ;
2013-02-19 04:26:25 +00:00
2013-05-08 14:13:32 +00:00
return ret ;
}
2012-10-24 14:30:01 +00:00
static GstClockTime
2013-01-29 14:22:52 +00:00
gst_mpd_client_get_segment_duration ( GstMpdClient * client ,
2015-10-29 11:38:35 +00:00
GstActiveStream * stream , guint64 * scale_dur )
2012-10-24 14:30:01 +00:00
{
GstStreamPeriod * stream_period ;
GstMultSegmentBaseType * base = NULL ;
2013-09-26 19:13:33 +00:00
GstClockTime duration = 0 ;
2012-10-24 14:30:01 +00:00
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 ;
2015-05-07 19:30:48 +00:00
if ( scale_dur )
* scale_dur = duration ;
2012-10-24 14:30:01 +00:00
} else {
2015-07-20 09:50:44 +00:00
/* duration is guint so this cannot overflow */
2012-10-24 14:30:01 +00:00
duration = base - > duration * GST_SECOND ;
2015-05-07 19:30:48 +00:00
if ( scale_dur )
* scale_dur = duration ;
2015-07-15 12:02:54 +00:00
duration / = base - > SegBaseType - > timescale ;
2012-10-24 14:30:01 +00:00
}
return duration ;
}
2013-05-08 14:13:32 +00:00
/*****************************/
/******* API functions *******/
/*****************************/
2012-12-20 08:04:28 +00:00
GstMpdClient *
2013-01-25 12:36:35 +00:00
gst_mpd_client_new ( void )
2013-05-08 14:13:32 +00:00
{
2012-12-20 08:04:28 +00:00
GstMpdClient * client ;
2013-05-08 14:13:32 +00:00
client = g_new0 ( GstMpdClient , 1 ) ;
return client ;
}
2012-12-20 08:04:28 +00:00
void
gst_active_streams_free ( GstMpdClient * client )
2012-10-24 09:49:51 +00:00
{
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 ;
}
}
2012-12-20 08:04:28 +00:00
void
gst_mpd_client_free ( GstMpdClient * client )
2013-05-08 14:13:32 +00:00
{
g_return_if_fail ( client ! = NULL ) ;
if ( client - > mpd_node )
gst_mpdparser_free_mpd_node ( client - > mpd_node ) ;
2012-10-22 16:12:30 +00:00
if ( client - > periods ) {
2013-06-25 16:26:24 +00:00
g_list_free_full ( client - > periods ,
( GDestroyNotify ) gst_mpdparser_free_stream_period ) ;
2012-10-22 16:12:30 +00:00
}
2012-10-24 09:49:51 +00:00
gst_active_streams_free ( client ) ;
2013-05-08 14:13:32 +00:00
2014-05-28 10:43:43 +00:00
g_free ( client - > mpd_uri ) ;
client - > mpd_uri = NULL ;
g_free ( client - > mpd_base_uri ) ;
client - > mpd_base_uri = NULL ;
2013-05-08 14:13:32 +00:00
2015-08-21 09:06:07 +00:00
if ( client - > downloader )
gst_object_unref ( client - > downloader ) ;
client - > downloader = NULL ;
2013-05-08 14:13:32 +00:00
g_free ( client ) ;
}
2015-08-21 09:06:07 +00:00
void
gst_mpd_client_set_uri_downloader ( GstMpdClient * client ,
GstUriDownloader * downloader )
{
if ( client - > downloader )
gst_object_unref ( client - > downloader ) ;
client - > downloader = gst_object_ref ( downloader ) ;
}
2015-01-09 19:43:03 +00:00
static void
gst_mpd_client_check_profiles ( GstMpdClient * client )
{
2015-09-11 09:57:26 +00:00
GST_DEBUG ( " Profiles: %s " ,
client - > mpd_node - > profiles ? client - > mpd_node - > profiles : " <none> " ) ;
if ( ! client - > mpd_node - > profiles )
return ;
2015-01-09 19:43:03 +00:00
if ( g_strstr_len ( client - > mpd_node - > profiles , - 1 ,
" urn:mpeg:dash:profile:isoff-on-demand:2011 " ) ) {
client - > profile_isoff_ondemand = TRUE ;
GST_DEBUG ( " Found ISOFF on demand profile (2011) " ) ;
}
}
2015-09-21 19:05:03 +00:00
static gboolean
gst_mpd_client_fetch_on_load_external_resources ( GstMpdClient * client )
{
GList * l ;
for ( l = client - > mpd_node - > Periods ; l ; /* explicitly advanced below */ ) {
GstPeriodNode * period = l - > data ;
GList * m ;
if ( period - > xlink_href & & period - > actuate = = GST_XLINK_ACTUATE_ON_LOAD ) {
GList * new_periods , * prev , * next ;
gboolean error ;
new_periods =
gst_mpd_client_fetch_external_period ( client , period , & error ) ;
if ( ! new_periods & & error )
goto syntax_error ;
prev = l - > prev ;
client - > mpd_node - > Periods =
g_list_delete_link ( client - > mpd_node - > Periods , l ) ;
gst_mpdparser_free_period_node ( period ) ;
period = NULL ;
/* Get new next node, we will insert before this */
if ( prev )
next = prev - > next ;
else
next = client - > mpd_node - > Periods ;
while ( new_periods ) {
client - > mpd_node - > Periods =
g_list_insert_before ( client - > mpd_node - > Periods , next ,
new_periods - > data ) ;
new_periods = g_list_delete_link ( new_periods , new_periods ) ;
}
next = NULL ;
/* Update our iterator to the first new period if any, or the next */
if ( prev )
l = prev - > next ;
else
l = client - > mpd_node - > Periods ;
continue ;
}
if ( period - > SegmentList & & period - > SegmentList - > xlink_href
& & period - > SegmentList - > actuate = = GST_XLINK_ACTUATE_ON_LOAD ) {
GstSegmentListNode * new_segment_list ;
gboolean error ;
new_segment_list =
gst_mpd_client_fetch_external_segment_list ( client , period , NULL ,
NULL , NULL , period - > SegmentList , & error ) ;
if ( ! new_segment_list & & error )
goto syntax_error ;
gst_mpdparser_free_segment_list_node ( period - > SegmentList ) ;
period - > SegmentList = new_segment_list ;
}
for ( m = period - > AdaptationSets ; m ; /* explicitly advanced below */ ) {
GstAdaptationSetNode * adapt_set = m - > data ;
GList * n ;
if ( adapt_set - > xlink_href
& & adapt_set - > actuate = = GST_XLINK_ACTUATE_ON_LOAD ) {
GList * new_adapt_sets , * prev , * next ;
gboolean error ;
new_adapt_sets =
gst_mpd_client_fetch_external_adaptation_set ( client , period ,
adapt_set , & error ) ;
if ( ! new_adapt_sets & & error )
goto syntax_error ;
prev = l - > prev ;
period - > AdaptationSets = g_list_delete_link ( period - > AdaptationSets , l ) ;
gst_mpdparser_free_adaptation_set_node ( adapt_set ) ;
adapt_set = NULL ;
/* Get new next node, we will insert before this */
if ( prev )
next = prev - > next ;
else
next = period - > AdaptationSets ;
while ( new_adapt_sets ) {
period - > AdaptationSets =
g_list_insert_before ( period - > AdaptationSets , next ,
new_adapt_sets - > data ) ;
new_adapt_sets = g_list_delete_link ( new_adapt_sets , new_adapt_sets ) ;
}
next = NULL ;
/* Update our iterator to the first new adapt_set if any, or the next */
if ( prev )
l = prev - > next ;
else
l = period - > AdaptationSets ;
continue ;
}
if ( adapt_set - > SegmentList & & adapt_set - > SegmentList - > xlink_href
& & adapt_set - > SegmentList - > actuate = = GST_XLINK_ACTUATE_ON_LOAD ) {
GstSegmentListNode * new_segment_list ;
gboolean error ;
new_segment_list =
gst_mpd_client_fetch_external_segment_list ( client , period ,
adapt_set , NULL , period - > SegmentList , adapt_set - > SegmentList ,
& error ) ;
if ( ! new_segment_list & & error )
goto syntax_error ;
gst_mpdparser_free_segment_list_node ( adapt_set - > SegmentList ) ;
adapt_set - > SegmentList = new_segment_list ;
}
for ( n = adapt_set - > Representations ; n ; n = n - > next ) {
GstRepresentationNode * representation = n - > data ;
if ( representation - > SegmentList
& & representation - > SegmentList - > xlink_href
& & representation - > SegmentList - > actuate = =
GST_XLINK_ACTUATE_ON_LOAD ) {
GstSegmentListNode * new_segment_list ;
gboolean error ;
new_segment_list =
gst_mpd_client_fetch_external_segment_list ( client , period ,
adapt_set , representation , adapt_set - > SegmentList ,
representation - > SegmentList , & error ) ;
if ( ! new_segment_list & & error )
goto syntax_error ;
gst_mpdparser_free_segment_list_node ( representation - > SegmentList ) ;
representation - > SegmentList = new_segment_list ;
}
}
m = m - > next ;
}
l = l - > next ;
}
return TRUE ;
syntax_error :
return FALSE ;
}
2013-05-08 14:13:32 +00:00
gboolean
gst_mpd_parse ( GstMpdClient * client , const gchar * data , gint size )
{
2015-10-28 15:34:29 +00:00
gboolean ret = FALSE ;
2013-05-08 14:13:32 +00:00
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
*/
2015-07-10 10:19:40 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
if ( doc = = NULL ) {
GST_ERROR ( " failed to parse the MPD file " ) ;
2015-10-28 15:34:29 +00:00
ret = FALSE ;
2013-05-08 14:13:32 +00:00
} else {
/* get the root element node */
root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type ! = XML_ELEMENT_NODE
| | xmlStrcmp ( root_element - > name , ( xmlChar * ) " MPD " ) ! = 0 ) {
2012-12-20 08:04:28 +00:00
GST_ERROR
( " can not find the root element MPD, failed to parse the MPD file " ) ;
2015-10-28 15:34:29 +00:00
ret = FALSE ; /* used to return TRUE before, but this seems wrong */
2013-05-08 14:13:32 +00:00
} else {
/* now we can parse the MPD root node and all children nodes, recursively */
2015-10-28 15:34:29 +00:00
ret = gst_mpdparser_parse_root_node ( & client - > mpd_node , root_element ) ;
2013-05-08 14:13:32 +00:00
}
/* free the document */
xmlFreeDoc ( doc ) ;
}
2015-10-28 15:34:29 +00:00
if ( ret ) {
gst_mpd_client_check_profiles ( client ) ;
2015-09-21 19:05:03 +00:00
2015-10-28 15:34:29 +00:00
if ( ! gst_mpd_client_fetch_on_load_external_resources ( client ) )
return FALSE ;
}
2013-05-08 14:13:32 +00:00
}
2015-10-28 15:34:29 +00:00
return ret ;
2013-05-08 14:13:32 +00:00
}
const gchar *
2013-01-16 18:58:52 +00:00
gst_mpdparser_get_baseURL ( GstMpdClient * client , guint indexStream )
2013-05-08 14:13:32 +00:00
{
GstActiveStream * stream ;
g_return_val_if_fail ( client ! = NULL , NULL ) ;
g_return_val_if_fail ( client - > active_streams ! = NULL , NULL ) ;
2013-01-16 18:58:52 +00:00
stream = g_list_nth_data ( client - > active_streams , indexStream ) ;
2013-05-08 14:13:32 +00:00
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
return stream - > baseURL ;
}
2015-09-09 13:49:17 +00:00
static GstClockTime
gst_mpdparser_get_segment_end_time ( GstMpdClient * client , GPtrArray * segments ,
const GstMediaSegment * segment , gint index )
{
const GstStreamPeriod * stream_period ;
GstClockTime end ;
if ( segment - > repeat > = 0 )
return segment - > start + ( segment - > repeat + 1 ) * segment - > duration ;
if ( index < segments - > len - 1 ) {
const GstMediaSegment * next_segment =
g_ptr_array_index ( segments , index + 1 ) ;
end = next_segment - > start ;
} else {
stream_period = gst_mpdparser_get_stream_period ( client ) ;
end = stream_period - > start + stream_period - > duration ;
}
return end ;
}
2015-05-07 19:30:48 +00:00
static gboolean
gst_mpdparser_find_segment_by_index ( GstMpdClient * client ,
GPtrArray * segments , gint index , GstMediaSegment * result )
{
gint i ;
for ( i = 0 ; i < segments - > len ; i + + ) {
GstMediaSegment * s ;
2015-09-09 13:49:17 +00:00
gint repeat ;
2015-05-07 19:30:48 +00:00
s = g_ptr_array_index ( segments , i ) ;
2015-09-09 13:49:17 +00:00
if ( s - > repeat > = 0 ) {
repeat = s - > repeat ;
} else {
GstClockTime start = s - > start ;
GstClockTime end =
gst_mpdparser_get_segment_end_time ( client , segments , s , i ) ;
repeat = ( guint ) ( end - start ) / s - > duration ;
}
if ( s - > number + repeat > = index ) {
2015-05-07 19:30:48 +00:00
/* it is in this segment */
result - > SegmentURL = s - > SegmentURL ;
result - > number = index ;
result - > scale_start =
s - > scale_start + ( index - s - > number ) * s - > scale_duration ;
result - > scale_duration = s - > scale_duration ;
result - > start = s - > start + ( index - s - > number ) * s - > duration ;
result - > duration = s - > duration ;
return TRUE ;
}
}
return FALSE ;
}
2013-07-05 16:22:17 +00:00
gboolean
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_chunk_by_index ( GstMpdClient * client , guint indexStream ,
2013-07-05 16:22:17 +00:00
guint indexChunk , GstMediaSegment * segment )
2013-05-08 14:13:32 +00:00
{
GstActiveStream * stream ;
/* select stream */
2013-07-05 16:22:17 +00:00
g_return_val_if_fail ( client ! = NULL , FALSE ) ;
g_return_val_if_fail ( client - > active_streams ! = NULL , FALSE ) ;
2013-05-08 14:13:32 +00:00
stream = g_list_nth_data ( client - > active_streams , indexStream ) ;
2013-07-05 16:22:17 +00:00
g_return_val_if_fail ( stream ! = NULL , FALSE ) ;
2013-07-05 02:42:23 +00:00
2015-05-07 19:30:48 +00:00
indexChunk + = 1 ;
2013-05-08 14:13:32 +00:00
2015-05-07 19:30:48 +00:00
if ( stream - > segments ) {
return gst_mpdparser_find_segment_by_index ( client , stream - > segments ,
indexChunk , segment ) ;
2013-07-05 16:22:17 +00:00
} else {
GstClockTime duration ;
2013-12-20 22:39:16 +00:00
GstStreamPeriod * stream_period ;
2015-10-29 11:38:35 +00:00
guint64 scale_dur ;
2013-12-20 22:39:16 +00:00
2014-08-26 19:45:46 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - > MultSegBaseType - >
SegmentTimeline = = NULL , FALSE ) ;
2013-07-05 16:22:17 +00:00
/* segment template generator */
2015-05-07 19:30:48 +00:00
duration = gst_mpd_client_get_segment_duration ( client , stream , & scale_dur ) ;
2013-07-05 16:22:17 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) )
return FALSE ;
2013-12-20 22:39:16 +00:00
stream_period = gst_mpdparser_get_stream_period ( client ) ;
2013-07-05 16:22:17 +00:00
2013-08-08 10:19:00 +00:00
segment - > number = indexChunk
+ stream - > cur_seg_template - > MultSegBaseType - > startNumber ;
2015-05-07 19:30:48 +00:00
segment - > scale_start = indexChunk * scale_dur ;
segment - > scale_duration = scale_dur ;
segment - > start = duration * indexChunk ;
2013-07-05 16:22:17 +00:00
segment - > duration = duration ;
segment - > SegmentURL = NULL ;
2013-12-20 22:39:16 +00:00
2015-05-07 19:30:48 +00:00
if ( segment - > start > = stream_period - > duration ) {
2013-12-20 22:39:16 +00:00
return FALSE ;
}
2013-07-05 16:22:17 +00:00
}
return TRUE ;
2013-05-08 14:13:32 +00:00
}
2012-10-19 15:47:51 +00:00
static gboolean
2012-12-20 08:04:28 +00:00
gst_mpd_client_add_media_segment ( GstActiveStream * stream ,
2015-05-07 19:30:48 +00:00
GstSegmentURLNode * url_node , guint number , gint repeat ,
2015-10-29 11:38:35 +00:00
guint64 scale_start , guint64 scale_duration ,
2015-05-07 19:30:48 +00:00
GstClockTime start , GstClockTime duration )
2012-10-19 15:47:51 +00:00
{
GstMediaSegment * media_segment ;
2013-07-05 02:42:23 +00:00
g_return_val_if_fail ( stream - > segments ! = NULL , FALSE ) ;
2012-10-19 15:47:51 +00:00
media_segment = g_slice_new0 ( GstMediaSegment ) ;
2013-07-05 02:42:23 +00:00
2012-10-19 15:47:51 +00:00
media_segment - > SegmentURL = url_node ;
media_segment - > number = number ;
2015-05-07 19:30:48 +00:00
media_segment - > scale_start = scale_start ;
media_segment - > scale_duration = scale_duration ;
2012-10-19 15:47:51 +00:00
media_segment - > start = start ;
media_segment - > duration = duration ;
2015-05-07 19:30:48 +00:00
media_segment - > repeat = repeat ;
2012-10-19 15:47:51 +00:00
2013-07-05 02:42:23 +00:00
g_ptr_array_add ( stream - > segments , media_segment ) ;
2015-05-07 19:30:48 +00:00
GST_LOG ( " Added new segment: number %d, repeat %d, "
" ts: % " GST_TIME_FORMAT " , dur: % "
GST_TIME_FORMAT , number , repeat ,
GST_TIME_ARGS ( start ) , GST_TIME_ARGS ( duration ) ) ;
2013-07-05 02:42:23 +00:00
2012-10-19 15:47:51 +00:00
return TRUE ;
}
2013-05-08 14:13:32 +00:00
gboolean
2012-12-20 08:04:28 +00:00
gst_mpd_client_setup_representation ( GstMpdClient * client ,
GstActiveStream * stream , GstRepresentationNode * representation )
2013-05-08 14:13:32 +00:00
{
2012-10-22 16:12:30 +00:00
GstStreamPeriod * stream_period ;
2013-05-08 14:13:32 +00:00
GList * rep_list ;
2012-10-24 09:49:51 +00:00
GstClockTime PeriodStart , PeriodEnd , start_time , duration ;
2012-10-19 16:45:30 +00:00
GstMediaSegment * last_media_segment ;
2013-02-19 22:35:34 +00:00
guint i ;
guint64 start ;
2013-05-08 14:13:32 +00:00
2012-10-08 09:16:09 +00:00
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 ;
2013-05-08 14:13:32 +00:00
stream - > cur_representation = representation ;
stream - > representation_idx = g_list_index ( rep_list , representation ) ;
/* clean the old segment list, if any */
if ( stream - > segments ) {
2013-07-05 02:42:23 +00:00
g_ptr_array_unref ( stream - > segments ) ;
2013-05-08 14:13:32 +00:00
stream - > segments = NULL ;
}
2012-10-22 16:12:30 +00:00
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 ;
2012-10-24 14:30:01 +00:00
if ( GST_CLOCK_TIME_IS_VALID ( stream_period - > duration ) )
PeriodEnd = stream_period - > start + stream_period - > duration ;
else
PeriodEnd = GST_CLOCK_TIME_NONE ;
2013-05-08 14:13:32 +00:00
GST_LOG ( " Building segment list for Period from % " GST_TIME_FORMAT " to % "
GST_TIME_FORMAT , GST_TIME_ARGS ( PeriodStart ) , GST_TIME_ARGS ( PeriodEnd ) ) ;
2012-12-20 08:04:28 +00:00
if ( representation - > SegmentBase ! = NULL
| | representation - > SegmentList ! = NULL ) {
2013-05-08 14:13:32 +00:00
GList * SegmentURL ;
2013-07-05 16:22:17 +00:00
/* We have a fixed list of segments for any of the cases here,
* init the segments list */
gst_mpdparser_init_active_stream_segments ( stream ) ;
2013-05-08 14:13:32 +00:00
/* get the first segment_base of the selected representation */
if ( ( stream - > cur_segment_base =
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_segment_base ( stream_period - > period ,
stream - > cur_adapt_set , representation ) ) = = NULL ) {
2012-10-18 16:49:53 +00:00
GST_DEBUG ( " No useful SegmentBase node for the current Representation " ) ;
2013-05-08 14:13:32 +00:00
}
/* get the first segment_list of the selected representation */
if ( ( stream - > cur_segment_list =
2015-09-08 10:36:23 +00:00
gst_mpdparser_get_segment_list ( client , stream_period - > period ,
2012-12-20 08:04:28 +00:00
stream - > cur_adapt_set , representation ) ) = = NULL ) {
2012-10-18 16:49:53 +00:00
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 */
2015-05-07 19:30:48 +00:00
if ( ! gst_mpd_client_add_media_segment ( stream , NULL , 1 , 0 , 0 ,
2015-09-03 11:20:00 +00:00
PeriodEnd - PeriodStart , 0 , PeriodEnd - PeriodStart ) ) {
2013-05-08 14:13:32 +00:00
return FALSE ;
}
2012-10-18 16:49:53 +00:00
} else {
/* build the list of GstMediaSegment nodes from the SegmentList node */
SegmentURL = stream - > cur_segment_list - > SegmentURL ;
if ( SegmentURL = = NULL ) {
2012-12-20 08:04:28 +00:00
GST_WARNING
( " No valid list of SegmentURL nodes in the MPD file, aborting... " ) ;
2012-10-18 16:49:53 +00:00
return FALSE ;
}
/* build segment list */
i = stream - > cur_segment_list - > MultSegBaseType - > startNumber ;
2012-10-19 16:45:30 +00:00
start = 0 ;
2015-09-03 11:20:00 +00:00
start_time = 0 ;
2012-10-18 16:49:53 +00:00
2012-10-19 16:45:30 +00:00
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 ;
2013-12-02 20:41:01 +00:00
for ( list = g_queue_peek_head_link ( & timeline - > S ) ; list ;
list = g_list_next ( list ) ) {
2015-05-07 19:30:48 +00:00
guint timescale ;
2012-10-19 16:45:30 +00:00
S = ( GstSNode * ) list - > data ;
2013-04-23 15:08:07 +00:00
GST_LOG ( " Processing S node: d=% " G_GUINT64_FORMAT " r=%d t=% "
G_GUINT64_FORMAT , S - > d , S - > r , S - > t ) ;
2012-12-20 08:04:28 +00:00
timescale =
stream - > cur_segment_list - > MultSegBaseType - > SegBaseType - > timescale ;
2015-07-20 09:50:44 +00:00
duration = gst_util_uint64_scale ( S - > d , GST_SECOND , timescale ) ;
2012-10-19 16:45:30 +00:00
if ( S - > t > 0 ) {
start = S - > t ;
2015-07-20 09:50:44 +00:00
start_time = gst_util_uint64_scale ( S - > t , GST_SECOND , timescale ) ;
2012-10-19 16:45:30 +00:00
}
2015-10-28 17:02:51 +00:00
if ( ! SegmentURL ) {
GST_WARNING
( " SegmentTimeline does not have a matching SegmentURL, aborting... " ) ;
return FALSE ;
}
2015-05-07 19:30:48 +00:00
if ( ! gst_mpd_client_add_media_segment ( stream , SegmentURL - > data , i ,
S - > r , start , S - > d , start_time , duration ) ) {
return FALSE ;
2012-10-19 16:45:30 +00:00
}
2015-05-07 19:30:48 +00:00
i + = S - > r + 1 ;
start_time + = duration * ( S - > r + 1 ) ;
start + = S - > d * ( S - > r + 1 ) ;
SegmentURL = g_list_next ( SegmentURL ) ;
2012-10-19 16:45:30 +00:00
}
} else {
2015-10-29 11:38:35 +00:00
guint64 scale_dur ;
2015-05-07 19:30:48 +00:00
duration =
gst_mpd_client_get_segment_duration ( client , stream , & scale_dur ) ;
2012-10-24 14:30:01 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) )
return FALSE ;
2012-10-19 16:45:30 +00:00
while ( SegmentURL ) {
2015-05-07 19:30:48 +00:00
if ( ! gst_mpd_client_add_media_segment ( stream , SegmentURL - > data , i ,
0 , start , scale_dur , start_time , duration ) ) {
2012-10-19 16:45:30 +00:00
return FALSE ;
}
i + + ;
2015-05-07 19:30:48 +00:00
start + = scale_dur ;
2012-10-19 16:45:30 +00:00
start_time + = duration ;
SegmentURL = g_list_next ( SegmentURL ) ;
2012-10-18 16:49:53 +00:00
}
}
2013-05-08 14:13:32 +00:00
}
} 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 ;
2012-10-22 16:12:30 +00:00
} else if ( stream_period - > period - > SegmentTemplate ! = NULL ) {
stream - > cur_seg_template = stream_period - > period - > SegmentTemplate ;
2013-05-08 14:13:32 +00:00
}
2012-12-20 08:04:28 +00:00
if ( stream - > cur_seg_template = = NULL
| | stream - > cur_seg_template - > MultSegBaseType = = NULL ) {
2013-07-05 16:22:17 +00:00
gst_mpdparser_init_active_stream_segments ( stream ) ;
2012-10-08 14:59:21 +00:00
/* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
2015-07-03 16:00:31 +00:00
if ( ! gst_mpd_client_add_media_segment ( stream , NULL , 1 , 0 , 0 ,
2015-09-03 11:20:00 +00:00
PeriodEnd - PeriodStart , 0 , PeriodEnd - PeriodStart ) ) {
2013-05-08 14:13:32 +00:00
return FALSE ;
}
2012-10-08 14:59:21 +00:00
} else {
2015-03-02 13:00:03 +00:00
GstMultSegmentBaseType * mult_seg =
stream - > cur_seg_template - > MultSegBaseType ;
2012-10-08 14:59:21 +00:00
/* build segment list */
2015-03-02 13:00:03 +00:00
i = mult_seg - > startNumber ;
2012-10-19 15:47:51 +00:00
start = 0 ;
2015-09-03 11:20:00 +00:00
start_time = 0 ;
2012-10-08 14:59:21 +00:00
2012-12-20 08:04:28 +00:00
GST_LOG ( " Building media segment list using this template: %s " ,
stream - > cur_seg_template - > media ) ;
2015-03-02 13:00:03 +00:00
2015-07-15 12:02:54 +00:00
/* Avoid overflows */
stream - > presentationTimeOffset =
gst_util_uint64_scale ( mult_seg - > SegBaseType - > presentationTimeOffset ,
GST_SECOND , mult_seg - > SegBaseType - > timescale ) ;
2015-03-02 13:00:03 +00:00
GST_LOG ( " Setting stream's presentation time offset to % " GST_TIME_FORMAT ,
GST_TIME_ARGS ( stream - > presentationTimeOffset ) ) ;
if ( mult_seg - > SegmentTimeline ) {
2012-10-19 15:47:51 +00:00
GstSegmentTimelineNode * timeline ;
GstSNode * S ;
GList * list ;
2015-03-02 13:00:03 +00:00
timeline = mult_seg - > SegmentTimeline ;
2013-07-05 16:22:17 +00:00
gst_mpdparser_init_active_stream_segments ( stream ) ;
2013-12-02 20:41:01 +00:00
for ( list = g_queue_peek_head_link ( & timeline - > S ) ; list ;
list = g_list_next ( list ) ) {
2015-05-07 19:30:48 +00:00
guint timescale ;
2012-10-19 15:47:51 +00:00
S = ( GstSNode * ) list - > data ;
2013-04-23 15:08:07 +00:00
GST_LOG ( " Processing S node: d=% " G_GUINT64_FORMAT " r=%u t=% "
G_GUINT64_FORMAT , S - > d , S - > r , S - > t ) ;
2015-03-02 13:00:03 +00:00
timescale = mult_seg - > SegBaseType - > timescale ;
2015-07-20 09:50:44 +00:00
duration = gst_util_uint64_scale ( S - > d , GST_SECOND , timescale ) ;
2012-10-19 15:47:51 +00:00
if ( S - > t > 0 ) {
start = S - > t ;
2015-07-20 09:50:44 +00:00
start_time = gst_util_uint64_scale ( S - > t , GST_SECOND , timescale ) ;
2012-10-19 15:47:51 +00:00
}
2015-05-07 19:30:48 +00:00
if ( ! gst_mpd_client_add_media_segment ( stream , NULL , i , S - > r , start ,
S - > d , start_time , duration ) ) {
return FALSE ;
2012-10-19 15:47:51 +00:00
}
2015-05-07 19:30:48 +00:00
i + = S - > r + 1 ;
start + = S - > d * ( S - > r + 1 ) ;
start_time + = duration * ( S - > r + 1 ) ;
2012-10-19 15:47:51 +00:00
}
} else {
2013-07-05 16:22:17 +00:00
/* NOP - The segment is created on demand with the template, no need
* to build a list */
2012-10-08 14:59:21 +00:00
}
2013-05-08 14:13:32 +00:00
}
}
2012-10-19 16:45:30 +00:00
/* check duration of last segment */
2013-07-05 16:22:17 +00:00
last_media_segment = ( stream - > segments & & stream - > segments - > len ) ?
2013-07-05 02:42:23 +00:00
g_ptr_array_index ( stream - > segments , stream - > segments - > len - 1 ) : NULL ;
2012-10-24 14:30:01 +00:00
if ( last_media_segment & & GST_CLOCK_TIME_IS_VALID ( PeriodEnd ) ) {
2015-05-07 19:30:48 +00:00
if ( last_media_segment - > start + last_media_segment - > duration > PeriodEnd ) {
2015-09-03 11:20:00 +00:00
last_media_segment - > duration =
PeriodEnd - PeriodStart - last_media_segment - > start ;
2012-12-20 08:04:28 +00:00
GST_LOG ( " Fixed duration of last segment: % " GST_TIME_FORMAT ,
GST_TIME_ARGS ( last_media_segment - > duration ) ) ;
2013-05-08 14:13:32 +00:00
}
2012-10-19 16:45:30 +00:00
GST_LOG ( " Built a list of %d segments " , last_media_segment - > number ) ;
2013-05-08 14:13:32 +00:00
}
g_free ( stream - > baseURL ) ;
2013-02-13 00:54:32 +00:00
g_free ( stream - > queryURL ) ;
stream - > baseURL =
gst_mpdparser_parse_baseURL ( client , stream , & stream - > queryURL ) ;
2013-05-08 14:13:32 +00:00
return TRUE ;
}
2015-08-21 13:40:10 +00:00
static GList *
gst_mpd_client_fetch_external_period ( GstMpdClient * client ,
GstPeriodNode * period_node , gboolean * error )
{
GstFragment * download ;
GstBuffer * period_buffer ;
GstMapInfo map ;
GError * err = NULL ;
xmlDocPtr doc ;
GstUri * base_uri , * uri ;
gchar * query = NULL ;
gchar * uri_string ;
GList * new_periods = NULL ;
* error = FALSE ;
/* ISO/IEC 23009-1:2014 5.5.3 4)
* Remove nodes that resolve to nothing when resolving
*/
if ( strcmp ( period_node - > xlink_href ,
" urn:mpeg:dash:resolve-to-zero:2013 " ) = = 0 ) {
return NULL ;
}
if ( ! client - > downloader ) {
* error = TRUE ;
return NULL ;
}
/* Build absolute URI */
/* Get base URI at the MPD level */
base_uri =
gst_uri_from_string ( client - >
mpd_base_uri ? client - > mpd_base_uri : client - > mpd_uri ) ;
/* combine a BaseURL at the MPD level with the current base url */
base_uri = combine_urls ( base_uri , client - > mpd_node - > BaseURLs , & query , 0 ) ;
uri = gst_uri_from_string_with_base ( base_uri , period_node - > xlink_href ) ;
if ( query )
gst_uri_set_query_string ( uri , query ) ;
g_free ( query ) ;
uri_string = gst_uri_to_string ( uri ) ;
gst_uri_unref ( base_uri ) ;
gst_uri_unref ( uri ) ;
download =
gst_uri_downloader_fetch_uri ( client - > downloader ,
uri_string , client - > mpd_uri , TRUE , FALSE , TRUE , & err ) ;
g_free ( uri_string ) ;
if ( ! download ) {
GST_ERROR ( " Failed to download external Period node at '%s': %s " ,
period_node - > xlink_href , err - > message ) ;
g_clear_error ( & err ) ;
* error = TRUE ;
return NULL ;
}
period_buffer = gst_fragment_get_buffer ( download ) ;
g_object_unref ( download ) ;
gst_buffer_map ( period_buffer , & map , GST_MAP_READ ) ;
doc =
xmlReadMemory ( ( const gchar * ) map . data , map . size , " noname.xml " , NULL ,
XML_PARSE_NONET ) ;
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type ! = XML_ELEMENT_NODE | |
xmlStrcmp ( root_element - > name , ( xmlChar * ) " Period " ) ! = 0 ) {
xmlFreeDoc ( doc ) ;
gst_buffer_unmap ( period_buffer , & map ) ;
gst_buffer_unref ( period_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_mpdparser_parse_period_node ( & new_periods , root_element ) ;
} else {
GST_ERROR ( " Failed to parse period node XML " ) ;
gst_buffer_unmap ( period_buffer , & map ) ;
gst_buffer_unref ( period_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_buffer_unmap ( period_buffer , & map ) ;
gst_buffer_unref ( period_buffer ) ;
return new_periods ;
}
2012-10-22 16:12:30 +00:00
gboolean
2015-09-22 14:17:38 +00:00
gst_mpd_client_setup_media_presentation ( GstMpdClient * client ,
GstClockTime time , gint period_idx , const gchar * period_id )
2012-10-22 16:12:30 +00:00
{
GstStreamPeriod * stream_period ;
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 ) ;
2015-09-22 14:17:38 +00:00
/* Check if we set up the media presentation far enough already */
for ( list = client - > periods ; list ; list = list - > next ) {
GstStreamPeriod * stream_period = list - > data ;
if ( ( time ! = GST_CLOCK_TIME_NONE
& & stream_period - > duration ! = GST_CLOCK_TIME_NONE
& & stream_period - > start + stream_period - > duration > = time )
| | ( time ! = GST_CLOCK_TIME_NONE & & stream_period - > start > = time ) )
return TRUE ;
if ( period_idx ! = - 1 & & stream_period - > number > = period_idx )
return TRUE ;
if ( period_id ! = NULL & & stream_period - > period - > id ! = NULL
& & strcmp ( stream_period - > period - > id , period_id ) = = 0 )
return TRUE ;
}
2012-10-22 16:12:30 +00:00
GST_DEBUG ( " Building the list of Periods in the Media Presentation " ) ;
/* clean the old period list, if any */
2015-09-22 14:17:38 +00:00
/* TODO: In theory we could reuse the ones we have so far but that
* seems more complicated than the overhead caused here
*/
2012-10-22 16:12:30 +00:00
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 ;
2015-10-28 16:31:39 +00:00
if ( client - > mpd_node - > mediaPresentationDuration < = 0 & &
client - > mpd_node - > mediaPresentationDuration ! = - 1 ) {
/* Invalid MPD file: MPD duration is negative or zero */
goto syntax_error ;
}
2015-08-21 13:40:10 +00:00
for ( list = client - > mpd_node - > Periods ; list ; /* explicitly advanced below */ ) {
GstPeriodNode * period_node = list - > data ;
GstPeriodNode * next_period_node = NULL ;
/* Download external period */
if ( period_node - > xlink_href ) {
GList * new_periods ;
gboolean error = FALSE ;
GList * prev ;
new_periods =
gst_mpd_client_fetch_external_period ( client , period_node , & error ) ;
if ( ! new_periods & & error )
goto syntax_error ;
prev = list - > prev ;
client - > mpd_node - > Periods =
g_list_delete_link ( client - > mpd_node - > Periods , list ) ;
gst_mpdparser_free_period_node ( period_node ) ;
period_node = NULL ;
/* Get new next node, we will insert before this */
if ( prev )
next = prev - > next ;
else
next = client - > mpd_node - > Periods ;
while ( new_periods ) {
client - > mpd_node - > Periods =
g_list_insert_before ( client - > mpd_node - > Periods , next ,
new_periods - > data ) ;
new_periods = g_list_delete_link ( new_periods , new_periods ) ;
}
next = NULL ;
/* Update our iterator to the first new period if any, or the next */
if ( prev )
list = prev - > next ;
else
list = client - > mpd_node - > Periods ;
/* And try again */
continue ;
}
2012-10-22 16:12:30 +00:00
if ( period_node - > start ! = - 1 ) {
/* we have a regular period */
2015-06-15 11:59:55 +00:00
/* start cannot be smaller than previous start */
if ( list ! = g_list_first ( client - > mpd_node - > Periods )
& & start > = period_node - > start * GST_MSECOND ) {
/* Invalid MPD file: duration would be negative or zero */
goto syntax_error ;
}
2012-10-22 16:12:30 +00:00
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 ;
2013-02-20 15:49:25 +00:00
} else if ( client - > mpd_node - > type = = GST_MPD_FILE_TYPE_DYNAMIC ) {
/* this should be a live stream, let this pass */
2012-10-22 16:12:30 +00:00
} else {
/* this is an 'Early Available Period' */
goto early ;
}
2015-06-15 11:59:55 +00:00
/* compute duration.
If there is a start time for the next period , or this is the last period
and mediaPresentationDuration was set , those values will take precedence
over a configured period duration in computing this period ' s duration
ISO / IEC 23009 - 1 : 2014 ( E ) , chapter 5.3 .2 .1
" The Period extends until the PeriodStart of the next Period, or until
the end of the Media Presentation in the case of the last Period . "
*/
2015-08-21 13:40:10 +00:00
while ( ( next = g_list_next ( list ) ) ! = NULL ) {
2012-10-22 16:12:30 +00:00
/* try to infer this period duration from the start time of the next period */
2015-08-21 13:40:10 +00:00
next_period_node = next - > data ;
if ( next_period_node - > xlink_href ) {
gboolean next_error ;
GList * new_periods ;
new_periods =
gst_mpd_client_fetch_external_period ( client , next_period_node ,
& next_error ) ;
if ( ! new_periods & & next_error )
goto syntax_error ;
client - > mpd_node - > Periods =
g_list_delete_link ( client - > mpd_node - > Periods , next ) ;
gst_mpdparser_free_period_node ( next_period_node ) ;
next_period_node = NULL ;
/* Get new next node, we will insert before this */
next = g_list_next ( list ) ;
while ( new_periods ) {
client - > mpd_node - > Periods =
g_list_insert_before ( client - > mpd_node - > Periods , next ,
new_periods - > data ) ;
new_periods = g_list_delete_link ( new_periods , new_periods ) ;
}
/* And try again, getting the next list element which is now our newly
* inserted nodes . If any */
} else {
/* Got the next period and it doesn't have to be downloaded first */
break ;
}
}
if ( next_period_node ) {
2012-10-22 16:12:30 +00:00
if ( next_period_node - > start ! = - 1 ) {
2015-06-15 11:59:55 +00:00
if ( start > = next_period_node - > start * GST_MSECOND ) {
/* Invalid MPD file: duration would be negative or zero */
goto syntax_error ;
}
2012-10-22 16:12:30 +00:00
duration = next_period_node - > start * GST_MSECOND - start ;
2015-06-15 11:59:55 +00:00
} else if ( period_node - > duration ! = - 1 ) {
2015-10-28 15:39:07 +00:00
if ( period_node - > duration < = 0 ) {
/* Invalid MPD file: duration would be negative or zero */
goto syntax_error ;
}
2015-06-15 11:59:55 +00:00
duration = period_node - > duration * GST_MSECOND ;
2013-02-19 22:35:34 +00:00
} else if ( client - > mpd_node - > type = = GST_MPD_FILE_TYPE_DYNAMIC ) {
/* might be a live file, ignore unspecified duration */
2012-10-22 16:12:30 +00:00
} else {
/* Invalid MPD file! */
goto syntax_error ;
}
} else if ( client - > mpd_node - > mediaPresentationDuration ! = - 1 ) {
/* last Period of the Media Presentation */
2015-06-15 11:59:55 +00:00
if ( client - > mpd_node - > mediaPresentationDuration * GST_MSECOND < = start ) {
/* Invalid MPD file: duration would be negative or zero */
goto syntax_error ;
}
2012-12-20 08:04:28 +00:00
duration =
client - > mpd_node - > mediaPresentationDuration * GST_MSECOND - start ;
2015-06-15 11:59:55 +00:00
} else if ( period_node - > duration ! = - 1 ) {
duration = period_node - > duration * GST_MSECOND ;
2013-02-19 22:35:34 +00:00
} else if ( client - > mpd_node - > type = = GST_MPD_FILE_TYPE_DYNAMIC ) {
/* might be a live file, ignore unspecified duration */
2012-10-22 16:12:30 +00:00
} else {
/* Invalid MPD file! */
goto syntax_error ;
}
stream_period = g_slice_new0 ( GstStreamPeriod ) ;
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 ) ) ;
2015-08-21 13:40:10 +00:00
2015-09-22 14:17:38 +00:00
if ( ( time ! = GST_CLOCK_TIME_NONE
& & stream_period - > duration ! = GST_CLOCK_TIME_NONE
& & stream_period - > start + stream_period - > duration > = time )
| | ( time ! = GST_CLOCK_TIME_NONE & & stream_period - > start > = time ) )
break ;
if ( period_idx ! = - 1 & & stream_period - > number > = period_idx )
break ;
if ( period_id ! = NULL & & stream_period - > period - > id ! = NULL
& & strcmp ( stream_period - > period - > id , period_id ) = = 0 )
break ;
2015-08-21 13:40:10 +00:00
list = list - > next ;
2012-10-22 16:12:30 +00:00
}
2015-09-22 14:17:38 +00:00
GST_DEBUG
( " Found a total of %d valid Periods in the Media Presentation up to this point " ,
2012-12-20 08:04:28 +00:00
idx ) ;
2012-10-22 16:12:30 +00:00
return ret ;
early :
2012-12-20 08:04:28 +00:00
GST_WARNING
( " Found an Early Available Period, skipping the rest of the Media Presentation " ) ;
2012-10-22 16:12:30 +00:00
return ret ;
syntax_error :
2012-12-20 08:04:28 +00:00
GST_WARNING
( " Cannot get the duration of the Period %d, skipping the rest of the Media Presentation " ,
idx ) ;
2012-10-22 16:12:30 +00:00
return ret ;
}
2015-09-08 10:04:11 +00:00
static GList *
gst_mpd_client_fetch_external_adaptation_set ( GstMpdClient * client ,
GstPeriodNode * period , GstAdaptationSetNode * adapt_set , gboolean * error )
{
GstFragment * download ;
GstBuffer * adapt_set_buffer ;
GstMapInfo map ;
GError * err = NULL ;
xmlDocPtr doc ;
GstUri * base_uri , * uri ;
gchar * query = NULL ;
gchar * uri_string ;
GList * new_adapt_sets = NULL ;
* error = FALSE ;
/* ISO/IEC 23009-1:2014 5.5.3 4)
* Remove nodes that resolve to nothing when resolving
*/
if ( strcmp ( adapt_set - > xlink_href , " urn:mpeg:dash:resolve-to-zero:2013 " ) = = 0 ) {
return NULL ;
}
if ( ! client - > downloader ) {
* error = TRUE ;
return NULL ;
}
/* Build absolute URI */
/* Get base URI at the MPD level */
base_uri =
gst_uri_from_string ( client - >
mpd_base_uri ? client - > mpd_base_uri : client - > mpd_uri ) ;
/* combine a BaseURL at the MPD level with the current base url */
base_uri = combine_urls ( base_uri , client - > mpd_node - > BaseURLs , & query , 0 ) ;
/* combine a BaseURL at the Period level with the current base url */
base_uri = combine_urls ( base_uri , period - > BaseURLs , & query , 0 ) ;
uri = gst_uri_from_string_with_base ( base_uri , adapt_set - > xlink_href ) ;
if ( query )
gst_uri_set_query_string ( uri , query ) ;
g_free ( query ) ;
uri_string = gst_uri_to_string ( uri ) ;
gst_uri_unref ( base_uri ) ;
gst_uri_unref ( uri ) ;
download =
gst_uri_downloader_fetch_uri ( client - > downloader ,
uri_string , client - > mpd_uri , TRUE , FALSE , TRUE , & err ) ;
g_free ( uri_string ) ;
if ( ! download ) {
GST_ERROR ( " Failed to download external AdaptationSet node at '%s': %s " ,
adapt_set - > xlink_href , err - > message ) ;
g_clear_error ( & err ) ;
* error = TRUE ;
return NULL ;
}
adapt_set_buffer = gst_fragment_get_buffer ( download ) ;
g_object_unref ( download ) ;
gst_buffer_map ( adapt_set_buffer , & map , GST_MAP_READ ) ;
doc =
xmlReadMemory ( ( const gchar * ) map . data , map . size , " noname.xml " , NULL ,
XML_PARSE_NONET ) ;
if ( doc ) {
xmlNode * root_element = xmlDocGetRootElement ( doc ) ;
if ( root_element - > type ! = XML_ELEMENT_NODE | |
xmlStrcmp ( root_element - > name , ( xmlChar * ) " AdaptationSet " ) ! = 0 ) {
xmlFreeDoc ( doc ) ;
gst_buffer_unmap ( adapt_set_buffer , & map ) ;
gst_buffer_unref ( adapt_set_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_mpdparser_parse_adaptation_set_node ( & new_adapt_sets , root_element ,
period ) ;
} else {
GST_ERROR ( " Failed to parse adaptation set node XML " ) ;
gst_buffer_unmap ( adapt_set_buffer , & map ) ;
gst_buffer_unref ( adapt_set_buffer ) ;
* error = TRUE ;
return NULL ;
}
gst_buffer_unmap ( adapt_set_buffer , & map ) ;
gst_buffer_unref ( adapt_set_buffer ) ;
return new_adapt_sets ;
}
2013-12-02 20:31:41 +00:00
static GList *
gst_mpd_client_get_adaptation_sets_for_period ( GstMpdClient * client ,
GstStreamPeriod * period )
{
2015-09-08 10:04:11 +00:00
GList * list ;
2013-12-02 20:31:41 +00:00
g_return_val_if_fail ( period ! = NULL , NULL ) ;
2015-09-08 10:04:11 +00:00
/* Resolve all external adaptation sets of this period. Every user of
* the adaptation sets would need to know the content of all adaptation sets
* to decide which one to use , so we have to resolve them all here
*/
for ( list = period - > period - > AdaptationSets ; list ;
/* advanced explicitely below */ ) {
GstAdaptationSetNode * adapt_set = ( GstAdaptationSetNode * ) list - > data ;
GList * new_adapt_sets = NULL , * prev , * next ;
gboolean error ;
if ( ! adapt_set - > xlink_href ) {
list = list - > next ;
continue ;
}
new_adapt_sets =
gst_mpd_client_fetch_external_adaptation_set ( client , period - > period ,
adapt_set , & error ) ;
prev = list - > prev ;
period - > period - > AdaptationSets =
g_list_delete_link ( period - > period - > AdaptationSets , list ) ;
gst_mpdparser_free_adaptation_set_node ( adapt_set ) ;
adapt_set = NULL ;
/* Get new next node, we will insert before this */
if ( prev )
next = prev - > next ;
else
next = period - > period - > AdaptationSets ;
while ( new_adapt_sets ) {
period - > period - > AdaptationSets =
g_list_insert_before ( period - > period - > AdaptationSets , next ,
new_adapt_sets - > data ) ;
new_adapt_sets = g_list_delete_link ( new_adapt_sets , new_adapt_sets ) ;
}
/* Update our iterator to the first new adaptation set if any, or the next */
if ( prev )
list = prev - > next ;
else
list = period - > period - > AdaptationSets ;
}
2013-12-02 20:31:41 +00:00
return period - > period - > AdaptationSets ;
}
GList *
gst_mpd_client_get_adaptation_sets ( GstMpdClient * client )
2013-05-08 14:13:32 +00:00
{
2012-10-22 16:12:30 +00:00
GstStreamPeriod * stream_period ;
2013-05-08 14:13:32 +00:00
2012-10-22 16:12:30 +00:00
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... " ) ;
2013-12-02 20:31:41 +00:00
return NULL ;
2013-05-08 14:13:32 +00:00
}
2013-12-02 20:31:41 +00:00
return gst_mpd_client_get_adaptation_sets_for_period ( client , stream_period ) ;
}
gboolean
gst_mpd_client_setup_streaming ( GstMpdClient * client ,
GstAdaptationSetNode * adapt_set )
{
GstRepresentationNode * representation ;
GList * rep_list = NULL ;
GstActiveStream * stream ;
rep_list = adapt_set - > Representations ;
if ( ! rep_list ) {
GST_WARNING ( " Can not retrieve any representation, aborting... " ) ;
return FALSE ;
2013-05-08 14:13:32 +00:00
}
stream = g_slice_new0 ( GstActiveStream ) ;
2013-07-05 02:42:23 +00:00
gst_mpdparser_init_active_stream_segments ( stream ) ;
2013-05-08 14:13:32 +00:00
stream - > baseURL_idx = 0 ;
stream - > cur_adapt_set = adapt_set ;
2013-01-25 12:36:35 +00:00
GST_DEBUG ( " 0. Current stream %p " , stream ) ;
2013-01-16 18:58:52 +00:00
2015-07-03 15:10:20 +00:00
/* retrieve representation list */
2013-05-08 14:13:32 +00:00
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 ) {
2012-12-20 08:04:28 +00:00
GST_WARNING
( " Can not retrieve a representation with the requested bandwidth " ) ;
representation = gst_mpdparser_get_lowest_representation ( rep_list ) ;
2013-05-08 14:13:32 +00:00
}
# else
/* slow start */
2012-12-20 08:04:28 +00:00
representation = gst_mpdparser_get_lowest_representation ( rep_list ) ;
2013-05-08 14:13:32 +00:00
# endif
if ( ! representation ) {
GST_WARNING ( " No valid representation in the MPD file, aborting... " ) ;
2014-05-13 11:52:48 +00:00
g_slice_free ( GstActiveStream , stream ) ;
2013-05-08 14:13:32 +00:00
return FALSE ;
}
2013-12-02 20:31:41 +00:00
stream - > mimeType =
gst_mpdparser_representation_get_mimetype ( adapt_set , representation ) ;
if ( stream - > mimeType = = GST_STREAM_UNKNOWN ) {
2015-06-18 15:20:26 +00:00
GST_WARNING ( " Unknown mime type in the representation, aborting... " ) ;
2013-12-02 20:31:41 +00:00
g_slice_free ( GstActiveStream , stream ) ;
return FALSE ;
}
2013-05-08 14:13:32 +00:00
2013-12-02 20:31:41 +00:00
client - > active_streams = g_list_append ( client - > active_streams , stream ) ;
2015-06-18 15:20:26 +00:00
if ( ! gst_mpd_client_setup_representation ( client , stream , representation ) ) {
GST_WARNING ( " Failed to setup the representation, aborting... " ) ;
2013-05-08 14:13:32 +00:00
return FALSE ;
2015-06-18 15:20:26 +00:00
}
2013-05-08 14:13:32 +00:00
2012-12-20 08:04:28 +00:00
GST_INFO ( " Successfully setup the download pipeline for mimeType %d " ,
2013-12-02 20:31:41 +00:00
stream - > mimeType ) ;
2013-05-08 14:13:32 +00:00
return TRUE ;
}
2013-02-22 19:40:36 +00:00
gboolean
gst_mpd_client_stream_seek ( GstMpdClient * client , GstActiveStream * stream ,
GstClockTime ts )
{
2014-01-09 12:13:48 +00:00
gint index = 0 ;
2015-05-08 19:58:36 +00:00
gint repeat_index = 0 ;
2013-02-22 19:40:36 +00:00
GstMediaSegment * selectedChunk = NULL ;
2015-09-09 13:49:17 +00:00
gboolean in_segment ;
2013-02-22 19:40:36 +00:00
g_return_val_if_fail ( stream ! = NULL , 0 ) ;
2013-07-05 16:22:17 +00:00
if ( stream - > segments ) {
2014-01-09 12:13:48 +00:00
for ( index = 0 ; index < stream - > segments - > len ; index + + ) {
GstMediaSegment * segment = g_ptr_array_index ( stream - > segments , index ) ;
2014-01-09 12:11:23 +00:00
2015-05-11 15:30:03 +00:00
GST_DEBUG ( " Looking at fragment sequence chunk %d / %d " , index ,
stream - > segments - > len ) ;
2015-09-09 13:49:17 +00:00
in_segment = FALSE ;
if ( segment - > start < = ts ) {
if ( segment - > repeat > = 0 ) {
in_segment =
ts < segment - > start + ( segment - > repeat + 1 ) * segment - > duration ;
} else {
GstClockTime end =
gst_mpdparser_get_segment_end_time ( client , stream - > segments ,
segment , index ) ;
in_segment = ts < end ;
}
if ( in_segment ) {
selectedChunk = segment ;
repeat_index = ( ts - segment - > start ) / segment - > duration ;
break ;
}
2013-07-05 16:22:17 +00:00
}
2013-02-22 19:40:36 +00:00
}
2013-07-05 16:22:17 +00:00
if ( selectedChunk = = NULL ) {
2015-05-11 15:30:03 +00:00
stream - > segment_index = stream - > segments - > len ;
stream - > segment_repeat_index = 0 ;
GST_DEBUG ( " Seek to after last segment " ) ;
2013-07-05 16:22:17 +00:00
return FALSE ;
}
} else {
GstClockTime duration =
2015-05-07 19:30:48 +00:00
gst_mpd_client_get_segment_duration ( client , stream , NULL ) ;
2015-06-25 18:19:34 +00:00
GstStreamPeriod * stream_period = gst_mpdparser_get_stream_period ( client ) ;
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2014-08-26 19:45:46 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - >
MultSegBaseType - > SegmentTimeline = = NULL , FALSE ) ;
2013-07-05 16:22:17 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) ) {
return FALSE ;
}
2015-06-25 18:19:34 +00:00
if ( ts > stream_period - > start )
ts - = stream_period - > start ;
else
ts = 0 ;
2014-01-09 12:13:48 +00:00
index = ts / duration ;
2015-06-05 12:10:43 +00:00
if ( segments_count > 0 & & index > = segments_count ) {
stream - > segment_index = segments_count ;
stream - > segment_repeat_index = 0 ;
GST_DEBUG ( " Seek to after last segment " ) ;
return FALSE ;
}
2013-02-22 19:40:36 +00:00
}
2015-05-08 19:58:36 +00:00
stream - > segment_repeat_index = repeat_index ;
stream - > segment_index = index ;
2013-02-22 19:40:36 +00:00
return TRUE ;
}
2013-09-26 19:13:33 +00:00
gint64
2013-06-11 13:28:53 +00:00
gst_mpd_client_calculate_time_difference ( const GstDateTime * t1 ,
const GstDateTime * t2 )
{
GDateTime * gdt1 , * gdt2 ;
GTimeSpan diff ;
g_assert ( t1 ! = NULL & & t2 ! = NULL ) ;
gdt1 = gst_date_time_to_g_date_time ( ( GstDateTime * ) t1 ) ;
gdt2 = gst_date_time_to_g_date_time ( ( GstDateTime * ) t2 ) ;
diff = g_date_time_difference ( gdt2 , gdt1 ) ;
g_date_time_unref ( gdt1 ) ;
g_date_time_unref ( gdt2 ) ;
return diff * GST_USECOND ;
}
GstDateTime *
gst_mpd_client_add_time_difference ( GstDateTime * t1 , gint64 usecs )
{
GDateTime * gdt ;
GDateTime * gdt2 ;
GstDateTime * rv ;
g_assert ( t1 ! = NULL ) ;
gdt = gst_date_time_to_g_date_time ( t1 ) ;
g_assert ( gdt ! = NULL ) ;
gdt2 = g_date_time_add ( gdt , usecs ) ;
g_assert ( gdt2 ! = NULL ) ;
g_date_time_unref ( gdt ) ;
rv = gst_date_time_new_from_g_date_time ( gdt2 ) ;
/* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
* ownership of the GDateTime pointer .
*/
return rv ;
}
2013-09-26 19:13:33 +00:00
static GstDateTime *
2013-06-11 13:28:53 +00:00
gst_mpd_client_get_availability_start_time ( GstMpdClient * client )
{
2013-09-26 19:13:33 +00:00
GstDateTime * start_time ;
2013-06-11 13:28:53 +00:00
2013-09-26 19:13:33 +00:00
if ( client = = NULL )
return ( GstDateTime * ) NULL ;
2013-06-11 13:28:53 +00:00
2013-09-26 19:13:33 +00:00
start_time = client - > mpd_node - > availabilityStartTime ;
2014-12-29 13:44:53 +00:00
if ( start_time )
gst_date_time_ref ( start_time ) ;
2013-09-26 19:13:33 +00:00
return start_time ;
2013-06-11 13:28:53 +00:00
}
2013-02-22 19:40:36 +00:00
gboolean
2015-05-11 17:19:20 +00:00
gst_mpd_client_get_last_fragment_timestamp_end ( GstMpdClient * client ,
2013-02-22 19:40:36 +00:00
guint stream_idx , GstClockTime * ts )
{
GstActiveStream * stream ;
gint segment_idx ;
2015-05-08 19:31:44 +00:00
GstMediaSegment * currentChunk ;
2015-09-09 13:49:17 +00:00
GstStreamPeriod * stream_period ;
2013-02-22 19:40:36 +00:00
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 ) ;
2015-06-05 12:10:43 +00:00
segment_idx = gst_mpd_client_get_segments_counts ( client , stream ) - 1 ;
2015-05-08 19:31:44 +00:00
currentChunk = g_ptr_array_index ( stream - > segments , segment_idx ) ;
2013-02-22 19:40:36 +00:00
2015-09-09 13:49:17 +00:00
if ( currentChunk - > repeat > = 0 ) {
* ts =
currentChunk - > start + ( currentChunk - > duration * ( 1 +
currentChunk - > repeat ) ) ;
} else {
/* 5.3.9.6.1: negative repeat means repeat till the end of the
* period , or the next update of the MPD ( which I think is
* implicit , as this will all get deleted / recreated ) , or the
* start of the next segment , if any . */
stream_period = gst_mpdparser_get_stream_period ( client ) ;
* ts = stream_period - > start + stream_period - > duration ;
}
2013-02-22 19:40:36 +00:00
return TRUE ;
}
2013-02-01 05:10:15 +00:00
gboolean
gst_mpd_client_get_next_fragment_timestamp ( GstMpdClient * client ,
guint stream_idx , GstClockTime * ts )
{
GstActiveStream * stream ;
2015-05-08 19:31:44 +00:00
GstMediaSegment * currentChunk ;
2013-02-01 05:10:15 +00:00
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 ) ;
2015-05-20 11:08:38 +00:00
if ( stream - > segments ) {
GST_DEBUG ( " Looking for fragment sequence chunk %d / %d " ,
stream - > segment_index , stream - > segments - > len ) ;
if ( stream - > segment_index > = stream - > segments - > len )
return FALSE ;
currentChunk = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
2013-02-01 05:10:15 +00:00
2015-05-20 11:08:38 +00:00
* ts =
currentChunk - > start +
( currentChunk - > duration * stream - > segment_repeat_index ) ;
} else {
GstClockTime duration =
gst_mpd_client_get_segment_duration ( client , stream , NULL ) ;
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2015-05-20 11:08:38 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - >
MultSegBaseType - > SegmentTimeline = = NULL , FALSE ) ;
2015-06-05 12:10:43 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) | | ( segments_count > 0
& & stream - > segment_index > = segments_count ) ) {
2015-05-20 11:08:38 +00:00
return FALSE ;
}
2015-09-01 10:12:45 +00:00
* ts = stream - > segment_index * duration ;
2015-05-20 11:08:38 +00:00
}
2013-02-01 05:10:15 +00:00
return TRUE ;
}
2015-03-02 13:00:03 +00:00
GstClockTime
gst_mpd_parser_get_stream_presentation_offset ( GstMpdClient * client ,
guint stream_idx )
{
GstActiveStream * stream = NULL ;
2015-09-02 15:33:51 +00:00
g_return_val_if_fail ( client ! = NULL , 0 ) ;
g_return_val_if_fail ( client - > active_streams ! = NULL , 0 ) ;
2015-03-02 13:00:03 +00:00
stream = g_list_nth_data ( client - > active_streams , stream_idx ) ;
2015-09-02 15:33:51 +00:00
g_return_val_if_fail ( stream ! = NULL , 0 ) ;
2015-03-02 13:00:03 +00:00
2015-09-01 10:13:58 +00:00
return stream - > presentationTimeOffset ;
2015-03-02 13:00:03 +00:00
}
2015-09-02 15:33:51 +00:00
GstClockTime
gst_mpd_parser_get_period_start_time ( GstMpdClient * client )
{
GstStreamPeriod * stream_period = NULL ;
g_return_val_if_fail ( client ! = NULL , 0 ) ;
stream_period = gst_mpdparser_get_stream_period ( client ) ;
g_return_val_if_fail ( stream_period ! = NULL , 0 ) ;
return stream_period - > start ;
}
2015-07-15 10:56:13 +00:00
/**
* gst_mpd_client_get_utc_timing_sources :
* @ client : # GstMpdClient to check for UTCTiming elements
* @ methods : A bit mask of # GstMPDUTCTimingType that specifies the methods
* to search for .
* @ selected_method : ( nullable ) : The selected method
* Returns : ( transfer none ) : A NULL terminated array of URLs of servers
* that use @ selected_method to provide a realtime clock .
*
* Searches the UTCTiming elements found in the manifest for an element
* that uses one of the UTC timing methods specified in @ selected_method .
* If multiple UTCTiming elements are present that support one of the
* methods specified in @ selected_method , the first one is returned .
*
* Since : 1.6
*/
gchar * *
gst_mpd_client_get_utc_timing_sources ( GstMpdClient * client ,
guint methods , GstMPDUTCTimingType * selected_method )
{
GList * list ;
g_return_val_if_fail ( client ! = NULL , NULL ) ;
g_return_val_if_fail ( client - > mpd_node ! = NULL , NULL ) ;
for ( list = g_list_first ( client - > mpd_node - > UTCTiming ) ; list ;
list = g_list_next ( list ) ) {
const GstUTCTimingNode * node = ( const GstUTCTimingNode * ) list - > data ;
if ( node - > method & methods ) {
if ( selected_method ) {
* selected_method = node - > method ;
}
return node - > urls ;
}
}
return NULL ;
}
2013-05-08 14:13:32 +00:00
gboolean
gst_mpd_client_get_next_fragment ( GstMpdClient * client ,
2014-08-26 19:45:46 +00:00
guint indexStream , GstMediaFragmentInfo * fragment )
2013-05-08 14:13:32 +00:00
{
GstActiveStream * stream = NULL ;
2015-05-08 19:31:44 +00:00
GstMediaSegment * currentChunk ;
2013-05-08 14:13:32 +00:00
gchar * mediaURL = NULL ;
2013-07-01 20:50:37 +00:00
gchar * indexURL = NULL ;
2014-07-31 21:57:40 +00:00
GstUri * base_url , * frag_url ;
2013-05-08 14:13:32 +00:00
/* 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 ) ;
2015-05-20 11:08:38 +00:00
if ( stream - > segments ) {
GST_DEBUG ( " Looking for fragment sequence chunk %d / %d " ,
stream - > segment_index , stream - > segments - > len ) ;
if ( stream - > segment_index > = stream - > segments - > len )
return FALSE ;
} else {
GstClockTime duration = gst_mpd_client_get_segment_duration ( client ,
stream , NULL ) ;
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2015-05-20 11:08:38 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - >
MultSegBaseType - > SegmentTimeline = = NULL , FALSE ) ;
2015-06-05 12:10:43 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) | | ( segments_count > 0
& & stream - > segment_index > = segments_count ) ) {
2015-05-20 11:08:38 +00:00
return FALSE ;
2013-07-01 20:50:37 +00:00
}
2015-05-20 11:08:38 +00:00
fragment - > duration = duration ;
2013-05-08 14:13:32 +00:00
}
2013-07-01 20:50:37 +00:00
2015-05-08 19:31:44 +00:00
/* FIXME rework discont checking */
/* fragment->discontinuity = segment_idx != currentChunk.number; */
2013-07-01 20:50:37 +00:00
fragment - > range_start = 0 ;
fragment - > range_end = - 1 ;
fragment - > index_uri = NULL ;
fragment - > index_range_start = 0 ;
fragment - > index_range_end = - 1 ;
2015-05-20 11:08:38 +00:00
if ( stream - > segments ) {
currentChunk = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
GST_DEBUG ( " currentChunk->SegmentURL = %p " , currentChunk - > SegmentURL ) ;
if ( currentChunk - > SegmentURL ! = NULL ) {
mediaURL =
g_strdup ( gst_mpdparser_get_mediaURL ( stream ,
currentChunk - > SegmentURL ) ) ;
2015-07-08 20:14:13 +00:00
indexURL = g_strdup ( currentChunk - > SegmentURL - > index ) ;
2015-05-20 11:08:38 +00:00
} 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 - > segment_repeat_index ,
stream - > cur_representation - > bandwidth ,
currentChunk - > scale_start +
stream - > segment_repeat_index * currentChunk - > scale_duration ) ;
if ( stream - > cur_seg_template - > index ) {
indexURL =
gst_mpdparser_build_URL_from_template ( stream - >
cur_seg_template - > index , stream - > cur_representation - > id ,
currentChunk - > number + stream - > segment_repeat_index ,
stream - > cur_representation - > bandwidth ,
currentChunk - > scale_start +
stream - > segment_repeat_index * currentChunk - > scale_duration ) ;
}
}
GST_DEBUG ( " mediaURL = %s " , mediaURL ) ;
GST_DEBUG ( " indexURL = %s " , indexURL ) ;
fragment - > timestamp =
currentChunk - > start +
stream - > segment_repeat_index * currentChunk - > duration ;
fragment - > duration = currentChunk - > duration ;
if ( currentChunk - > SegmentURL ) {
if ( currentChunk - > SegmentURL - > mediaRange ) {
fragment - > range_start =
currentChunk - > SegmentURL - > mediaRange - > first_byte_pos ;
fragment - > range_end =
currentChunk - > SegmentURL - > mediaRange - > last_byte_pos ;
}
if ( currentChunk - > SegmentURL - > indexRange ) {
fragment - > index_range_start =
currentChunk - > SegmentURL - > indexRange - > first_byte_pos ;
fragment - > index_range_end =
currentChunk - > SegmentURL - > indexRange - > last_byte_pos ;
}
2013-07-01 20:50:37 +00:00
}
2015-05-20 11:08:38 +00:00
} else {
if ( stream - > cur_seg_template ! = NULL ) {
mediaURL =
gst_mpdparser_build_URL_from_template ( stream - >
cur_seg_template - > media , stream - > cur_representation - > id ,
2015-06-03 18:03:37 +00:00
stream - > segment_index +
stream - > cur_seg_template - > MultSegBaseType - > startNumber ,
stream - > cur_representation - > bandwidth ,
2015-05-20 11:08:38 +00:00
stream - > segment_index * fragment - > duration ) ;
if ( stream - > cur_seg_template - > index ) {
indexURL =
gst_mpdparser_build_URL_from_template ( stream - >
cur_seg_template - > index , stream - > cur_representation - > id ,
2015-06-03 18:03:37 +00:00
stream - > segment_index +
stream - > cur_seg_template - > MultSegBaseType - > startNumber ,
stream - > cur_representation - > bandwidth ,
2015-05-20 11:08:38 +00:00
stream - > segment_index * fragment - > duration ) ;
}
} else {
return FALSE ;
2013-07-01 20:50:37 +00:00
}
2015-05-20 11:08:38 +00:00
GST_DEBUG ( " mediaURL = %s " , mediaURL ) ;
GST_DEBUG ( " indexURL = %s " , indexURL ) ;
2015-09-01 10:12:45 +00:00
fragment - > timestamp = stream - > segment_index * fragment - > duration ;
2013-06-20 06:52:31 +00:00
}
2014-07-31 21:57:40 +00:00
base_url = gst_uri_from_string ( stream - > baseURL ) ;
frag_url = gst_uri_from_string_with_base ( base_url , mediaURL ) ;
2015-07-08 20:14:13 +00:00
g_free ( mediaURL ) ;
2014-07-31 21:57:40 +00:00
if ( stream - > queryURL ) {
frag_url = gst_uri_make_writable ( frag_url ) ;
gst_uri_set_query_string ( frag_url , stream - > queryURL ) ;
2013-05-08 14:13:32 +00:00
}
2014-07-31 21:57:40 +00:00
fragment - > uri = gst_uri_to_string ( frag_url ) ;
gst_uri_unref ( frag_url ) ;
2013-07-01 20:50:37 +00:00
if ( indexURL ! = NULL ) {
2014-07-31 21:57:40 +00:00
frag_url = gst_uri_make_writable ( gst_uri_from_string_with_base ( base_url ,
indexURL ) ) ;
gst_uri_set_query_string ( frag_url , stream - > queryURL ) ;
fragment - > index_uri = gst_uri_to_string ( frag_url ) ;
gst_uri_unref ( frag_url ) ;
2015-07-08 20:14:13 +00:00
g_free ( indexURL ) ;
} else if ( indexURL = = NULL & & ( fragment - > index_range_start
2014-07-31 21:57:40 +00:00
| | fragment - > index_range_end ! = - 1 ) ) {
2013-07-01 20:50:37 +00:00
/* index has no specific URL but has a range, we should only use this if
* the media also has a range , otherwise we are serving some data twice
* ( in the media fragment and again in the index ) */
if ( ! ( fragment - > range_start | | fragment - > range_end ! = - 1 ) ) {
GST_WARNING ( " Ignoring index ranges because there isn't a media range "
" and URIs would be the same " ) ;
/* removing index information */
fragment - > index_range_start = 0 ;
fragment - > index_range_end = - 1 ;
}
}
2015-07-08 20:14:13 +00:00
gst_uri_unref ( base_url ) ;
2013-07-01 20:50:37 +00:00
GST_DEBUG ( " Loading chunk with URL %s " , fragment - > uri ) ;
2013-05-08 14:13:32 +00:00
return TRUE ;
}
2015-05-12 16:41:19 +00:00
gboolean
gst_mpd_client_has_next_segment ( GstMpdClient * client ,
GstActiveStream * stream , gboolean forward )
{
if ( forward ) {
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2015-09-22 12:43:44 +00:00
if ( segments_count > 0 & & stream - > segments
& & stream - > segment_index + 1 = = segments_count ) {
2015-09-14 17:03:10 +00:00
GstMediaSegment * segment ;
segment = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
if ( segment - > repeat > = 0
& & stream - > segment_repeat_index > = segment - > repeat )
return FALSE ;
} else if ( segments_count > 0
& & stream - > segment_index + 1 > = segments_count ) {
2015-05-12 16:41:19 +00:00
return FALSE ;
2015-09-14 17:03:10 +00:00
}
2015-05-12 16:41:19 +00:00
} else {
if ( stream - > segment_index < 0 )
return FALSE ;
}
return TRUE ;
}
2015-05-08 19:31:44 +00:00
GstFlowReturn
gst_mpd_client_advance_segment ( GstMpdClient * client , GstActiveStream * stream ,
gboolean forward )
2014-08-26 19:45:46 +00:00
{
2015-05-08 19:31:44 +00:00
GstMediaSegment * segment ;
2015-06-04 11:13:39 +00:00
GstFlowReturn ret = GST_FLOW_OK ;
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2014-08-26 19:45:46 +00:00
2015-05-11 15:30:03 +00:00
GST_DEBUG ( " Advancing segment. Current: %d / %d r:%d " , stream - > segment_index ,
2015-05-20 11:08:38 +00:00
segments_count , stream - > segment_repeat_index ) ;
2015-05-11 15:30:03 +00:00
/* handle special cases first */
if ( forward ) {
2015-06-05 12:10:43 +00:00
if ( segments_count > 0 & & stream - > segment_index > = segments_count ) {
ret = GST_FLOW_EOS ;
goto done ;
}
2015-05-20 11:08:38 +00:00
if ( stream - > segments = = NULL ) {
2015-07-08 13:31:48 +00:00
if ( stream - > segment_index < 0 ) {
2015-05-20 11:08:38 +00:00
stream - > segment_index = 0 ;
2015-07-08 13:31:48 +00:00
} else {
2015-05-20 11:08:38 +00:00
stream - > segment_index + + ;
2015-07-08 13:31:48 +00:00
if ( segments_count > 0 & & stream - > segment_index > = segments_count ) {
ret = GST_FLOW_EOS ;
}
}
2015-06-04 11:13:39 +00:00
goto done ;
2015-05-20 11:08:38 +00:00
}
2015-05-11 15:30:03 +00:00
/* special case for when playback direction is reverted right at *
* the end of the segment list */
if ( stream - > segment_index < 0 ) {
stream - > segment_index = 0 ;
2015-06-04 11:13:39 +00:00
goto done ;
2015-05-11 15:30:03 +00:00
}
} else {
2015-05-20 11:08:38 +00:00
if ( stream - > segments = = NULL )
stream - > segment_index - - ;
if ( stream - > segment_index < 0 ) {
stream - > segment_index = - 1 ;
2015-06-04 11:13:39 +00:00
ret = GST_FLOW_EOS ;
goto done ;
2015-05-20 11:08:38 +00:00
}
if ( stream - > segments = = NULL )
2015-06-04 11:13:39 +00:00
goto done ;
2015-05-11 15:30:03 +00:00
/* special case for when playback direction is reverted right at *
* the end of the segment list */
2015-05-20 11:08:38 +00:00
if ( stream - > segment_index > = segments_count ) {
stream - > segment_index = segments_count - 1 ;
2015-05-11 15:30:03 +00:00
segment = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
2015-09-09 13:49:17 +00:00
if ( segment - > repeat > = 0 ) {
stream - > segment_repeat_index = segment - > repeat ;
} else {
GstClockTime start = segment - > start ;
GstClockTime end =
gst_mpdparser_get_segment_end_time ( client , stream - > segments ,
segment ,
stream - > segment_index ) ;
stream - > segment_repeat_index =
( guint ) ( end - start ) / segment - > duration ;
}
2015-06-04 11:13:39 +00:00
goto done ;
2015-05-11 15:30:03 +00:00
}
}
/* for the normal cases we can get the segment safely here */
2015-05-08 19:31:44 +00:00
segment = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
if ( forward ) {
2015-09-09 13:49:17 +00:00
if ( segment - > repeat > = 0 & & stream - > segment_repeat_index > = segment - > repeat ) {
2015-05-08 19:31:44 +00:00
stream - > segment_repeat_index = 0 ;
stream - > segment_index + + ;
2015-07-08 13:31:48 +00:00
if ( segments_count > 0 & & stream - > segment_index > = segments_count ) {
ret = GST_FLOW_EOS ;
goto done ;
}
2015-05-08 19:31:44 +00:00
} else {
stream - > segment_repeat_index + + ;
}
} else {
if ( stream - > segment_repeat_index = = 0 ) {
stream - > segment_index - - ;
2015-05-11 15:30:03 +00:00
if ( stream - > segment_index < 0 ) {
2015-07-08 13:31:48 +00:00
ret = GST_FLOW_EOS ;
2015-05-11 15:30:03 +00:00
goto done ;
}
2014-08-26 19:45:46 +00:00
2015-05-08 19:31:44 +00:00
segment = g_ptr_array_index ( stream - > segments , stream - > segment_index ) ;
2015-09-09 13:49:17 +00:00
/* negative repeats only seem to make sense at the end of a list,
* so this one will probably not be . Needs some sanity checking
* when loading the XML data . */
if ( segment - > repeat > = 0 ) {
stream - > segment_repeat_index = segment - > repeat ;
} else {
GstClockTime start = segment - > start ;
GstClockTime end =
gst_mpdparser_get_segment_end_time ( client , stream - > segments ,
segment ,
stream - > segment_index ) ;
stream - > segment_repeat_index =
( guint ) ( end - start ) / segment - > duration ;
}
2015-05-08 19:31:44 +00:00
} else {
stream - > segment_repeat_index - - ;
}
}
2015-05-11 15:30:03 +00:00
done :
2015-06-04 11:13:39 +00:00
GST_DEBUG ( " Advanced to segment: %d / %d r:%d (ret: %s) " ,
2015-06-05 12:10:43 +00:00
stream - > segment_index , segments_count ,
2015-06-04 11:13:39 +00:00
stream - > segment_repeat_index , gst_flow_get_name ( ret ) ) ;
return ret ;
2014-08-26 19:45:46 +00:00
}
2013-05-08 14:13:32 +00:00
gboolean
2013-02-18 17:02:48 +00:00
gst_mpd_client_get_next_header ( GstMpdClient * client , gchar * * uri ,
2013-06-20 06:52:31 +00:00
guint stream_idx , gint64 * range_start , gint64 * range_end )
2013-05-08 14:13:32 +00:00
{
2012-10-22 16:12:30 +00:00
GstActiveStream * stream ;
GstStreamPeriod * stream_period ;
2013-05-08 14:13:32 +00:00
2012-10-22 16:12:30 +00:00
stream = gst_mpdparser_get_active_stream_by_index ( client , stream_idx ) ;
2013-05-08 14:13:32 +00:00
g_return_val_if_fail ( stream ! = NULL , FALSE ) ;
g_return_val_if_fail ( stream - > cur_representation ! = NULL , FALSE ) ;
2012-10-22 16:12:30 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
2013-06-20 06:52:31 +00:00
* range_start = 0 ;
* range_end = - 1 ;
2013-05-08 14:13:32 +00:00
GST_DEBUG ( " Looking for current representation header " ) ;
* uri = NULL ;
2015-09-16 21:12:54 +00:00
if ( stream - > cur_segment_base ) {
if ( stream - > cur_segment_base - > Initialization ) {
* uri =
g_strdup ( gst_mpdparser_get_initializationURL ( stream ,
stream - > cur_segment_base - > Initialization ) ) ;
if ( stream - > cur_segment_base - > Initialization - > range ) {
* range_start =
stream - > cur_segment_base - > Initialization - > range - > first_byte_pos ;
* range_end =
stream - > cur_segment_base - > Initialization - > range - > last_byte_pos ;
}
} else if ( stream - > cur_segment_base - > indexRange ) {
* uri =
g_strdup ( gst_mpdparser_get_initializationURL ( stream ,
stream - > cur_segment_base - > Initialization ) ) ;
* range_start = 0 ;
* range_end = stream - > cur_segment_base - > indexRange - > first_byte_pos - 1 ;
2013-06-20 06:52:31 +00:00
}
2015-11-02 11:17:29 +00:00
} else if ( stream - > cur_seg_template
& & stream - > cur_seg_template - > initialization ) {
* uri =
gst_mpdparser_build_URL_from_template ( stream - >
cur_seg_template - > initialization , stream - > cur_representation - > id , 0 ,
stream - > cur_representation - > bandwidth , 0 ) ;
2013-05-08 14:13:32 +00:00
}
return * uri = = NULL ? FALSE : TRUE ;
}
2013-07-01 16:19:15 +00:00
gboolean
gst_mpd_client_get_next_header_index ( GstMpdClient * client , gchar * * uri ,
guint stream_idx , gint64 * range_start , gint64 * range_end )
{
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 ) ;
* range_start = 0 ;
* range_end = - 1 ;
GST_DEBUG ( " Looking for current representation index " ) ;
* uri = NULL ;
if ( stream - > cur_segment_base & & stream - > cur_segment_base - > indexRange ) {
* uri =
2013-07-01 20:50:37 +00:00
g_strdup ( gst_mpdparser_get_initializationURL ( stream ,
stream - > cur_segment_base - > Initialization ) ) ;
* range_start = stream - > cur_segment_base - > indexRange - > first_byte_pos ;
* range_end = stream - > cur_segment_base - > indexRange - > last_byte_pos ;
2015-11-02 11:17:29 +00:00
} else if ( stream - > cur_seg_template & & stream - > cur_seg_template - > index ) {
* uri =
gst_mpdparser_build_URL_from_template ( stream - > cur_seg_template - > index ,
stream - > cur_representation - > id , 0 ,
stream - > cur_representation - > bandwidth , 0 ) ;
2013-07-01 16:19:15 +00:00
}
return * uri = = NULL ? FALSE : TRUE ;
}
2012-10-24 14:30:01 +00:00
GstClockTime
2013-06-11 13:28:53 +00:00
gst_mpd_client_get_next_fragment_duration ( GstMpdClient * client ,
GstActiveStream * stream )
2012-10-24 14:30:01 +00:00
{
2013-07-05 02:42:23 +00:00
GstMediaSegment * media_segment = NULL ;
2015-05-19 01:33:50 +00:00
gint seg_idx ;
2013-05-08 14:13:32 +00:00
2012-10-24 14:30:01 +00:00
g_return_val_if_fail ( stream ! = NULL , 0 ) ;
2015-05-08 19:58:36 +00:00
seg_idx = stream - > segment_index ;
2013-07-05 02:42:23 +00:00
2013-07-05 16:22:17 +00:00
if ( stream - > segments ) {
2015-05-11 15:30:03 +00:00
if ( seg_idx < stream - > segments - > len & & seg_idx > = 0 )
2013-07-05 16:22:17 +00:00
media_segment = g_ptr_array_index ( stream - > segments , seg_idx ) ;
return media_segment = = NULL ? 0 : media_segment - > duration ;
} else {
GstClockTime duration =
2015-05-07 19:30:48 +00:00
gst_mpd_client_get_segment_duration ( client , stream , NULL ) ;
2015-06-05 12:10:43 +00:00
guint segments_count = gst_mpd_client_get_segments_counts ( client , stream ) ;
2014-08-26 19:45:46 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - > MultSegBaseType - >
SegmentTimeline = = NULL , 0 ) ;
2013-05-08 14:13:32 +00:00
2015-06-26 12:09:54 +00:00
if ( ! GST_CLOCK_TIME_IS_VALID ( duration ) | | ( segments_count > 0
& & seg_idx > = segments_count ) ) {
return 0 ;
}
return duration ;
2013-07-05 16:22:17 +00:00
}
2013-05-08 14:13:32 +00:00
}
GstClockTime
2012-10-24 14:30:01 +00:00
gst_mpd_client_get_media_presentation_duration ( GstMpdClient * client )
2013-05-08 14:13:32 +00:00
{
GstClockTime duration ;
g_return_val_if_fail ( client ! = NULL , GST_CLOCK_TIME_NONE ) ;
2012-10-19 17:03:41 +00:00
if ( client - > mpd_node - > mediaPresentationDuration ! = - 1 ) {
duration = client - > mpd_node - > mediaPresentationDuration * GST_MSECOND ;
} else {
/* We can only get the duration for on-demand streams */
2013-05-08 14:13:32 +00:00
duration = GST_CLOCK_TIME_NONE ;
}
return duration ;
}
2013-02-22 19:40:36 +00:00
gboolean
gst_mpd_client_set_period_id ( GstMpdClient * client , const gchar * period_id )
{
GstStreamPeriod * next_stream_period ;
gboolean ret = FALSE ;
GList * iter ;
2014-12-10 09:24:17 +00:00
guint period_idx ;
2013-02-22 19:40:36 +00:00
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 ) ;
2015-09-22 14:17:38 +00:00
if ( ! gst_mpd_client_setup_media_presentation ( client , GST_CLOCK_TIME_NONE , - 1 ,
period_id ) )
return FALSE ;
2014-12-10 09:24:17 +00:00
for ( period_idx = 0 , iter = client - > periods ; iter ;
2014-12-29 13:44:19 +00:00
period_idx + + , iter = g_list_next ( iter ) ) {
2013-02-22 19:40:36 +00:00
next_stream_period = iter - > data ;
if ( next_stream_period - > period - > id
& & strcmp ( next_stream_period - > period - > id , period_id ) = = 0 ) {
ret = TRUE ;
2014-12-10 09:24:17 +00:00
client - > period_idx = period_idx ;
2013-02-22 19:40:36 +00:00
break ;
}
}
return ret ;
}
2012-10-24 09:49:51 +00:00
gboolean
2012-12-20 08:04:28 +00:00
gst_mpd_client_set_period_index ( GstMpdClient * client , guint period_idx )
2012-10-24 09:49:51 +00:00
{
GstStreamPeriod * next_stream_period ;
2012-12-17 14:19:33 +00:00
gboolean ret = FALSE ;
2012-10-24 09:49:51 +00:00
g_return_val_if_fail ( client ! = NULL , FALSE ) ;
g_return_val_if_fail ( client - > periods ! = NULL , FALSE ) ;
2015-09-22 14:17:38 +00:00
if ( ! gst_mpd_client_setup_media_presentation ( client , - 1 , period_idx , NULL ) )
return FALSE ;
2012-12-17 14:00:52 +00:00
next_stream_period = g_list_nth_data ( client - > periods , period_idx ) ;
2012-12-17 14:19:33 +00:00
if ( next_stream_period ! = NULL ) {
client - > period_idx = period_idx ;
ret = TRUE ;
}
2012-10-24 09:49:51 +00:00
2012-12-17 14:19:33 +00:00
return ret ;
2012-10-24 09:49:51 +00:00
}
2012-12-17 14:04:45 +00:00
guint
2012-12-20 08:04:28 +00:00
gst_mpd_client_get_period_index ( GstMpdClient * client )
2012-12-17 14:04:45 +00:00
{
2012-12-17 14:19:33 +00:00
guint period_idx ;
g_return_val_if_fail ( client ! = NULL , 0 ) ;
period_idx = client - > period_idx ;
return period_idx ;
2012-12-17 14:04:45 +00:00
}
2013-02-22 19:40:36 +00:00
const gchar *
gst_mpd_client_get_period_id ( GstMpdClient * client )
{
GstStreamPeriod * period ;
gchar * period_id = NULL ;
g_return_val_if_fail ( client ! = NULL , 0 ) ;
period = g_list_nth_data ( client - > periods , client - > period_idx ) ;
if ( period & & period - > period )
period_id = period - > period - > id ;
return period_id ;
}
2014-08-26 19:45:46 +00:00
gboolean
gst_mpd_client_has_previous_period ( GstMpdClient * client )
{
GList * next_stream_period ;
g_return_val_if_fail ( client ! = NULL , FALSE ) ;
g_return_val_if_fail ( client - > periods ! = NULL , FALSE ) ;
2015-10-07 13:22:46 +00:00
if ( ! gst_mpd_client_setup_media_presentation ( client , GST_CLOCK_TIME_NONE ,
client - > period_idx - 1 , NULL ) )
return FALSE ;
2014-08-26 19:45:46 +00:00
next_stream_period =
g_list_nth_data ( client - > periods , client - > period_idx - 1 ) ;
return next_stream_period ! = NULL ;
}
2013-01-29 18:58:50 +00:00
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 ) ;
2015-10-07 13:22:46 +00:00
if ( ! gst_mpd_client_setup_media_presentation ( client , GST_CLOCK_TIME_NONE ,
client - > period_idx + 1 , NULL ) )
return FALSE ;
2013-01-29 18:58:50 +00:00
next_stream_period =
g_list_nth_data ( client - > periods , client - > period_idx + 1 ) ;
return next_stream_period ! = NULL ;
}
2015-05-08 19:31:44 +00:00
void
gst_mpd_client_seek_to_first_segment ( GstMpdClient * client )
{
GList * list ;
g_return_if_fail ( client ! = NULL ) ;
g_return_if_fail ( client - > active_streams ! = NULL ) ;
for ( list = g_list_first ( client - > active_streams ) ; list ;
list = g_list_next ( list ) ) {
GstActiveStream * stream = ( GstActiveStream * ) list - > data ;
if ( stream ) {
stream - > segment_index = 0 ;
stream - > segment_repeat_index = 0 ;
}
}
2012-12-17 14:04:45 +00:00
}
2013-02-22 19:40:36 +00:00
static guint
2015-06-05 12:10:43 +00:00
gst_mpd_client_get_segments_counts ( GstMpdClient * client ,
GstActiveStream * stream )
2013-02-22 19:40:36 +00:00
{
2015-06-05 12:10:43 +00:00
GstStreamPeriod * stream_period ;
2013-02-22 19:40:36 +00:00
g_return_val_if_fail ( stream ! = NULL , 0 ) ;
2013-07-05 16:22:17 +00:00
if ( stream - > segments )
return stream - > segments - > len ;
2014-08-26 19:45:46 +00:00
g_return_val_if_fail ( stream - > cur_seg_template - > MultSegBaseType - >
SegmentTimeline = = NULL , 0 ) ;
2015-06-05 12:10:43 +00:00
stream_period = gst_mpdparser_get_stream_period ( client ) ;
if ( stream_period - > duration ! = - 1 )
return gst_util_uint64_scale_ceil ( stream_period - > duration , 1 ,
gst_mpd_client_get_segment_duration ( client , stream , NULL ) ) ;
2013-07-05 16:22:17 +00:00
return 0 ;
2013-02-22 19:40:36 +00:00
}
2013-05-08 14:13:32 +00:00
gboolean
gst_mpd_client_is_live ( GstMpdClient * client )
{
2012-10-22 16:12:30 +00:00
g_return_val_if_fail ( client ! = NULL , FALSE ) ;
g_return_val_if_fail ( client - > mpd_node ! = NULL , FALSE ) ;
2015-06-05 11:28:39 +00:00
return client - > mpd_node - > type = = GST_MPD_FILE_TYPE_DYNAMIC ;
2013-05-08 14:13:32 +00:00
}
2012-10-22 16:12:30 +00:00
guint
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_nb_active_stream ( GstMpdClient * client )
2012-12-17 14:19:33 +00:00
{
2012-10-22 16:12:30 +00:00
g_return_val_if_fail ( client ! = NULL , 0 ) ;
2013-05-08 14:13:32 +00:00
2012-10-22 16:12:30 +00:00
return g_list_length ( client - > active_streams ) ;
2013-05-08 14:13:32 +00:00
}
2012-10-22 16:12:30 +00:00
guint
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_nb_adaptationSet ( GstMpdClient * client )
2012-10-08 15:43:14 +00:00
{
2012-10-22 16:12:30 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
}
2012-10-22 16:12:30 +00:00
GstActiveStream *
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_active_stream_by_index ( GstMpdClient * client ,
guint stream_idx )
2012-10-08 15:43:14 +00:00
{
2012-10-22 16:12:30 +00:00
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 ) ;
2013-05-08 14:13:32 +00:00
}
2015-08-18 13:16:11 +00:00
gboolean
gst_mpd_client_active_stream_contains_subtitles ( GstActiveStream * stream )
{
const gchar * mimeType ;
const gchar * adapt_set_codecs ;
const gchar * rep_codecs ;
mimeType = stream - > cur_representation - > RepresentationBase - > mimeType ;
if ( ! mimeType )
mimeType = stream - > cur_adapt_set - > RepresentationBase - > mimeType ;
if ( g_strcmp0 ( mimeType , " application/ttml+xml " ) = = 0 )
return TRUE ;
adapt_set_codecs = stream - > cur_adapt_set - > RepresentationBase - > codecs ;
rep_codecs = stream - > cur_representation - > RepresentationBase - > codecs ;
return ( adapt_set_codecs & & g_str_has_prefix ( adapt_set_codecs , " stpp " ) )
| | ( rep_codecs & & g_str_has_prefix ( rep_codecs , " stpp " ) ) ;
}
2012-10-19 18:12:09 +00:00
static const gchar *
gst_mpdparser_mimetype_to_caps ( const gchar * mimeType )
2012-10-08 15:43:14 +00:00
{
2012-10-19 18:12:09 +00:00
if ( mimeType = = NULL )
return NULL ;
if ( strcmp ( mimeType , " video/mp2t " ) = = 0 ) {
2013-05-16 02:24:29 +00:00
return " video/mpegts, systemstream=(bool) true " ;
2012-10-19 18:12:09 +00:00
} else if ( strcmp ( mimeType , " video/mp4 " ) = = 0 ) {
return " video/quicktime " ;
} else if ( strcmp ( mimeType , " audio/mp4 " ) = = 0 ) {
return " audio/x-m4a " ;
} else
return mimeType ;
}
2015-08-18 13:16:11 +00:00
GstCaps *
gst_mpd_client_get_stream_caps ( GstActiveStream * stream )
2012-10-19 18:12:09 +00:00
{
2015-08-18 13:16:11 +00:00
const gchar * mimeType , * caps_string ;
GstCaps * ret = NULL ;
2012-10-19 18:12:09 +00:00
2012-12-20 08:04:28 +00:00
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL
| | stream - > cur_representation = = NULL )
2012-10-19 18:12:09 +00:00
return NULL ;
mimeType = stream - > cur_representation - > RepresentationBase - > mimeType ;
if ( mimeType = = NULL ) {
mimeType = stream - > cur_adapt_set - > RepresentationBase - > mimeType ;
}
2015-08-18 13:16:11 +00:00
caps_string = gst_mpdparser_mimetype_to_caps ( mimeType ) ;
if ( ( g_strcmp0 ( caps_string , " application/mp4 " ) = = 0 )
& & gst_mpd_client_active_stream_contains_subtitles ( stream ) )
caps_string = " video/quicktime " ;
if ( caps_string )
ret = gst_caps_from_string ( caps_string ) ;
return ret ;
2012-10-19 18:12:09 +00:00
}
2013-08-30 19:36:19 +00:00
gboolean
2012-12-20 08:04:28 +00:00
gst_mpd_client_get_bitstream_switching_flag ( GstActiveStream * stream )
2012-12-17 14:39:10 +00:00
{
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL )
return FALSE ;
return stream - > cur_adapt_set - > bitstreamSwitching ;
}
2012-12-20 08:04:28 +00:00
guint
gst_mpd_client_get_video_stream_width ( GstActiveStream * stream )
2012-10-19 18:12:09 +00:00
{
guint width ;
2012-12-20 08:04:28 +00:00
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL
| | stream - > cur_representation = = NULL )
2012-10-19 18:12:09 +00:00
return 0 ;
width = stream - > cur_representation - > RepresentationBase - > width ;
if ( width = = 0 ) {
width = stream - > cur_adapt_set - > RepresentationBase - > width ;
}
return width ;
2013-05-08 14:13:32 +00:00
}
2012-09-29 01:13:29 +00:00
2012-12-20 08:04:28 +00:00
guint
gst_mpd_client_get_video_stream_height ( GstActiveStream * stream )
2012-10-08 15:43:14 +00:00
{
2012-10-19 18:12:09 +00:00
guint height ;
2012-12-20 08:04:28 +00:00
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL
| | stream - > cur_representation = = NULL )
2012-10-19 18:12:09 +00:00
return 0 ;
height = stream - > cur_representation - > RepresentationBase - > height ;
if ( height = = 0 ) {
height = stream - > cur_adapt_set - > RepresentationBase - > height ;
}
return height ;
2013-05-08 14:13:32 +00:00
}
2012-12-20 08:04:28 +00:00
guint
gst_mpd_client_get_audio_stream_rate ( GstActiveStream * stream )
2012-10-08 15:43:14 +00:00
{
2012-10-19 18:12:09 +00:00
const gchar * rate ;
2012-12-20 08:04:28 +00:00
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL
| | stream - > cur_representation = = NULL )
2012-10-19 18:12:09 +00:00
return 0 ;
rate = stream - > cur_representation - > RepresentationBase - > audioSamplingRate ;
if ( rate = = NULL ) {
rate = stream - > cur_adapt_set - > RepresentationBase - > audioSamplingRate ;
}
return rate ? atoi ( rate ) : 0 ;
2013-05-08 14:13:32 +00:00
}
2012-09-29 01:13:29 +00:00
2012-12-20 08:04:28 +00:00
guint
gst_mpd_client_get_audio_stream_num_channels ( GstActiveStream * stream )
2012-10-08 15:43:14 +00:00
{
2012-12-20 08:04:28 +00:00
if ( stream = = NULL | | stream - > cur_adapt_set = = NULL
| | stream - > cur_representation = = NULL )
2012-10-19 18:12:09 +00:00
return 0 ;
/* TODO: here we have to parse the AudioChannelConfiguration descriptors */
return 0 ;
2013-05-08 14:13:32 +00:00
}
2012-09-29 01:13:29 +00:00
guint
2012-12-20 08:04:28 +00:00
gst_mpdparser_get_list_and_nb_of_audio_language ( GstMpdClient * client ,
GList * * lang )
2012-10-02 00:28:58 +00:00
{
2012-10-22 16:12:30 +00:00
GstStreamPeriod * stream_period ;
2012-10-02 00:28:58 +00:00
GstAdaptationSetNode * adapt_set ;
2015-09-08 10:04:11 +00:00
GList * adaptation_sets , * list ;
2013-01-25 12:36:35 +00:00
const gchar * this_mimeType = " audio " ;
2012-10-02 00:28:58 +00:00
gchar * mimeType = NULL ;
2015-06-18 15:20:26 +00:00
guint nb_adaptation_set = 0 ;
2012-10-08 08:29:04 +00:00
2012-10-22 16:12:30 +00:00
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 ) ;
2012-10-02 00:28:58 +00:00
2015-09-08 10:04:11 +00:00
adaptation_sets =
gst_mpd_client_get_adaptation_sets_for_period ( client , stream_period ) ;
for ( list = adaptation_sets ; list ; list = g_list_next ( list ) ) {
2012-10-02 00:28:58 +00:00
adapt_set = ( GstAdaptationSetNode * ) list - > data ;
2015-07-03 15:10:20 +00:00
if ( adapt_set & & adapt_set - > lang ) {
2012-10-02 00:28:58 +00:00
gchar * this_lang = adapt_set - > lang ;
GstRepresentationNode * rep ;
rep =
gst_mpdparser_get_lowest_representation ( adapt_set - > Representations ) ;
2015-07-03 15:10:20 +00:00
mimeType = NULL ;
2012-10-02 00:28:58 +00:00
if ( rep - > RepresentationBase )
mimeType = rep - > RepresentationBase - > mimeType ;
if ( ! mimeType & & adapt_set - > RepresentationBase ) {
mimeType = adapt_set - > RepresentationBase - > mimeType ;
}
if ( strncmp_ext ( mimeType , this_mimeType ) = = 0 ) {
2015-07-03 15:10:20 +00:00
nb_adaptation_set + + ;
* lang = g_list_append ( * lang , this_lang ) ;
2012-10-02 00:28:58 +00:00
}
}
}
2012-10-22 16:12:30 +00:00
2015-06-18 15:20:26 +00:00
return nb_adaptation_set ;
2012-09-29 01:13:29 +00:00
}
2013-07-01 20:50:37 +00:00
2013-09-26 19:13:33 +00:00
GstDateTime *
gst_mpd_client_get_next_segment_availability_end_time ( GstMpdClient * client ,
GstActiveStream * stream )
{
GstDateTime * availability_start_time , * rv ;
2015-05-19 01:33:50 +00:00
gint seg_idx ;
2013-09-26 19:13:33 +00:00
GstClockTime seg_duration ;
gint64 offset ;
GstStreamPeriod * stream_period ;
g_return_val_if_fail ( client ! = NULL , NULL ) ;
g_return_val_if_fail ( stream ! = NULL , NULL ) ;
stream_period = gst_mpdparser_get_stream_period ( client ) ;
2015-05-08 19:58:36 +00:00
seg_idx = stream - > segment_index ;
2015-05-07 19:30:48 +00:00
seg_duration = gst_mpd_client_get_segment_duration ( client , stream , NULL ) ;
2013-09-26 19:13:33 +00:00
if ( seg_duration = = 0 )
return NULL ;
availability_start_time = gst_mpd_client_get_availability_start_time ( client ) ;
if ( availability_start_time = = NULL )
return ( GstDateTime * ) NULL ;
if ( stream_period & & stream_period - > period ) {
GstDateTime * t =
gst_mpd_client_add_time_difference ( availability_start_time ,
2015-09-07 11:09:41 +00:00
stream_period - > start / 1000 ) ;
2013-09-26 19:13:33 +00:00
gst_date_time_unref ( availability_start_time ) ;
availability_start_time = t ;
2015-09-07 11:09:41 +00:00
if ( availability_start_time = = NULL ) {
GST_WARNING_OBJECT ( client , " Failed to offset availability_start_time " ) ;
return NULL ;
}
2013-09-26 19:13:33 +00:00
}
offset = ( 1 + seg_idx ) * seg_duration ;
rv = gst_mpd_client_add_time_difference ( availability_start_time ,
offset / GST_USECOND ) ;
gst_date_time_unref ( availability_start_time ) ;
return rv ;
}
2013-07-09 02:24:28 +00:00
gint
gst_mpd_client_check_time_position ( GstMpdClient * client ,
GstActiveStream * stream , GstClockTime ts , gint64 * diff )
{
GDateTime * now = g_date_time_new_now_utc ( ) ;
GDateTime * start =
gst_date_time_to_g_date_time ( client - > mpd_node - > availabilityStartTime ) ;
GTimeSpan stream_now ;
GTimeSpan ts_microseconds ;
GstClockTime duration ;
g_return_val_if_fail ( gst_mpd_client_is_live ( client ) , 0 ) ;
2015-05-07 19:30:48 +00:00
duration = gst_mpd_client_get_segment_duration ( client , stream , NULL ) ;
2013-07-09 02:24:28 +00:00
stream_now = g_date_time_difference ( now , start ) ;
g_date_time_unref ( now ) ;
g_date_time_unref ( start ) ;
/* sum duration to check if the segment is fully ready */
ts_microseconds = ( ts + duration ) / GST_USECOND ;
/*
* This functions checks if a given ts is in the ' available range ' of
* a DASH presentation . This only makes sense for live streams , which
* are continuously adding new segments and removing old ones .
*
* Note : Both the client and the server should use UTC as a time reference .
*
* @ ts is the time since the beginning of the stream and we need to find out
* if it is currently available . The server should be hosting segments
*
* * - - - - - - - - - - - - - - - - . . . - - - * - - - - - - - - - - - * - - - - . . .
* |
* | past ( unavailable ) | | available | future ( unavailable yet )
* |
* * - - - - - - - - - - - - - - - - . . . - - - * - - - - - - - - - - - * - - - - . . .
* | | |
* availabilitStartTime | UTC now
* UTC now - timeShiftBufferDepth
*
* This function should return 0 if @ ts is in the ' available ' area , 1 for
* ' future ' and ' - 1 ' for past and the corresponding distance to the
* ' available ' area is set to @ diff
*
* TODO untested with live presentations with multiple periods as no
* examples for it could be found / generated
*/
if ( ts_microseconds > stream_now ) {
* diff = ts_microseconds - stream_now ;
return 1 ;
}
2015-06-25 14:05:20 +00:00
if ( client - > mpd_node - > timeShiftBufferDepth ! = - 1
2013-07-09 02:24:28 +00:00
& & ts_microseconds <
stream_now - client - > mpd_node - > timeShiftBufferDepth ) {
* diff = ts_microseconds - stream_now ;
return - 1 ;
}
* diff = 0 ;
return 0 ;
}
gboolean
gst_mpd_client_seek_to_time ( GstMpdClient * client , GDateTime * time )
{
GDateTime * start =
gst_date_time_to_g_date_time ( client - > mpd_node - > availabilityStartTime ) ;
GTimeSpan ts_microseconds ;
GstClockTime ts ;
gboolean ret = TRUE ;
GList * stream ;
g_return_val_if_fail ( gst_mpd_client_is_live ( client ) , 0 ) ;
ts_microseconds = g_date_time_difference ( time , start ) ;
g_date_time_unref ( start ) ;
2015-06-04 11:36:05 +00:00
/* Clamp to availability start time, otherwise calculations wrap around */
if ( ts_microseconds < 0 )
ts_microseconds = 0 ;
2013-07-09 02:24:28 +00:00
ts = ts_microseconds * GST_USECOND ;
for ( stream = client - > active_streams ; stream ; stream = g_list_next ( stream ) ) {
ret = ret & gst_mpd_client_stream_seek ( client , stream - > data , ts ) ;
}
return ret ;
}
2013-07-01 20:50:37 +00:00
void
gst_media_fragment_info_clear ( GstMediaFragmentInfo * fragment )
{
g_free ( fragment - > uri ) ;
g_free ( fragment - > index_uri ) ;
}
2015-01-09 19:43:03 +00:00
gboolean
gst_mpd_client_has_isoff_ondemand_profile ( GstMpdClient * client )
{
return client - > profile_isoff_ondemand ;
}
2015-07-07 14:38:08 +00:00
/**
* gst_mpd_client_parse_default_presentation_delay :
* @ client : # GstMpdClient that has a parsed manifest
* @ default_presentation_delay : A string that specifies a time period
* in fragments ( e . g . " 5 f " ) , seconds ( " 12 s " ) or milliseconds
* ( " 12000 ms " )
* Returns : the parsed string in milliseconds
*
* Since : 1.6
*/
gint64
gst_mpd_client_parse_default_presentation_delay ( GstMpdClient * client ,
const gchar * default_presentation_delay )
{
gint64 value ;
char * endptr = NULL ;
g_return_val_if_fail ( client ! = NULL , 0 ) ;
g_return_val_if_fail ( default_presentation_delay ! = NULL , 0 ) ;
value = strtol ( default_presentation_delay , & endptr , 10 ) ;
if ( endptr = = default_presentation_delay | | value = = 0 ) {
return 0 ;
}
while ( * endptr = = ' ' )
endptr + + ;
if ( * endptr = = ' s ' | | * endptr = = ' S ' ) {
value * = 1000 ; /* convert to ms */
} else if ( * endptr = = ' f ' | | * endptr = = ' F ' ) {
gint64 segment_duration ;
g_assert ( client - > mpd_node ! = NULL ) ;
segment_duration = client - > mpd_node - > maxSegmentDuration ;
value * = segment_duration ;
} else if ( * endptr ! = ' m ' & & * endptr ! = ' M ' ) {
GST_ERROR ( " Unable to parse default presentation delay: %s " ,
default_presentation_delay ) ;
value = 0 ;
}
return value ;
}