/* * Dynamic Adaptive Streaming over HTTP demux * Copyright (c) 2017 samsamsam@o2.pl based on HLS demux * Copyright (c) 2017 Steven Liu * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */#include <libxml/parser.h>#include "libavutil/intreadwrite.h"#include "libavutil/opt.h"#include "libavutil/time.h"#include "libavutil/parseutils.h"#include "internal.h"#include "avio_internal.h"#include "dash.h" #define INITIAL_BUFFER_SIZE 32768 struct fragment { int64_t url_offset; int64_t size; char *url;}; /* * reference to : ISO_IEC_23009-1-DASH-2012 * Section: 5.3.9.6.2 * Table: Table 17 — Semantics of SegmentTimeline element * */struct timeline { /* starttime: Element or Attribute Name * specifies the MPD start time, in @timescale units, * the first Segment in the series starts relative to the beginning of the Period. * The value of this attribute must be equal to or greater than the sum of the previous S * element earliest presentation time and the sum of the contiguous Segment durations. * If the value of the attribute is greater than what is expressed by the previous S element, * it expresses discontinuities in the timeline. * If not present then the value shall be assumed to be zero for the first S element * and for the subsequent S elements, the value shall be assumed to be the sum of * the previous S element's earliest presentation time and contiguous duration * (i.e. previous S@starttime + @duration * (@repeat + 1)). * */ int64_t starttime; /* repeat: Element or Attribute Name * specifies the repeat count of the number of following contiguous Segments with * the same duration expressed by the value of @duration. This value is zero-based * (e.g. a value of three means four Segments in the contiguous series). * */ int64_t repeat; /* duration: Element or Attribute Name * specifies the Segment duration, in units of the value of the @timescale. * */ int64_t duration;}; /* * Each playlist has its own demuxer. If it is currently active, * it has an opened AVIOContext too, and potentially an AVPacket * containing the next packet from this stream. */struct representation { char *url_template; AVIOContext pb; AVIOContext *input; AVFormatContext *parent; AVFormatContext *ctx; AVPacket pkt; int rep_idx; int rep_count; int stream_index; enum AVMediaType type; char id[20]; int bandwidth; AVRational framerate; AVStream *assoc_stream; /* demuxer stream associated with this representation */ int n_fragments; struct fragment **fragments; /* VOD list of fragment for profile */ int n_timelines; struct timeline **timelines; int64_t first_seq_no; int64_t last_seq_no; int64_t start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/ int64_t fragment_duration; int64_t fragment_timescale; int64_t presentation_timeoffset; int64_t cur_seq_no; int64_t cur_seg_offset; int64_t cur_seg_size; struct fragment *cur_seg; /* Currently active Media Initialization Section */ struct fragment *init_section; uint8_t *init_sec_buf; uint32_t init_sec_buf_size; uint32_t init_sec_data_len; uint32_t init_sec_buf_read_offset; int64_t cur_timestamp; int is_restart_needed;}; typedef struct DASHContext { const AVClass *class; char *base_url; int n_videos; struct representation **videos; int n_audios; struct representation **audios; int n_subtitles; struct representation **subtitles; /* MediaPresentationDescription Attribute */ uint64_t media_presentation_duration; uint64_t suggested_presentation_delay; uint64_t availability_start_time; uint64_t availability_end_time; uint64_t publish_time; uint64_t minimum_update_period; uint64_t time_shift_buffer_depth; uint64_t min_buffer_time; /* Period Attribute */ uint64_t period_duration; uint64_t period_start; int is_live; AVIOInterruptCB *interrupt_callback; char *allowed_extensions; AVDictionary *avio_opts; int max_url_size; /* Flags for init section*/ int is_init_section_common_video; int is_init_section_common_audio; } DASHContext; static int ishttp(char *url){ const char *proto_name = avio_find_protocol_name(url); return av_strstart(proto_name, "http", NULL);} static int aligned(int val){ return ((val + 0x3F) >> 6) << 6;} static uint64_t get_current_time_in_sec(void){ return av_gettime() / 1000000;} static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime){ struct tm timeinfo; int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int ret = 0; float second = 0.0; /* ISO-8601 date parser */ if (!datetime) return 0; ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second); /* year, month, day, hour, minute, second 6 arguments */ if (ret != 6) { av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n"); } timeinfo.tm_year = year - 1900; timeinfo.tm_mon = month - 1; timeinfo.tm_mday = day; timeinfo.tm_hour = hour; timeinfo.tm_min = minute; timeinfo.tm_sec = (int)second; return av_timegm(&timeinfo);} static uint32_t get_duration_insec(AVFormatContext *s, const char *duration){ /* ISO-8601 duration parser */ uint32_t days = 0; uint32_t hours = 0; uint32_t mins = 0; uint32_t secs = 0; int size = 0; float value = 0; char type = '\0'; const char *ptr = duration; while (*ptr) { if (*ptr == 'P' || *ptr == 'T') { ptr++; continue; } if (sscanf(ptr, "%f%c%n", &value, &type, &size) != 2) { av_log(s, AV_LOG_WARNING, "get_duration_insec get a wrong time format\n"); return 0; /* parser error */ } switch (type) { case 'D': days = (uint32_t)value; break; case 'H': hours = (uint32_t)value; break; case 'M': mins = (uint32_t)value; break; case 'S': secs = (uint32_t)value; break; default: // handle invalid type break; } ptr += size; } return ((days * 24 + hours) * 60 + mins) * 60 + secs;} static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no){ int64_t start_time = 0; int64_t i = 0; int64_t j = 0; int64_t num = 0; if (pls->n_timelines) { for (i = 0; i < pls->n_timelines; i++) { if (pls->timelines[i]->starttime > 0) { start_time = pls->timelines[i]->starttime; } if (num == cur_seq_no) goto finish; start_time += pls->timelines[i]->duration; if (pls->timelines[i]->repeat == -1) { start_time = pls->timelines[i]->duration * cur_seq_no; goto finish; } for (j = 0; j < pls->timelines[i]->repeat; j++) { num++; if (num == cur_seq_no) goto finish; start_time += pls->timelines[i]->duration; } num++; } }finish: return start_time;} static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time){ int64_t i = 0; int64_t j = 0; int64_t num = 0; int64_t start_time = 0; for (i = 0; i < pls->n_timelines; i++) { if (pls->timelines[i]->starttime > 0) { start_time = pls->timelines[i]->starttime; } if (start_time > cur_time) goto finish; start_time += pls->timelines[i]->duration; for (j = 0; j < pls->timelines[i]->repeat; j++) { num++; if (start_time > cur_time) goto finish; start_time += pls->timelines[i]->duration; } num++; } return -1; finish: return num;} static void free_fragment(struct fragment **seg){ if (!(*seg)) { return; } av_freep(&(*seg)->url); av_freep(seg);} static void free_fragment_list(struct representation *pls){ int i; for (i = 0; i < pls->n_fragments; i++) { free_fragment(&pls->fragments[i]); } av_freep(&pls->fragments); pls->n_fragments = 0;} static void free_timelines_list(struct representation *pls){ int i; for (i = 0; i < pls->n_timelines; i++) { av_freep(&pls->timelines[i]); } av_freep(&pls->timelines); pls->n_timelines = 0;} static void free_representation(struct representation *pls){ free_fragment_list(pls); free_timelines_list(pls); free_fragment(&pls->cur_seg); free_fragment(&pls->init_section); av_freep(&pls->init_sec_buf); av_freep(&pls->pb.buffer); ff_format_io_close(pls->parent, &pls->input); if (pls->ctx) { pls->ctx->pb = NULL; avformat_close_input(&pls->ctx); } av_freep(&pls->url_template); av_freep(&pls);} static void free_video_list(DASHContext *c){ int i; for (i = 0; i < c->n_videos; i++) { struct representation *pls = c->videos[i]; free_representation(pls); } av_freep(&c->videos); c->n_videos = 0;} static void free_audio_list(DASHContext *c){ int i; for (i = 0; i < c->n_audios; i++) { struct representation *pls = c->audios[i]; free_representation(pls); } av_freep(&c->audios); c->n_audios = 0;} static void free_subtitle_list(DASHContext *c){ int i; for (i = 0; i < c->n_subtitles; i++) { struct representation *pls = c->subtitles[i]; free_representation(pls); } av_freep(&c->subtitles); c->n_subtitles = 0;} static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, AVDictionary *opts, AVDictionary *opts2, int *is_http){ DASHContext *c = s->priv_data; AVDictionary *tmp = NULL; const char *proto_name = NULL; int ret; av_dict_copy(&tmp, opts, 0); av_dict_copy(&tmp, opts2, 0); if (av_strstart(url, "crypto", NULL)) { if (url[6] == '+' || url[6] == ':') proto_name = avio_find_protocol_name(url + 7); } if (!proto_name) proto_name = avio_find_protocol_name(url); if (!proto_name) return AVERROR_INVALIDDATA; // only http(s) & file are allowed if (av_strstart(proto_name, "file", NULL)) { if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) { av_log(s, AV_LOG_ERROR, "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n" "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n", url); return AVERROR_INVALIDDATA; } } else if (av_strstart(proto_name, "http", NULL)) { ; } else return AVERROR_INVALIDDATA; if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':') ; else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':') ; else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5)) return AVERROR_INVALIDDATA; av_freep(pb); ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp); if (ret >= 0) { // update cookies on http response with setcookies. char *new_cookies = NULL; if (!(s->flags & AVFMT_FLAG_CUSTOM_IO)) av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies); if (new_cookies) { av_dict_set(&opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); } } av_dict_free(&tmp); if (is_http) *is_http = av_strstart(proto_name, "http", NULL); return ret;} static char *get_content_url(xmlNodePtr *baseurl_nodes, int n_baseurl_nodes, int max_url_size, char *rep_id_val, char *rep_bandwidth_val, char *val){ int i; char *text; char *url = NULL; char *tmp_str = av_mallocz(max_url_size); char *tmp_str_2 = av_mallocz(max_url_size); if (!tmp_str || !tmp_str_2) { return NULL; } for (i = 0; i < n_baseurl_nodes; ++i) { if (baseurl_nodes[i] && baseurl_nodes[i]->children && baseurl_nodes[i]->children->type == XML_TEXT_NODE) { text = xmlNodeGetContent(baseurl_nodes[i]->children); if (text) { memset(tmp_str, 0, max_url_size); memset(tmp_str_2, 0, max_url_size); ff_make_absolute_url(tmp_str_2, max_url_size, tmp_str, text); av_strlcpy(tmp_str, tmp_str_2, max_url_size); xmlFree(text); } } } if (val) ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); if (rep_id_val) { url = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val); if (!url) { goto end;Lines 1–500 of 2376. Load more
/* * Dynamic Adaptive Streaming over HTTP demux * Copyright (c) 2017 samsamsam@o2.pl based on HLS demux * Copyright (c) 2017 Steven Liu * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <libxml/parser.h> #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/parseutils.h" #include "internal.h" #include "avio_internal.h" #include "dash.h" #define INITIAL_BUFFER_SIZE 32768 struct fragment { int64_t url_offset; int64_t size; char *url; }; /* * reference to : ISO_IEC_23009-1-DASH-2012 * Section: 5.3.9.6.2 * Table: Table 17 — Semantics of SegmentTimeline element * */ struct timeline { /* starttime: Element or Attribute Name * specifies the MPD start time, in @timescale units, * the first Segment in the series starts relative to the beginning of the Period. * The value of this attribute must be equal to or greater than the sum of the previous S * element earliest presentation time and the sum of the contiguous Segment durations. * If the value of the attribute is greater than what is expressed by the previous S element, * it expresses discontinuities in the timeline. * If not present then the value shall be assumed to be zero for the first S element * and for the subsequent S elements, the value shall be assumed to be the sum of * the previous S element's earliest presentation time and contiguous duration * (i.e. previous S@starttime + @duration * (@repeat + 1)). * */ int64_t starttime; /* repeat: Element or Attribute Name * specifies the repeat count of the number of following contiguous Segments with * the same duration expressed by the value of @duration. This value is zero-based * (e.g. a value of three means four Segments in the contiguous series). * */ int64_t repeat; /* duration: Element or Attribute Name * specifies the Segment duration, in units of the value of the @timescale. * */ int64_t duration; }; /* * Each playlist has its own demuxer. If it is currently active, * it has an opened AVIOContext too, and potentially an AVPacket * containing the next packet from this stream. */ struct representation { char *url_template; AVIOContext pb; AVIOContext *input; AVFormatContext *parent; AVFormatContext *ctx; AVPacket pkt; int rep_idx; int rep_count; int stream_index; enum AVMediaType type; char id[20]; int bandwidth; AVRational framerate; AVStream *assoc_stream; /* demuxer stream associated with this representation */ int n_fragments; struct fragment **fragments; /* VOD list of fragment for profile */ int n_timelines; struct timeline **timelines; int64_t first_seq_no; int64_t last_seq_no; int64_t start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/ int64_t fragment_duration; int64_t fragment_timescale; int64_t presentation_timeoffset; int64_t cur_seq_no; int64_t cur_seg_offset; int64_t cur_seg_size; struct fragment *cur_seg; /* Currently active Media Initialization Section */ struct fragment *init_section; uint8_t *init_sec_buf; uint32_t init_sec_buf_size; uint32_t init_sec_data_len; uint32_t init_sec_buf_read_offset; int64_t cur_timestamp; int is_restart_needed; }; typedef struct DASHContext { const AVClass *class; char *base_url; int n_videos; struct representation **videos; int n_audios; struct representation **audios; int n_subtitles; struct representation **subtitles; /* MediaPresentationDescription Attribute */ uint64_t media_presentation_duration; uint64_t suggested_presentation_delay; uint64_t availability_start_time; uint64_t availability_end_time; uint64_t publish_time; uint64_t minimum_update_period; uint64_t time_shift_buffer_depth; uint64_t min_buffer_time; /* Period Attribute */ uint64_t period_duration; uint64_t period_start; int is_live; AVIOInterruptCB *interrupt_callback; char *allowed_extensions; AVDictionary *avio_opts; int max_url_size; /* Flags for init section*/ int is_init_section_common_video; int is_init_section_common_audio; } DASHContext; static int ishttp(char *url) { const char *proto_name = avio_find_protocol_name(url); return av_strstart(proto_name, "http", NULL); } static int aligned(int val) { return ((val + 0x3F) >> 6) << 6; } static uint64_t get_current_time_in_sec(void) { return av_gettime() / 1000000; } static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime) { struct tm timeinfo; int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int ret = 0; float second = 0.0; /* ISO-8601 date parser */ if (!datetime) return 0; ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second); /* year, month, day, hour, minute, second 6 arguments */ if (ret != 6) { av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n"); } timeinfo.tm_year = year - 1900; timeinfo.tm_mon = month - 1; timeinfo.tm_mday = day; timeinfo.tm_hour = hour; timeinfo.tm_min = minute; timeinfo.tm_sec = (int)second; return av_timegm(&timeinfo); } static uint32_t get_duration_insec(AVFormatContext *s, const char *duration) { /* ISO-8601 duration parser */ uint32_t days = 0; uint32_t hours = 0; uint32_t mins = 0; uint32_t secs = 0; int size = 0; float value = 0; char type = '\0'; const char *ptr = duration; while (*ptr) { if (*ptr == 'P' || *ptr == 'T') { ptr++; continue; } if (sscanf(ptr, "%f%c%n", &value, &type, &size) != 2) { av_log(s, AV_LOG_WARNING, "get_duration_insec get a wrong time format\n"); return 0; /* parser error */ } switch (type) { case 'D': days = (uint32_t)value; break; case 'H': hours = (uint32_t)value; break; case 'M': mins = (uint32_t)value; break; case 'S': secs = (uint32_t)value; break; default: // handle invalid type break; } ptr += size; } return ((days * 24 + hours) * 60 + mins) * 60 + secs; } static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no) { int64_t start_time = 0; int64_t i = 0; int64_t j = 0; int64_t num = 0; if (pls->n_timelines) { for (i = 0; i < pls->n_timelines; i++) { if (pls->timelines[i]->starttime > 0) { start_time = pls->timelines[i]->starttime; } if (num == cur_seq_no) goto finish; start_time += pls->timelines[i]->duration; if (pls->timelines[i]->repeat == -1) { start_time = pls->timelines[i]->duration * cur_seq_no; goto finish; } for (j = 0; j < pls->timelines[i]->repeat; j++) { num++; if (num == cur_seq_no) goto finish; start_time += pls->timelines[i]->duration; } num++; } } finish: return start_time; } static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time) { int64_t i = 0; int64_t j = 0; int64_t num = 0; int64_t start_time = 0; for (i = 0; i < pls->n_timelines; i++) { if (pls->timelines[i]->starttime > 0) { start_time = pls->timelines[i]->starttime; } if (start_time > cur_time) goto finish; start_time += pls->timelines[i]->duration; for (j = 0; j < pls->timelines[i]->repeat; j++) { num++; if (start_time > cur_time) goto finish; start_time += pls->timelines[i]->duration; } num++; } return -1; finish: return num; } static void free_fragment(struct fragment **seg) { if (!(*seg)) { return; } av_freep(&(*seg)->url); av_freep(seg); } static void free_fragment_list(struct representation *pls) { int i; for (i = 0; i < pls->n_fragments; i++) { free_fragment(&pls->fragments[i]); } av_freep(&pls->fragments); pls->n_fragments = 0; } static void free_timelines_list(struct representation *pls) { int i; for (i = 0; i < pls->n_timelines; i++) { av_freep(&pls->timelines[i]); } av_freep(&pls->timelines); pls->n_timelines = 0; } static void free_representation(struct representation *pls) { free_fragment_list(pls); free_timelines_list(pls); free_fragment(&pls->cur_seg); free_fragment(&pls->init_section); av_freep(&pls->init_sec_buf); av_freep(&pls->pb.buffer); ff_format_io_close(pls->parent, &pls->input); if (pls->ctx) { pls->ctx->pb = NULL; avformat_close_input(&pls->ctx); } av_freep(&pls->url_template); av_freep(&pls); } static void free_video_list(DASHContext *c) { int i; for (i = 0; i < c->n_videos; i++) { struct representation *pls = c->videos[i]; free_representation(pls); } av_freep(&c->videos); c->n_videos = 0; } static void free_audio_list(DASHContext *c) { int i; for (i = 0; i < c->n_audios; i++) { struct representation *pls = c->audios[i]; free_representation(pls); } av_freep(&c->audios); c->n_audios = 0; } static void free_subtitle_list(DASHContext *c) { int i; for (i = 0; i < c->n_subtitles; i++) { struct representation *pls = c->subtitles[i]; free_representation(pls); } av_freep(&c->subtitles); c->n_subtitles = 0; } static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, AVDictionary *opts, AVDictionary *opts2, int *is_http) { DASHContext *c = s->priv_data; AVDictionary *tmp = NULL; const char *proto_name = NULL; int ret; av_dict_copy(&tmp, opts, 0); av_dict_copy(&tmp, opts2, 0); if (av_strstart(url, "crypto", NULL)) { if (url[6] == '+' || url[6] == ':') proto_name = avio_find_protocol_name(url + 7); } if (!proto_name) proto_name = avio_find_protocol_name(url); if (!proto_name) return AVERROR_INVALIDDATA; // only http(s) & file are allowed if (av_strstart(proto_name, "file", NULL)) { if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) { av_log(s, AV_LOG_ERROR, "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n" "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n", url); return AVERROR_INVALIDDATA; } } else if (av_strstart(proto_name, "http", NULL)) { ; } else return AVERROR_INVALIDDATA; if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':') ; else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':') ; else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5)) return AVERROR_INVALIDDATA; av_freep(pb); ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp); if (ret >= 0) { // update cookies on http response with setcookies. char *new_cookies = NULL; if (!(s->flags & AVFMT_FLAG_CUSTOM_IO)) av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies); if (new_cookies) { av_dict_set(&opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); } } av_dict_free(&tmp); if (is_http) *is_http = av_strstart(proto_name, "http", NULL); return ret; } static char *get_content_url(xmlNodePtr *baseurl_nodes, int n_baseurl_nodes, int max_url_size, char *rep_id_val, char *rep_bandwidth_val, char *val) { int i; char *text; char *url = NULL; char *tmp_str = av_mallocz(max_url_size); char *tmp_str_2 = av_mallocz(max_url_size); if (!tmp_str || !tmp_str_2) { return NULL; } for (i = 0; i < n_baseurl_nodes; ++i) { if (baseurl_nodes[i] && baseurl_nodes[i]->children && baseurl_nodes[i]->children->type == XML_TEXT_NODE) { text = xmlNodeGetContent(baseurl_nodes[i]->children); if (text) { memset(tmp_str, 0, max_url_size); memset(tmp_str_2, 0, max_url_size); ff_make_absolute_url(tmp_str_2, max_url_size, tmp_str, text); av_strlcpy(tmp_str, tmp_str_2, max_url_size); xmlFree(text); } } } if (val) ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); if (rep_id_val) { url = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val); if (!url) { goto end;