diff mbox

[FFmpeg-devel,v9] avformat/dashdec: add dash demuxer base version

Message ID 20170327001059.69301-1-lq@chinaffmpeg.org
State Superseded
Headers show

Commit Message

Steven Liu March 27, 2017, 12:10 a.m. UTC
v2 fixed:
1. from autodetect to disabled
2. from camelCase code style to ffmpeg code style
3. from RepType to AVMediaType
4. fix variable typo
5. change time value from uint32_t to uint64_t
6. removed be used once API
7. change 'time(NULL)`, except it is not 2038-safe.' to av_gettime and
av_timegm
8. merge complex free operation to free_fragment
9. use API from snprintf to av_asprintf

v3 fixed:
1. fix typo from --enabled-xml2 to --enable-xml2

v4 fixed:
1. from --enable-xml2 to --enable-libxml2
2. move system includes to top
3. remove nouse includes
4. rename enum name
5. add a trailing comma for the last entry enum
6. fix comment typo
7. add const to DASHContext class front
8. check sscanf if return arguments and give warning message when error
9. check validity before free seg->url and seg
10. check if the val is null, before use atoll

v5 fixed:
1. fix typo from mainifest to manifest

v6 fixed:
1. from realloc to av_realloc
2. from free to av_free

v7 fixed:
1. remove the -lxml2 from configure when require_pkg_config

v8 fixed:
1. fix replace filename template by av_asprintf secure problem

v9 modified:
1. make manifest parser clearly

Reviewed-by: Clément Bœsch <u@pkh.me>
Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
Reviewed-by: Carl Eugen Hoyos <cehoyos@ag.or.at>
Reviewed-by: Rodger Combs <rodger.combs@gmail.com>
Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
Reviewed-by: Nicolas George <george@nsup.org>
Reviewed-by: Ricardo Constantino <wiiaboo@gmail.com>
Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
---
 configure                |    4 +
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    2 +-
 libavformat/dashdec.c    | 1842 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1848 insertions(+), 1 deletion(-)
 create mode 100644 libavformat/dashdec.c

Comments

Rodger Combs March 27, 2017, 12:39 a.m. UTC | #1
> On Mar 26, 2017, at 19:10, Steven Liu <lq@chinaffmpeg.org> wrote:
> 
> v2 fixed:
> 1. from autodetect to disabled
> 2. from camelCase code style to ffmpeg code style
> 3. from RepType to AVMediaType
> 4. fix variable typo
> 5. change time value from uint32_t to uint64_t
> 6. removed be used once API
> 7. change 'time(NULL)`, except it is not 2038-safe.' to av_gettime and
> av_timegm
> 8. merge complex free operation to free_fragment
> 9. use API from snprintf to av_asprintf
> 
> v3 fixed:
> 1. fix typo from --enabled-xml2 to --enable-xml2
> 
> v4 fixed:
> 1. from --enable-xml2 to --enable-libxml2
> 2. move system includes to top
> 3. remove nouse includes
> 4. rename enum name
> 5. add a trailing comma for the last entry enum
> 6. fix comment typo
> 7. add const to DASHContext class front
> 8. check sscanf if return arguments and give warning message when error
> 9. check validity before free seg->url and seg
> 10. check if the val is null, before use atoll
> 
> v5 fixed:
> 1. fix typo from mainifest to manifest
> 
> v6 fixed:
> 1. from realloc to av_realloc
> 2. from free to av_free
> 
> v7 fixed:
> 1. remove the -lxml2 from configure when require_pkg_config
> 
> v8 fixed:
> 1. fix replace filename template by av_asprintf secure problem
> 
> v9 modified:
> 1. make manifest parser clearly
> 
> Reviewed-by: Clément Bœsch <u@pkh.me>
> Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
> Reviewed-by: Carl Eugen Hoyos <cehoyos@ag.or.at>
> Reviewed-by: Rodger Combs <rodger.combs@gmail.com>
> Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
> Reviewed-by: Nicolas George <george@nsup.org>
> Reviewed-by: Ricardo Constantino <wiiaboo@gmail.com>
> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> ---
> configure                |    4 +
> libavformat/Makefile     |    1 +
> libavformat/allformats.c |    2 +-
> libavformat/dashdec.c    | 1842 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1848 insertions(+), 1 deletion(-)
> create mode 100644 libavformat/dashdec.c
> 
> diff --git a/configure b/configure
> index 9cc7e7d..4a6fe8a 100755
> --- a/configure
> +++ b/configure
> @@ -273,6 +273,7 @@ External library support:
>   --enable-libxcb-shape    enable X11 grabbing shape rendering [autodetect]
>   --enable-libxvid         enable Xvid encoding via xvidcore,
>                            native MPEG-4/Xvid encoder exists [no]
> +  --enable-libxml2            enable XML parsing using the C library libxml2 [no]
>   --enable-libzimg         enable z.lib, needed for zscale filter [no]
>   --enable-libzmq          enable message passing via libzmq [no]
>   --enable-libzvbi         enable teletext support via libzvbi [no]
> @@ -1574,6 +1575,7 @@ EXTERNAL_LIBRARY_LIST="
>     libvpx
>     libwavpack
>     libwebp
> +    libxml2
>     libzimg
>     libzmq
>     libzvbi
> @@ -2906,6 +2908,7 @@ avi_muxer_select="riffenc"
> caf_demuxer_select="iso_media riffdec"
> caf_muxer_select="iso_media"
> dash_muxer_select="mp4_muxer"
> +dash_demuxer_deps="libxml2"
> dirac_demuxer_select="dirac_parser"
> dts_demuxer_select="dca_parser"
> dtshd_demuxer_select="dca_parser"
> @@ -5908,6 +5911,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>                                check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||
>                                die "ERROR: openssl not found"; }
> enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; }
> +enabled libxml2             && require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion
> 
> # libdc1394 check
> if enabled libdc1394; then
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index f56ef16..d77ffaa 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -134,6 +134,7 @@ OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
> OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
> OBJS-$(CONFIG_DATA_MUXER)                += rawenc.o
> OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o
> +OBJS-$(CONFIG_DASH_DEMUXER)              += dashdec.o
> OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
> OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
> OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index 09e62c3..d57314b 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -96,7 +96,7 @@ static void register_all(void)
>     REGISTER_DEMUXER (CINE,             cine);
>     REGISTER_DEMUXER (CONCAT,           concat);
>     REGISTER_MUXER   (CRC,              crc);
> -    REGISTER_MUXER   (DASH,             dash);
> +    REGISTER_MUXDEMUX(DASH,             dash);
>     REGISTER_MUXDEMUX(DATA,             data);
>     REGISTER_MUXDEMUX(DAUD,             daud);
>     REGISTER_DEMUXER (DCSTR,            dcstr);
> diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
> new file mode 100644
> index 0000000..a4d3ba2
> --- /dev/null
> +++ b/libavformat/dashdec.c
> @@ -0,0 +1,1842 @@
> +/*
> + * 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"
> +
> +#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 {
> +    /* t: 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@t + @d * (@r + 1)).
> +     * */
> +    int64_t t;
> +    /* r: Element or Attribute Name
> +     * specifies the repeat count of the number of following contiguous Segments with
> +     * the same duration expressed by the value of @d. This value is zero-based
> +     * (e.g. a value of three means four Segments in the contiguous series).
> +     * */
> +    int32_t r;
> +    /* d: Element or Attribute Name
> +     * specifies the Segment duration, in units of the value of the @timescale.
> +     * */
> +    int64_t d;
> +};
> +
> +enum DASHTmplUrlType {
> +    TMP_URL_TYPE_UNSPECIFIED,
> +    TMP_URL_TYPE_NUMBER,
> +    TMP_URL_TYPE_TIME,
> +};
> +
> +/*
> + * 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;
> +    enum DASHTmplUrlType tmp_url_type;
> +    AVIOContext pb;
> +    AVIOContext *input;
> +    AVFormatContext *parent;
> +    AVFormatContext *ctx;
> +    AVPacket pkt;
> +    int rep_idx;
> +    int rep_count;
> +    int stream_index;
> +
> +    enum AVMediaType type;
> +    int64_t target_duration;
> +
> +    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 fragment_duration;
> +    int64_t fragment_timescale;
> +
> +    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;
> +    int fix_multiple_stsd_order;
> +    int64_t cur_timestamp;
> +};
> +
> +typedef struct DASHContext {
> +    const AVClass *class;
> +    char *base_url;
> +    struct representation *cur_video;
> +    struct representation *cur_audio;
> +    uint64_t media_presentation_duration_sec;
> +    uint64_t suggested_presentation_delay_sec;
> +    uint64_t presentation_delay_sec;
> +    uint64_t availability_start_time_sec;
> +    uint64_t publish_time_sec;
> +    uint64_t minimum_update_period_sec;
> +    uint64_t time_shift_buffer_depth_sec;
> +    uint64_t min_buffer_time_sec;
> +    uint64_t period_duration_sec;
> +    uint64_t period_start_sec;
> +    int is_live;
> +    AVIOInterruptCB *interrupt_callback;
> +    char *user_agent;                    ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
> +    char *cookies;                       ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
> +    char *headers;                       ///< holds HTTP headers set as an AVOption to the HTTP protocol context
> +    AVDictionary *avio_opts;
> +} DASHContext;
> +
> +static uint64_t GetCurrentTimeInSec(void)

Code style: "gettime_sec(void)" or the like; not CamelCased.

> +{
> +    return  av_gettime() / 1000000;
> +}
> +
> +static uint64_t GetUTCDateTimeInSec(AVFormatContext *s, const char *datetime)

I still don't understand why av_parse_time isn't applicable here; it parses ISO-8601 dates just fine.

> +{
> +    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, "GetUTCDateTimeInSec get a wrong time format\n");
> +       // return 0; /* parser error */
> +    }
> +    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 GetDurationInSec(AVFormatContext *s, const char *duration)

Same question here; av_parse_time parses ISO-8601 durations if you set duration=1.

> +{
> +    /* ISO-8601 duration parser */
> +    uint32_t days = 0;
> +    uint32_t hours = 0;
> +    uint32_t mins = 0;
> +    uint32_t secs = 0;
> +    uint32_t size = 0;
> +    float value = 0;
> +    uint8_t 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, "GetDurationInSec 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 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;
> +}
> +
> +/*
> + * Used to reset a statically allocated AVPacket to a clean slate,
> + * containing no data.
> + */
> +static void reset_packet(AVPacket *pkt)
> +{
> +    av_init_packet(pkt);
> +    pkt->data = NULL;
> +}
> +
> +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_packet_unref(&pls->pkt);
> +    reset_packet(&pls->pkt);
> +    av_freep(&pls->pb.buffer);
> +    if (pls->input)
> +        ff_format_io_close(pls->parent, &pls->input);
> +    if (pls->ctx) {
> +        pls->ctx->pb = NULL;
> +        avformat_close_input(&pls->ctx);
> +    }
> +
> +    av_free(pls->url_template);
> +    av_free(pls);
> +}
> +
> +static void update_options(char **dest, const char *name, void *src)
> +{
> +    av_freep(dest);
> +    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
> +    if (*dest)
> +        av_freep(dest);
> +}
> +
> +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, "http", NULL) && !av_strstart(proto_name, "file", NULL))
> +        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;
> +
> +    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
> +    if (ret >= 0) {
> +        // update cookies on http response with setcookies.
> +        void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> +        update_options(&c->cookies, "cookies", u);
> +        av_dict_set(&opts, "cookies", c->cookies, 0);
> +    }
> +
> +    av_dict_free(&tmp);
> +
> +    if (is_http)
> +        *is_http = av_strstart(proto_name, "http", NULL);
> +
> +    return ret;
> +
> +}
> +
> +static char *strreplace(const char *str, const char *from, const char *to)
> +{
> +    /* Adjust each of the below values to suit your needs. */
> +    /* Increment positions cache size initially by this number. */
> +    size_t cache_sz_inc = 16;
> +    /* Thereafter, each time capacity needs to be increased,
> +     * multiply the increment by this factor. */
> +    const size_t cache_sz_inc_factor = 3;
> +    /* But never increment capacity by more than this number. */
> +    const size_t cache_sz_inc_max = 1048576;
> +
> +    char *pret, *ret = NULL;
> +    const char *pstr2, *pstr = str;
> +    size_t i, count = 0;
> +    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
> +    size_t cache_sz = 0;
> +    size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
> +
> +    /* Find all matches and cache their positions. */
> +    while ((pstr2 = av_stristr(pstr, from))) {
> +        count++;
> +        /* Increase the cache size when necessary. */
> +        if (cache_sz < count) {
> +            cache_sz += cache_sz_inc;
> +            pos_cache_tmp = av_realloc(pos_cache, sizeof(*pos_cache) * cache_sz);
> +            if (!pos_cache_tmp) {
> +                goto end_strreplace;
> +            } else pos_cache = pos_cache_tmp;
> +            cache_sz_inc *= cache_sz_inc_factor;
> +            if (cache_sz_inc > cache_sz_inc_max) {
> +                cache_sz_inc = cache_sz_inc_max;
> +            }
> +        }
> +
> +        pos_cache[count-1] = pstr2 - str;
> +        pstr = pstr2 + fromlen;
> +    }
> +    orglen = pstr - str + strlen(pstr);
> +    /* Allocate memory for the post-replacement string. */
> +    if (count > 0) {
> +        tolen = strlen(to);
> +        retlen = orglen + (tolen - fromlen) * count;
> +    } else {
> +        retlen = orglen;
> +    }
> +    ret = av_malloc(retlen + 1);
> +    if (!ret) {
> +        goto end_strreplace;
> +    }
> +
> +    if (!count) {
> +        /* If no matches, then just duplicate the string. */
> +        av_strlcpy(ret, str, retlen + 1);
> +    } else {
> +        /* Otherwise, duplicate the string whilst performing
> +         * the replacements using the position cache. */
> +        pret = ret;
> +        memcpy(pret, str, pos_cache[0]);
> +        pret += pos_cache[0];
> +        for (i = 0; i < count; i++) {
> +            memcpy(pret, to, tolen);
> +            pret += tolen;
> +            pstr = str + pos_cache[i] + fromlen;
> +            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;
> +            memcpy(pret, pstr, cpylen);
> +            pret += cpylen;
> +        }
> +        ret[retlen] = '\0';
> +    }
> +
> +end_strreplace:
> +    /* Free the cache and return the post-replacement string,
> +     * which will be NULL in the event of an error. */
> +    av_free(pos_cache);
> +    return ret;
> +}
> +
> +static char *replace_template_str(const char *url, const char *marker)
> +{
> +    char *prefix = NULL;
> +    char *start = 0;
> +    char *end = NULL;
> +    char *tmp_url = NULL;
> +    char *new_url = NULL;
> +    int marker_len;
> +
> +    prefix = av_strdup(marker);
> +    marker_len = strlen(prefix) - 1;
> +    prefix[marker_len] = '\0';
> +
> +    start = av_stristr(url, prefix);
> +    if (!start)
> +        goto finish;
> +    end = strchr(start + 1, '$');
> +    if (!end)
> +        goto finish;
> +
> +    tmp_url = av_mallocz(MAX_URL_SIZE);
> +    if (!tmp_url) {
> +        return NULL;
> +    }
> +
> +    av_strlcpy(tmp_url, url, start - url+ 1);
> +    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end - start - marker_len + 1);
> +    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
> +
> +finish:
> +    av_free(prefix);
> +    new_url = strreplace(tmp_url ? tmp_url : url, marker, "%"PRId64);

Since you're no longer using a printf function, this replacement doesn't make sense. You should replace with the actual value you want to substitute instead.

> +    av_free(tmp_url);
> +    return new_url;
> +}
> +
> +static char *get_content_url(xmlNodePtr *baseurl_nodes, int n_baseurl_nodes, xmlChar *rep_id_val, xmlChar *rep_bandwidth_val, xmlChar *val)
> +{
> +    int i;
> +    xmlChar *text;
> +    char *url = NULL;
> +    char *tmp_str = av_mallocz(MAX_URL_SIZE);
> +    char *tmp_str_2 = NULL;
> +
> +    if (!tmp_str) {
> +        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) {
> +                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
> +                if (!tmp_str_2) {
> +                    av_free(tmp_str);
> +                    return NULL;
> +                }
> +                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str, text);
> +                av_free(tmp_str);
> +                tmp_str = tmp_str_2;
> +                xmlFree(text);
> +            }
> +        }
> +    }
> +    if (val)
> +        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
> +    if (rep_id_val) {
> +        url = strreplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
> +        av_free(tmp_str);
> +        tmp_str = url;
> +    }
> +    if (rep_bandwidth_val && tmp_str)
> +        url = strreplace(tmp_str, "$Bandwidth$", (const char*)rep_bandwidth_val);
> +    if (tmp_str != url)
> +        av_free(tmp_str);
> +    return url;
> +}
> +
> +static xmlChar *get_val_from_nodes_tab(xmlNodePtr *nodes, const int n_nodes, const xmlChar *attrname)
> +{
> +    int i;
> +    xmlChar *val;
> +
> +    for (i = 0; i < n_nodes; ++i) {
> +        if (nodes[i]) {
> +            val = xmlGetProp(nodes[i], attrname);
> +            if (val)
> +                return val;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +static xmlNodePtr findChildNodeByName(xmlNodePtr rootnode, const xmlChar *nodename)
> +{
> +    xmlNodePtr node = rootnode;
> +    if (!node) {
> +        return NULL;
> +    }
> +
> +    node = xmlFirstElementChild(node);
> +    while (node) {
> +        if (!xmlStrcmp(node->name, nodename)) {
> +            return node;
> +        }
> +        node = xmlNextElementSibling(node);
> +    }
> +    return NULL;
> +}
> +
> +static enum AVMediaType get_content_type(xmlNodePtr node)
> +{
> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> +    int i = 0;
> +    const char *attr;
> +
> +    if (node) {
> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> +            attr = (i) ? "mimeType" : "contentType";
> +            xmlChar *val = xmlGetProp(node, attr);
> +            if (val) {
> +                if (av_stristr((const char *)val, "video")) {
> +                    type = AVMEDIA_TYPE_VIDEO;
> +                } else if (av_stristr((const char *)val, "audio")) {
> +                    type = AVMEDIA_TYPE_AUDIO;
> +                }
> +                xmlFree(val);
> +            }
> +            i++;
> +        }
> +    }
> +    return type;
> +}
> +
> +static int parse_manifest_segmenturlnode(AVFormatContext *s, struct representation *rep,
> +                                          xmlNodePtr fragmenturl_node,
> +                                          xmlNodePtr *baseurl_nodes,
> +                                          xmlChar *rep_id_val,
> +                                          xmlChar *rep_bandwidth_val)
> +{
> +    xmlChar *initialization_val = NULL;
> +    xmlChar *media_val = NULL;
> +
> +    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar *)"Initialization")) {
> +        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
> +        if (initialization_val) {
> +            rep->init_section = av_mallocz(sizeof(struct fragment));
> +            if (!rep->init_section) {
> +                xmlFree(initialization_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
> +            rep->init_section->size = -1;
> +            xmlFree(initialization_val);
> +        }
> +    } else if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar *)"SegmentURL")) {
> +        media_val = xmlGetProp(fragmenturl_node, "media");
> +        if (media_val) {
> +            struct fragment *seg = av_mallocz(sizeof(struct fragment));
> +            if (!seg) {
> +                xmlFree(media_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
> +            seg->size = -1;
> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> +            xmlFree(media_val);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int parse_manifest_segmenttimeline(AVFormatContext *s, struct representation *rep,
> +                                           xmlNodePtr fragment_timeline_node)
> +{
> +    xmlAttrPtr attr = NULL;
> +    xmlChar *val  = NULL;
> +
> +    if (!xmlStrcmp(fragment_timeline_node->name, (const xmlChar *)"S")) {
> +        struct timeline *tml = av_mallocz(sizeof(struct timeline));
> +        if (!tml) {
> +            return AVERROR(ENOMEM);
> +        }
> +        attr = fragment_timeline_node->properties;
> +        while (attr) {
> +            val = xmlGetProp(fragment_timeline_node, attr->name);
> +
> +            if (!val) {
> +                av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline attr->name = %s val is NULL\n", attr->name);
> +                continue;
> +            }
> +
> +            if (!xmlStrcmp(attr->name, (const xmlChar *)"t")) {
> +                tml->t = (int64_t)atoll((const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> +                tml->r =(int32_t) atoi((const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> +                tml->d = (int64_t)atoll((const char *)val);
> +                rep->fragment_duration = (int64_t) atoll((const char *)val);
> +            }
> +            attr = attr->next;
> +            xmlFree(val);
> +        }
> +        dynarray_add(&rep->timelines, &rep->n_timelines, tml);
> +    }
> +
> +    return 0;
> +}
> +
> +static int parse_manifest_representation(AVFormatContext *s, const char *url,
> +                                          xmlNodePtr node,
> +                                          xmlNodePtr adaptionset_node,
> +                                          xmlNodePtr mpd_baseurl_node,
> +                                          xmlNodePtr period_baseurl_node,
> +                                          xmlNodePtr fragment_template_node,
> +                                          xmlNodePtr content_component_node,
> +                                          xmlNodePtr adaptionset_baseurl_node)
> +{
> +    int32_t ret = 0;
> +    int32_t audio_rep_idx = 0;
> +    int32_t video_rep_idx = 0;
> +    char *temp_string = NULL;
> +    DASHContext *c = s->priv_data;
> +    struct representation *rep = NULL;
> +    struct fragment *seg = NULL;
> +    xmlNodePtr representation_segmenttemplate_node = NULL;
> +    xmlNodePtr representation_baseurl_node = NULL;
> +    xmlNodePtr representation_segmentlist_node = NULL;
> +    xmlNodePtr fragment_timeline_node = NULL;
> +    xmlNodePtr fragment_templates_tab[2];
> +    xmlChar *duration_val = NULL;
> +    xmlChar *startnumber_val = NULL;
> +    xmlChar *timescale_val = NULL;
> +    xmlChar *initialization_val = NULL;
> +    xmlChar *media_val = NULL;
> +    xmlNodePtr baseurl_nodes[4];
> +    xmlNodePtr representation_node = node;
> +    xmlChar *rep_id_val = xmlGetProp(representation_node, "id");
> +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node, "bandwidth");
> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> +
> +    // try get information from representation
> +    if (type == AVMEDIA_TYPE_UNKNOWN)
> +        type = get_content_type(representation_node);
> +    // try get information from contentComponen
> +    if (type == AVMEDIA_TYPE_UNKNOWN)
> +        type = get_content_type(content_component_node);
> +    // try get information from adaption set
> +    if (type == AVMEDIA_TYPE_UNKNOWN)
> +        type = get_content_type(adaptionset_node);
> +    if (type == AVMEDIA_TYPE_UNKNOWN) {
> +        av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported representation type\n", url);
> +    } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
> +        // convert selected representation to our internal struct
> +        rep = av_mallocz(sizeof(struct representation));
> +        if (!rep) {
> +            ret = AVERROR(ENOMEM);
> +            goto end;
> +        }
> +        representation_segmenttemplate_node = findChildNodeByName(representation_node, "SegmentTemplate");
> +        representation_baseurl_node = findChildNodeByName(representation_node, "BaseURL");
> +        representation_segmentlist_node = findChildNodeByName(representation_node, "SegmentList");
> +        reset_packet(&rep->pkt);
> +
> +        baseurl_nodes[0] = mpd_baseurl_node;
> +        baseurl_nodes[1] = period_baseurl_node;
> +        baseurl_nodes[2] = adaptionset_baseurl_node;
> +        baseurl_nodes[3] = representation_baseurl_node;
> +
> +        if (representation_segmenttemplate_node || fragment_template_node) {
> +            fragment_timeline_node = NULL;
> +            fragment_templates_tab[0] = representation_segmenttemplate_node;
> +            fragment_templates_tab[1] = fragment_template_node;
> +
> +            duration_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "duration");
> +            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "startNumber");
> +            timescale_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "timescale");
> +            initialization_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "initialization");
> +            media_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "media");
> +
> +            if (initialization_val) {
> +                rep->init_section = av_mallocz(sizeof(struct fragment));
> +                if (!rep->init_section) {
> +                    av_free(rep);
> +                    ret = AVERROR(ENOMEM);
> +                    goto end;
> +                }
> +                rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
> +                rep->init_section->size = -1;
> +                xmlFree(initialization_val);
> +            }
> +
> +            if (media_val) {
> +                temp_string = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
> +                if (temp_string) {
> +                    if (av_stristr(temp_string, "$Number")) {
> +                        rep->url_template = av_strdup(temp_string);

Since you're no longer replacing here, this if/else tree mostly doesn't make sense anymore. You should remove the av_free below and store the result of get_content_url() directly to rep->url_template, and just have a check here to set tmp_url_type. Also, you need to check for a NULL return from get_content_url.

> +                        /* A URL template is provided from which clients build a chunk list
> +                         * where the chunk URLs include chunk numbers (like index numbers).
> +                         */
> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
> +                    } else if (av_stristr(temp_string, "$Time")) {
> +                        rep->url_template = av_strdup(temp_string);
> +                        /* A URL template is provided from which clients build a chunk list
> +                         * where the chunk URLs include chunk start times.
> +                         */
> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
> +                    } else {
> +                        rep->url_template = temp_string;
> +                        temp_string = NULL;
> +                    }
> +                    av_free(temp_string);
> +                }
> +                xmlFree(media_val);
> +            }
> +            if (duration_val) {
> +                rep->fragment_duration = (int64_t) atoll((const char *)duration_val);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) atoll((const char *)timescale_val);
> +                xmlFree(timescale_val);
> +            }
> +            if (startnumber_val) {
> +                rep->first_seq_no = (int64_t) atoll((const char *)startnumber_val);
> +                xmlFree(startnumber_val);
> +            }
> +
> +            fragment_timeline_node = findChildNodeByName(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = findChildNodeByName(fragment_template_node, "SegmentTimeline");
> +            if (fragment_timeline_node) {
> +                fragment_timeline_node = xmlFirstElementChild(fragment_timeline_node);
> +                while (fragment_timeline_node) {
> +                    ret = parse_manifest_segmenttimeline(s, rep, fragment_timeline_node);
> +                    if (ret < 0) {
> +                        return ret;
> +                    }
> +                    fragment_timeline_node = xmlNextElementSibling(fragment_timeline_node);
> +                }
> +            }
> +        } else if (representation_baseurl_node && !representation_segmentlist_node) {
> +            seg = av_mallocz(sizeof(struct fragment));
> +            if (!seg) {
> +                ret = AVERROR(ENOMEM);
> +                goto end;
> +            }
> +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);
> +            seg->size = -1;
> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> +        } else if (!representation_baseurl_node && representation_segmentlist_node) {
> +            // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
> +            // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
> +            xmlNodePtr fragmenturl_node = NULL;
> +            duration_val = xmlGetProp(representation_segmentlist_node, "duration");
> +            timescale_val = xmlGetProp(representation_segmentlist_node, "timescale");
> +            if (duration_val) {
> +                rep->fragment_duration = (int64_t) atoll((const char *)duration_val);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) atoll((const char *)timescale_val);
> +                xmlFree(timescale_val);
> +            }
> +            fragmenturl_node = xmlFirstElementChild(representation_segmentlist_node);
> +            while (fragmenturl_node) {
> +                ret = parse_manifest_segmenturlnode(s, rep, fragmenturl_node, baseurl_nodes, rep_id_val, rep_bandwidth_val);
> +                if (ret < 0) {
> +                    return ret;
> +                }
> +                fragmenturl_node = xmlNextElementSibling(fragmenturl_node);
> +            }
> +        } else {
> +            free_representation(rep);
> +            rep = NULL;
> +            av_log(s, AV_LOG_ERROR, "Unknown format of Representation node id[%s] \n", (const char *)rep_id_val);
> +        }
> +
> +        if (rep) {
> +            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
> +                rep->fragment_timescale = 1;
> +            if (type == AVMEDIA_TYPE_VIDEO) {
> +                rep->rep_idx = video_rep_idx;
> +                c->cur_video = rep;
> +            } else {
> +                rep->rep_idx = audio_rep_idx;
> +                c->cur_audio = rep;
> +            }
> +        }
> +    }
> +
> +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
> +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
> +
> +end:
> +    if (rep_id_val)
> +        xmlFree(rep_id_val);
> +    if (rep_bandwidth_val)
> +        xmlFree(rep_bandwidth_val);
> +
> +    return ret;
> +}
> +
> +static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, xmlNodePtr period_baseurl_node)
> +{
> +    int ret = 0;
> +    xmlNodePtr fragment_template_node = NULL;
> +    xmlNodePtr content_component_node = NULL;
> +    xmlNodePtr adaptionset_baseurl_node = NULL;
> +    xmlNodePtr node = NULL;
> +
> +    node = xmlFirstElementChild(adaptionset_node);
> +    while (node) {
> +        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate")) {
> +            fragment_template_node = node;
> +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"ContentComponent")) {
> +            content_component_node = node;
> +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
> +            adaptionset_baseurl_node = node;
> +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"Representation")) {
> +            ret = parse_manifest_representation(s, url, node,
> +                                                 adaptionset_node,
> +                                                 mpd_baseurl_node,
> +                                                 period_baseurl_node,
> +                                                 fragment_template_node,
> +                                                 content_component_node,
> +                                                 adaptionset_baseurl_node);
> +            if (ret < 0) {
> +                return ret;
> +            }
> +        }
> +        node = xmlNextElementSibling(node);
> +    }
> +    return 0;
> +}
> +
> +
> +static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
> +{
> +    DASHContext *c = s->priv_data;
> +    int ret = 0;
> +    int close_in = 0;
> +    uint8_t *new_url = NULL;
> +    int64_t filesize = 0;
> +    char *buffer = NULL;
> +    AVDictionary *opts = NULL;
> +    xmlDoc *doc = NULL;
> +    xmlNodePtr root_element = NULL;
> +    xmlNodePtr node = NULL;
> +    xmlNodePtr period_node = NULL;
> +    xmlNodePtr mpd_baseurl_node = NULL;
> +    xmlNodePtr period_baseurl_node = NULL;
> +    xmlNodePtr adaptionset_node = NULL;
> +    xmlAttrPtr attr = NULL;
> +    xmlChar *val  = NULL;
> +    uint32_t perdiod_duration_sec = 0;
> +    uint32_t perdiod_start_sec = 0;
> +    int32_t audio_rep_idx = 0;
> +    int32_t video_rep_idx = 0;
> +
> +    if (!in) {
> +        close_in = 1;
> +        /* This is XML manifest there is no need to set range header */
> +        av_dict_set(&opts, "seekable", "0", 0);
> +        // broker prior HTTP options that should be consistent across requests
> +        av_dict_set(&opts, "user-agent", c->user_agent, 0);
> +        av_dict_set(&opts, "cookies", c->cookies, 0);
> +        av_dict_set(&opts, "headers", c->headers, 0);
> +
> +        ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
> +        av_dict_free(&opts);
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) {
> +        c->base_url = av_strdup(new_url);
> +    } else {
> +        c->base_url = av_strdup(url);
> +    }
> +
> +    filesize = avio_size(in);
> +    if (filesize <= 0){

Space between ) and { (this occurs a few times).

> +        filesize = 8 * 1024;
> +    }
> +
> +    buffer = av_mallocz(filesize);
> +    if (!buffer) {
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    filesize = avio_read(in, buffer, filesize);
> +    if (filesize > 0) {
> +        LIBXML_TEST_VERSION
> +
> +        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
> +        root_element = xmlDocGetRootElement(doc);
> +        node = root_element;
> +
> +        if (!node) {
> +            ret = AVERROR_INVALIDDATA;
> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing root node\n", url);
> +            goto cleanup;
> +        }
> +
> +        if (node->type != XML_ELEMENT_NODE ||
> +            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
> +            ret = AVERROR_INVALIDDATA;
> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root node name[%s] type[%d]\n", url, node->name, (int)node->type);
> +            goto cleanup;
> +        }
> +
> +        val = xmlGetProp(node, "type");
> +        if (!val) {
> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing type attrib\n", url);
> +            ret = AVERROR_INVALIDDATA;
> +            goto cleanup;
> +        }
> +        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
> +            c->is_live = 1;
> +        xmlFree(val);
> +
> +        attr = node->properties;
> +        while (attr) {
> +            val = xmlGetProp(node, attr->name);
> +
> +            if (!xmlStrcmp(attr->name, (const xmlChar *)"availabilityStartTime")) {
> +                c->availability_start_time_sec = GetUTCDateTimeInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
> +                c->publish_time_sec = GetUTCDateTimeInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
> +                c->minimum_update_period_sec = GetDurationInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
> +                c->time_shift_buffer_depth_sec = GetDurationInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
> +                c->min_buffer_time_sec = GetDurationInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
> +                c->suggested_presentation_delay_sec = GetDurationInSec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
> +                c->media_presentation_duration_sec = GetDurationInSec(s, (const char *)val);
> +            }
> +            attr = attr->next;
> +            xmlFree(val);
> +        }
> +
> +        mpd_baseurl_node = findChildNodeByName(node, "BaseURL");
> +
> +        // at now we can handle only one period, with the longest duration
> +        node = xmlFirstElementChild(node);
> +        while (node) {
> +            if (!xmlStrcmp(node->name, (const xmlChar *)"Period")) {
> +                perdiod_duration_sec = 0;
> +                perdiod_start_sec = 0;
> +                attr = node->properties;
> +                while (attr) {
> +                    val = xmlGetProp(node, attr->name);
> +                    if (!xmlStrcmp(attr->name, (const xmlChar *)"duration")) {
> +                        perdiod_duration_sec = GetDurationInSec(s, (const char *)val);
> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
> +                        perdiod_start_sec = GetDurationInSec(s, (const char *)val);
> +                    }
> +                    attr = attr->next;
> +                    xmlFree(val);
> +                }
> +                if ((perdiod_duration_sec) >= (c->period_duration_sec)) {
> +                    period_node = node;
> +                    c->period_duration_sec = perdiod_duration_sec;
> +                    c->period_start_sec = perdiod_start_sec;
> +                    if (c->period_start_sec > 0)
> +                        c->media_presentation_duration_sec = c->period_duration_sec;
> +                }
> +            }
> +            node = xmlNextElementSibling(node);
> +        }
> +        if (!period_node) {
> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing Period node\n", url);
> +            ret = AVERROR_INVALIDDATA;
> +            goto cleanup;
> +        }
> +        // explore AdaptationSet
> +        adaptionset_node = xmlFirstElementChild(period_node);
> +        while (adaptionset_node) {
> +            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"BaseURL")) {
> +                period_baseurl_node = adaptionset_node;
> +            } else if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"AdaptationSet")) {
> +                parse_manifest_adaptationset(s, url, adaptionset_node, mpd_baseurl_node, period_baseurl_node);
> +            }
> +            adaptionset_node = xmlNextElementSibling(adaptionset_node);
> +        }
> +        if (c->cur_video) {
> +            c->cur_video->rep_count = video_rep_idx;
> +            c->cur_video->fix_multiple_stsd_order = 1;
> +            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n", (int)c->cur_video->rep_idx);
> +            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n", (int)video_rep_idx);
> +        }
> +        if (c->cur_audio) {
> +            c->cur_audio->rep_count = audio_rep_idx;
> +        }
> +
> +cleanup:
> +        /*free the document */
> +        xmlFreeDoc(doc);
> +        xmlCleanupParser();
> +    } else {
> +        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
> +        ret = AVERROR_INVALIDDATA;
> +    }
> +
> +    av_free(new_url);
> +    av_free(buffer);
> +    if (close_in) {
> +        avio_close(in);
> +    }
> +    return ret;
> +}
> +
> +static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +
> +    if (c->is_live) {
> +        if (!pls->fragment_duration) {
> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
> +            return pls->first_seq_no;
> +        }
> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec) - c->presentation_delay_sec) * pls->fragment_timescale) / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no;
> +    }
> +    return num;
> +}
> +
> +static int64_t calc_min_seg_no(AVFormatContext *s, struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +
> +    if (c->is_live) {
> +        if (!pls->fragment_duration) {
> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
> +            return pls->first_seq_no;
> +        }
> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) * pls->fragment_timescale)  / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no;
> +    }
> +    return num;
> +}
> +
> +static int64_t calc_max_seg_no(AVFormatContext *s, struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +    int64_t i = 0;
> +
> +    if (c->is_live) {
> +        if (!pls->fragment_duration) {
> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
> +            return pls->first_seq_no;
> +        }
> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec)) * pls->fragment_timescale)  / pls->fragment_duration;
> +    } else {
> +        if (pls->n_fragments) {
> +            num = pls->first_seq_no + pls->n_fragments - 1;
> +        } else if (pls->n_timelines) {
> +            num = pls->first_seq_no + pls->n_timelines - 1;
> +            for (i = 0; i < pls->n_timelines; ++i) {
> +                num += pls->timelines[i]->r;
> +            }
> +        } else {
> +            num = pls->first_seq_no + (c->media_presentation_duration_sec * pls->fragment_timescale) / pls->fragment_duration;
> +        }
> +    }
> +
> +    return num;
> +}
> +
> +static int64_t get_fragment_start_time(struct representation *pls, int64_t cur_seq_no)
> +{
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +    int64_t startTime = 0;
> +
> +    if (pls->n_timelines) {
> +        for (i = 0; i < pls->n_timelines; ++i) {
> +            if (pls->timelines[i]->t > 0) {
> +                startTime = pls->timelines[i]->t;
> +            }
> +            if (num == cur_seq_no)
> +                goto finish;
> +            startTime += pls->timelines[i]->d;
> +            for (j = 0; j < pls->timelines[i]->r; ++j) {
> +                num++;
> +                if (num == cur_seq_no)
> +                    goto finish;
> +                startTime += pls->timelines[i]->d;
> +            }
> +            num++;
> +        }
> +    }
> +
> +finish:
> +    return startTime;
> +}
> +
> +static struct fragment *get_current_fragment(struct representation *pls)
> +{
> +    int64_t tmp_val = 0;
> +    int64_t min_seq_no = 0;
> +    int64_t max_seq_no = 0;
> +    char buffer[1024];
> +    char *tmp_str = NULL;
> +    struct fragment *seg = NULL;
> +    struct fragment *seg_ptr = NULL;
> +    DASHContext *c = pls->parent->priv_data;
> +
> +    if (pls->n_fragments > 0) {
> +        if (pls->cur_seq_no < pls->n_fragments) {
> +            seg_ptr = pls->fragments[pls->cur_seq_no];
> +            seg = av_mallocz(sizeof(struct fragment));
> +            if (!seg) {
> +                return NULL;
> +            }
> +            seg->url = av_strdup(seg_ptr->url);
> +            if (!seg->url) {
> +                av_free(seg);
> +                return NULL;
> +            }
> +            seg->size = seg_ptr->size;
> +            seg->url_offset = seg_ptr->url_offset;
> +            return seg;
> +        }
> +    }
> +    if (c->is_live) {
> +        while (1) {
> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> +            max_seq_no = calc_max_seg_no(pls->parent, pls);
> +
> +            if (pls->cur_seq_no <= min_seq_no) {
> +                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment: cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> +                       (int64_t)pls->cur_seq_no, min_seq_no, max_seq_no, (int)pls->rep_idx);
> +                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
> +            } else if (pls->cur_seq_no > max_seq_no) {
> +                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment: min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> +                       min_seq_no, max_seq_no, (int)pls->rep_idx);
> +                av_usleep(1000);
> +                continue;
> +            }
> +            break;
> +        }
> +        seg = av_mallocz(sizeof(struct fragment));
> +        if (!seg) {
> +            return NULL;
> +        }
> +    } else if (pls->cur_seq_no <= pls->last_seq_no) {
> +        seg = av_mallocz(sizeof(struct fragment));
> +        if (!seg) {
> +            return NULL;
> +        }
> +    }
> +    if (seg) {
> +        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
> +            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
> +            if (av_get_frame_filename(buffer, sizeof(buffer), pls->url_template, (int64_t)tmp_val) < 0) {

It's not a security issue like with printf functions, but you shouldn't use av_get_frame_filename() with arbitrary format-string input either, as it will fail if the input already contained a '%' character. I also don't understand how this is supposed to work, since your source string (url_template) shouldn't contain a "%d"?

> +                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment filename template %s\n", pls->url_template);
> +                return NULL;
> +            }
> +
> +            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
> +                tmp_str = replace_template_str(buffer, "$Number$");
> +            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME){
> +                tmp_str = replace_template_str(buffer, "$Time$");
> +            } else {
> +                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type [%d]\n", pls->tmp_url_type);

Since this shouldn't be possible, an av_assert0 is applicable; no need to log the value.

> +                return NULL;
> +            }
> +
> +            seg->url = av_strdup(tmp_str);
> +            if (!seg->url) {
> +                av_free(tmp_str);
> +                return NULL;
> +            }
> +        } else {
> +            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to resolve template url '%s'\n", pls->url_template);
> +            seg->url = av_strdup(pls->url_template);
> +            if (!seg->url) {
> +                return NULL;
> +            }
> +        }
> +        seg->size = -1;
> +    }
> +
> +    av_free(tmp_str);
> +    return seg;
> +}
> +
> +enum ReadFromURLMode {
> +    READ_NORMAL,
> +    READ_COMPLETE,
> +};
> +
> +static int read_from_url(struct representation *pls, struct fragment *seg,
> +                         uint8_t *buf, int buf_size,
> +                         enum ReadFromURLMode mode)
> +{
> +    int ret;
> +
> +    /* limit read if the fragment was only a part of a file */
> +    if (seg->size >= 0)
> +        buf_size = FFMIN(buf_size, pls->cur_seg_size - pls->cur_seg_offset);
> +
> +    if (mode == READ_COMPLETE) {
> +        ret = avio_read(pls->input, buf, buf_size);
> +        if (ret < buf_size) {
> +            av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment.\n");
> +        }
> +    } else {
> +        ret = avio_read(pls->input, buf, buf_size);
> +    }
> +    if (ret > 0)
> +        pls->cur_seg_offset += ret;
> +
> +    return ret;
> +}
> +
> +static int open_input(DASHContext *c, struct representation *pls, struct fragment *seg)
> +{
> +    AVDictionary *opts = NULL;
> +    char url[MAX_URL_SIZE];
> +    int ret;
> +
> +    // broker prior HTTP options that should be consistent across requests
> +    av_dict_set(&opts, "user-agent", c->user_agent, 0);
> +    av_dict_set(&opts, "cookies", c->cookies, 0);
> +    av_dict_set(&opts, "headers", c->headers, 0);
> +    if (c->is_live) {
> +        av_dict_set(&opts, "seekable", "0", 0);
> +    }
> +
> +    if (seg->size >= 0) {
> +        /* try to restrict the HTTP request to the part we want
> +         * (if this is in fact a HTTP request) */
> +        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
> +        av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0);
> +    }
> +
> +    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
> +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s', offset %"PRId64", playlist %d\n",
> +           url, seg->url_offset, pls->rep_idx);
> +    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts, NULL);
> +    if (ret < 0) {
> +        goto cleanup;
> +    }
> +
> +    /* Seek to the requested position. If this was a HTTP request, the offset
> +     * should already be where want it to, but this allows e.g. local testing
> +     * without a HTTP server. */
> +    if (!ret && seg->url_offset) {
> +        int64_t seekret = avio_seek(pls->input, seg->url_offset, SEEK_SET);
> +        if (seekret < 0) {
> +            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
> +            ret = (int) seekret;
> +            ff_format_io_close(pls->parent, &pls->input);
> +        }
> +    }
> +
> +cleanup:
> +    av_dict_free(&opts);
> +    pls->cur_seg_offset = 0;
> +    pls->cur_seg_size = seg->size;
> +    return ret;
> +}
> +
> +static int update_init_section(struct representation *pls)
> +{
> +    static const int max_init_section_size = 1024*1024;
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t sec_size = 0;
> +    int64_t urlsize = 0;
> +    int ret = 0;
> +
> +    /* read init section only once per representation */
> +    if (!pls->init_section || pls->init_sec_buf) {
> +        return 0;
> +    }
> +
> +    ret = open_input(c, pls, pls->init_section);
> +    if (ret < 0) {
> +        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an initialization section in playlist %d\n", pls->rep_idx);
> +        return ret;
> +    }
> +
> +    if (pls->init_section->size >= 0) {
> +        sec_size = pls->init_section->size;
> +    } else if ((urlsize = avio_size(pls->input)) >= 0) {
> +        sec_size = urlsize;
> +    } else {
> +        sec_size = max_init_section_size;
> +    }
> +    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization section of size %"PRId64"\n", sec_size);
> +    sec_size = FFMIN(sec_size, max_init_section_size);
> +    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
> +    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf, pls->init_sec_buf_size, READ_COMPLETE);
> +    ff_format_io_close(pls->parent, &pls->input);
> +    if (ret < 0)
> +        return ret;
> +
> +    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
> +        uint8_t **stsd_entries = NULL;
> +        int *stsd_entries_size = NULL;
> +        int i = 4;
> +
> +        while (i <= (ret - 4)) {
> +            // find start stsd atom
> +            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
> +                /* 1B version
> +                 * 3B flags
> +                 * 4B num of entries */
> +                int stsd_first_offset = i + 8;
> +                int stsd_offset = 0;
> +                int j = 0;
> +                uint32_t stsd_count = AV_RB32(pls->init_sec_buf + stsd_first_offset);
> +                stsd_first_offset += 4;
> +                if (stsd_count != pls->rep_count) {
> +                    i++;
> +                    continue;
> +                }
> +                // find all stsd entries
> +                stsd_entries = av_mallocz_array(stsd_count, sizeof(*stsd_entries));
> +                stsd_entries_size = av_mallocz_array(stsd_count, sizeof(*stsd_entries_size));
> +                for (j = 0; j < stsd_count; ++j) {
> +                    /* 4B - size
> +                     * 4B - format */
> +                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf + stsd_first_offset + stsd_offset);
> +                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
> +                    memcpy(stsd_entries[j], pls->init_sec_buf + stsd_first_offset + stsd_offset, stsd_entries_size[j]);
> +                    stsd_offset += stsd_entries_size[j];
> +                }
> +                // reorder stsd entries
> +                // as first put stsd entry for current representation
> +                j = pls->rep_idx;
> +                stsd_offset = stsd_first_offset;
> +                memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
> +                stsd_offset += stsd_entries_size[j];
> +                for (j = 0; j < stsd_count; ++j) {
> +                    if (j != pls->rep_idx) {
> +                        memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
> +                        stsd_offset += stsd_entries_size[j];
> +                    }
> +                    av_free(stsd_entries[j]);
> +                }
> +                av_freep(&stsd_entries);
> +                av_freep(&stsd_entries_size);
> +                break;
> +            }
> +            i++;
> +        }
> +    }
> +
> +    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section size[%d]\n", pls, (int)ret);
> +    pls->init_sec_data_len = ret;
> +    pls->init_sec_buf_read_offset = 0;
> +
> +    return 0;
> +}
> +
> +static int64_t seek_data(void *opaque, int64_t offset, int whence)
> +{
> +    struct representation *v = opaque;
> +    if (v->n_fragments && !v->init_sec_data_len) {
> +        return avio_seek(v->input, offset, whence);
> +    }
> +
> +    return AVERROR(ENOSYS);
> +}
> +
> +static int read_data(void *opaque, uint8_t *buf, int buf_size)
> +{
> +    int ret = 0;
> +    struct representation *v = opaque;
> +    DASHContext *c = v->parent->priv_data;
> +
> +restart:
> +    if (!v->input) {
> +        free_fragment(&v->cur_seg);
> +        v->cur_seg = get_current_fragment(v);
> +        if (!v->cur_seg) {
> +            ret = AVERROR_EOF;
> +            goto end;
> +        }
> +
> +        /* load/update Media Initialization Section, if any */
> +        ret = update_init_section(v);
> +        if (ret)
> +            goto end;
> +
> +        ret = open_input(c, v, v->cur_seg);
> +        if (ret < 0) {
> +            if (ff_check_interrupt(c->interrupt_callback)) {
> +                goto end;
> +                ret = AVERROR_EXIT;
> +            }
> +            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment of playlist %d\n", v->rep_idx);
> +            v->cur_seq_no++;
> +            goto restart;
> +        }
> +    }
> +
> +    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
> +        /* Push init section out first before first actual fragment */
> +        int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
> +        memcpy(buf, v->init_sec_buf, copy_size);
> +        v->init_sec_buf_read_offset += copy_size;
> +        ret = copy_size;
> +        goto end;
> +    }
> +
> +    /* check the v->cur_seg, if it is null, get current and double check if the new v->cur_seg*/
> +    if (!v->cur_seg) {
> +        v->cur_seg = get_current_fragment(v);
> +    }
> +    if (!v->cur_seg) {
> +        ret = AVERROR_EOF;
> +        goto end;
> +    }
> +    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
> +    if (ret > 0)
> +        goto end;
> +    ff_format_io_close(v->parent, &v->input);
> +    v->cur_seq_no++;
> +    goto restart;
> +
> +end:
> +    return ret;
> +}
> +
> +static int save_avio_options(AVFormatContext *s)
> +{
> +    DASHContext *c = s->priv_data;
> +    const char *opts[] = { "headers", "user_agent", "user-agent", "cookies", NULL }, **opt = opts;
> +    uint8_t *buf = NULL;
> +    int ret = 0;
> +
> +    while (*opt) {
> +        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
> +            if (buf) {
> +                ret = av_dict_set(&c->avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL);
> +                if (ret < 0)
> +                    return ret;
> +            }
> +        }
> +        opt++;
> +    }
> +
> +    return ret;
> +}
> +
> +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
> +                          int flags, AVDictionary **opts)
> +{
> +    av_log(s, AV_LOG_ERROR,
> +           "A HDS playlist item '%s' referred to an external file '%s'. "
> +           "Opening this file was forbidden for security reasons\n",
> +           s->filename, url);
> +    return AVERROR(EPERM);
> +}
> +
> +static int reopen_demux_for_component(AVFormatContext *s, struct representation *pls)
> +{
> +    DASHContext *c = s->priv_data;
> +    AVInputFormat *in_fmt = NULL;
> +    AVDictionary  *in_fmt_opts = NULL;
> +    uint8_t *avio_ctx_buffer  = NULL;
> +    int ret = 0;
> +
> +    if (pls->ctx) {
> +        /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
> +        av_freep(&pls->pb.buffer);
> +        memset(&pls->pb, 0x00, sizeof(AVIOContext));
> +        pls->ctx->pb = NULL;
> +        avformat_close_input(&pls->ctx);
> +        pls->ctx = NULL;
> +    }
> +    if (!(pls->ctx = avformat_alloc_context())) {
> +        ret = AVERROR(ENOMEM);
> +        goto fail;
> +    }
> +
> +    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
> +    if (!avio_ctx_buffer ){
> +        ret = AVERROR(ENOMEM);
> +        avformat_free_context(pls->ctx);
> +        pls->ctx = NULL;
> +        goto fail;
> +    }
> +    if (c->is_live) {
> +        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
> +    } else {
> +        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
> +    }
> +    pls->pb.seekable = 0;
> +
> +    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
> +        goto fail;
> +
> +    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
> +    pls->ctx->probesize = 1024 * 4;
> +    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
> +    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
> +    if (ret < 0) {
> +        av_log(s, AV_LOG_ERROR, "Error when loading first fragment, playlist %d\n", (int)pls->rep_idx);
> +        avformat_free_context(pls->ctx);
> +        pls->ctx = NULL;
> +        goto fail;
> +    }
> +
> +    pls->ctx->pb = &pls->pb;
> +    pls->ctx->io_open  = nested_io_open;
> +
> +    // provide additional information from mpd if available
> +    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
> +    av_dict_free(&in_fmt_opts);
> +    if (ret < 0)
> +        goto fail;
> +    if (pls->n_fragments) {
> +        ret = avformat_find_stream_info(pls->ctx, NULL);
> +        if (ret < 0)
> +            goto fail;
> +    }
> +
> +fail:
> +    return ret;
> +}
> +
> +static int open_demux_for_component(AVFormatContext *s, struct representation *pls)
> +{
> +    int ret = 0;
> +    int i;
> +
> +    pls->parent = s;
> +    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
> +    pls->last_seq_no = calc_max_seg_no(s, pls);
> +
> +    ret = reopen_demux_for_component(s, pls);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +    for (i = 0; i < pls->ctx->nb_streams; i++) {
> +        AVStream *st = avformat_new_stream(s, NULL);
> +        AVStream *ist = pls->ctx->streams[i];
> +        if (!st) {
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +        st->id = i;
> +        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->codecpar);
> +        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
> +    }
> +
> +    return 0;
> +fail:
> +    return ret;
> +}
> +
> +static int dash_read_header(AVFormatContext *s)
> +{
> +    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> +    DASHContext *c = s->priv_data;
> +    int ret = 0;
> +    int stream_index = 0;
> +
> +    c->interrupt_callback = &s->interrupt_callback;
> +    // if the URL context is good, read important options we must broker later
> +    if (u) {
> +        update_options(&c->user_agent, "user-agent", u);
> +        update_options(&c->cookies, "cookies", u);
> +        update_options(&c->headers, "headers", u);
> +    }
> +
> +    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
> +        goto fail;
> +
> +    if ((ret = save_avio_options(s)) < 0)
> +        goto fail;
> +
> +    /* If this isn't a live stream, fill the total duration of the
> +     * stream. */
> +    if (!c->is_live) {
> +        s->duration = (int64_t) c->media_presentation_duration_sec * AV_TIME_BASE;
> +    }
> +
> +    /* Open the demuxer for curent video and current audio components if available */
> +    if (!ret && c->cur_video) {
> +        ret = open_demux_for_component(s, c->cur_video);
> +        if (!ret) {
> +            c->cur_video->stream_index = stream_index;
> +            ++stream_index;
> +        } else {
> +            free_representation(c->cur_video);
> +            c->cur_video = NULL;
> +        }
> +    }
> +
> +    if (!ret && c->cur_audio) {
> +        ret = open_demux_for_component(s, c->cur_audio);
> +        if (!ret) {
> +            c->cur_audio->stream_index = stream_index;
> +            ++stream_index;
> +        } else {
> +            free_representation(c->cur_audio);
> +            c->cur_audio = NULL;
> +        }
> +    }
> +
> +    if (!stream_index) {
> +        ret = AVERROR_INVALIDDATA;
> +        goto fail;
> +    }
> +
> +    /* Create a program */
> +    if (!ret) {
> +        AVProgram *program;
> +        program = av_new_program(s, 0);
> +        if (!program) {
> +            goto fail;
> +        }
> +
> +        if (c->cur_video) {
> +            av_program_add_stream_index(s, 0, c->cur_video->stream_index);
> +        }
> +
> +        if (c->cur_audio) {
> +            av_program_add_stream_index(s, 0, c->cur_audio->stream_index);
> +        }
> +    }
> +
> +    return 0;
> +fail:
> +    return ret;
> +}
> +
> +static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> +    DASHContext *c = s->priv_data;
> +    int ret = 0;
> +    struct representation *cur = NULL;
> +
> +    if (!c->cur_audio && !c->cur_video) {
> +        return AVERROR_INVALIDDATA;
> +    }
> +    if (c->cur_audio && !c->cur_video) {
> +        cur = c->cur_audio;
> +    } else if (!c->cur_audio && c->cur_video) {
> +        cur = c->cur_video;
> +    } else if (c->cur_video->cur_timestamp < c->cur_audio->cur_timestamp) {
> +        cur = c->cur_video;
> +    } else {
> +        cur = c->cur_audio;
> +    }
> +
> +    if (cur->ctx) {
> +        ret = av_read_frame(cur->ctx, &cur->pkt);
> +        if (ret < 0) {
> +            av_packet_unref(&cur->pkt);
> +        } else {
> +            /* If we got a packet, return it */
> +            *pkt = cur->pkt;
> +            cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
> +            pkt->stream_index = cur->stream_index;
> +            reset_packet(&cur->pkt);
> +            return 0;
> +        }
> +    }
> +    return AVERROR_EOF;
> +}
> +
> +static int dash_close(AVFormatContext *s)
> +{
> +    DASHContext *c = s->priv_data;
> +
> +    if (c->cur_audio) {
> +        free_representation(c->cur_audio);
> +    }
> +
> +    if (c->cur_video) {
> +        free_representation(c->cur_video);
> +    }
> +
> +    av_freep(&c->cookies);
> +    av_freep(&c->user_agent);
> +    av_dict_free(&c->avio_opts);
> +    av_freep(&c->base_url);
> +    return 0;
> +}
> +
> +static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t seek_pos_msec, int flags)
> +{
> +    int ret = 0;
> +    int i = 0;
> +    int j = 0;
> +    int64_t duration = 0;
> +
> +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms], playlist %d\n", seek_pos_msec, pls->rep_idx);
> +
> +    // single fragment mode
> +    if (pls->n_fragments) {
> +        pls->cur_timestamp = 0;
> +        pls->cur_seg_offset = 0;
> +        ff_read_frame_flush(pls->ctx);
> +        return av_seek_frame(pls->ctx, -1, seek_pos_msec*1000, flags);
> +    }
> +
> +    if (pls->input)
> +        ff_format_io_close(pls->parent, &pls->input);
> +
> +    // find the nearest fragment
> +    if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
> +        int64_t num = pls->first_seq_no;
> +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline start n_timelines[%d] last_seq_no[%"PRId64"], playlist %d.\n",
> +               (int)pls->n_timelines, (int64_t)pls->last_seq_no, (int)pls->rep_idx);
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            if (pls->timelines[i]->t > 0) {
> +                duration = pls->timelines[i]->t;
> +            }
> +            duration += pls->timelines[i]->d;
> +            if (seek_pos_msec < ((duration * 1000) /  pls->fragment_timescale)) {
> +                goto set_seq_num;
> +            }
> +            for (j = 0; j < pls->timelines[i]->r; j++) {
> +                duration += pls->timelines[i]->d;
> +                num++;
> +                if (seek_pos_msec < ((duration * 1000) /  pls->fragment_timescale)) {
> +                    goto set_seq_num;
> +                }
> +            }
> +            num++;
> +        }
> +
> +set_seq_num:
> +        pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no : num;
> +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
> +               (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
> +    } else if (pls->fragment_duration > 0) {
> +        pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec * pls->fragment_timescale) / pls->fragment_duration) / 1000;
> +    } else {
> +        av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing fragment_duration\n");
> +        pls->cur_seq_no = pls->first_seq_no;
> +    }
> +    pls->cur_timestamp = 0;
> +    pls->cur_seg_offset = 0;
> +    pls->init_sec_buf_read_offset = 0;
> +    ret = reopen_demux_for_component(s, pls);
> +
> +    return ret;
> +}
> +
> +static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
> +{
> +    int ret = 0;
> +    DASHContext *c = s->priv_data;
> +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
> +                                         s->streams[stream_index]->time_base.den,
> +                                         flags & AVSEEK_FLAG_BACKWARD ?
> +                                         AV_ROUND_DOWN : AV_ROUND_UP);
> +    if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
> +        return AVERROR(ENOSYS);
> +    if (c->cur_audio) {
> +        ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
> +    }
> +    if (!ret && c->cur_video) {
> +        ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
> +    }
> +    return ret;
> +}
> +
> +static int dash_probe(AVProbeData *p)
> +{
> +    if (!av_stristr(p->buf, "<MPD"))
> +        return 0;
> +
> +    if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
> +        av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
> +        av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
> +        av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
> +        return AVPROBE_SCORE_MAX;
> +    }
> +    if (av_stristr(p->buf, "dash:profile")) {
> +        return AVPROBE_SCORE_MAX >> 1;
> +    }
> +
> +    return 0;
> +}
> +
> +#define OFFSET(x) offsetof(DASHContext, x)
> +#define FLAGS AV_OPT_FLAG_DECODING_PARAM
> +static const AVOption dash_options[] = {
> +    {NULL}
> +};
> +
> +static const AVClass dash_class = {
> +    .class_name = "dash",
> +    .item_name  = av_default_item_name,
> +    .option     = dash_options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +AVInputFormat ff_dash_demuxer = {
> +    .name           = "dash",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
> +    .priv_class     = &dash_class,
> +    .priv_data_size = sizeof(DASHContext),
> +    .read_probe     = dash_probe,
> +    .read_header    = dash_read_header,
> +    .read_packet    = dash_read_packet,
> +    .read_close     = dash_close,
> +    .read_seek      = dash_read_seek,
> +    .flags          = AVFMT_NO_BYTE_SEEK,
> +};
> -- 
> 2.10.1 (Apple Git-78)
> 
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Steven Liu March 27, 2017, 6:52 a.m. UTC | #2
2017-03-27 8:39 GMT+08:00 Rodger Combs <rodger.combs@gmail.com>:

>
> > On Mar 26, 2017, at 19:10, Steven Liu <lq@chinaffmpeg.org> wrote:
> >
> > v2 fixed:
> > 1. from autodetect to disabled
> > 2. from camelCase code style to ffmpeg code style
> > 3. from RepType to AVMediaType
> > 4. fix variable typo
> > 5. change time value from uint32_t to uint64_t
> > 6. removed be used once API
> > 7. change 'time(NULL)`, except it is not 2038-safe.' to av_gettime and
> > av_timegm
> > 8. merge complex free operation to free_fragment
> > 9. use API from snprintf to av_asprintf
> >
> > v3 fixed:
> > 1. fix typo from --enabled-xml2 to --enable-xml2
> >
> > v4 fixed:
> > 1. from --enable-xml2 to --enable-libxml2
> > 2. move system includes to top
> > 3. remove nouse includes
> > 4. rename enum name
> > 5. add a trailing comma for the last entry enum
> > 6. fix comment typo
> > 7. add const to DASHContext class front
> > 8. check sscanf if return arguments and give warning message when error
> > 9. check validity before free seg->url and seg
> > 10. check if the val is null, before use atoll
> >
> > v5 fixed:
> > 1. fix typo from mainifest to manifest
> >
> > v6 fixed:
> > 1. from realloc to av_realloc
> > 2. from free to av_free
> >
> > v7 fixed:
> > 1. remove the -lxml2 from configure when require_pkg_config
> >
> > v8 fixed:
> > 1. fix replace filename template by av_asprintf secure problem
> >
> > v9 modified:
> > 1. make manifest parser clearly
> >
> > Reviewed-by: Clément Bœsch <u@pkh.me>
> > Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
> > Reviewed-by: Carl Eugen Hoyos <cehoyos@ag.or.at>
> > Reviewed-by: Rodger Combs <rodger.combs@gmail.com>
> > Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
> > Reviewed-by: Nicolas George <george@nsup.org>
> > Reviewed-by: Ricardo Constantino <wiiaboo@gmail.com>
> > Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> > ---
> > configure                |    4 +
> > libavformat/Makefile     |    1 +
> > libavformat/allformats.c |    2 +-
> > libavformat/dashdec.c    | 1842 ++++++++++++++++++++++++++++++
> ++++++++++++++++
> > 4 files changed, 1848 insertions(+), 1 deletion(-)
> > create mode 100644 libavformat/dashdec.c
> >
> > diff --git a/configure b/configure
> > index 9cc7e7d..4a6fe8a 100755
> > --- a/configure
> > +++ b/configure
> > @@ -273,6 +273,7 @@ External library support:
> >   --enable-libxcb-shape    enable X11 grabbing shape rendering
> [autodetect]
> >   --enable-libxvid         enable Xvid encoding via xvidcore,
> >                            native MPEG-4/Xvid encoder exists [no]
> > +  --enable-libxml2            enable XML parsing using the C library
> libxml2 [no]
> >   --enable-libzimg         enable z.lib, needed for zscale filter [no]
> >   --enable-libzmq          enable message passing via libzmq [no]
> >   --enable-libzvbi         enable teletext support via libzvbi [no]
> > @@ -1574,6 +1575,7 @@ EXTERNAL_LIBRARY_LIST="
> >     libvpx
> >     libwavpack
> >     libwebp
> > +    libxml2
> >     libzimg
> >     libzmq
> >     libzvbi
> > @@ -2906,6 +2908,7 @@ avi_muxer_select="riffenc"
> > caf_demuxer_select="iso_media riffdec"
> > caf_muxer_select="iso_media"
> > dash_muxer_select="mp4_muxer"
> > +dash_demuxer_deps="libxml2"
> > dirac_demuxer_select="dirac_parser"
> > dts_demuxer_select="dca_parser"
> > dtshd_demuxer_select="dca_parser"
> > @@ -5908,6 +5911,7 @@ enabled openssl           && { use_pkg_config
> openssl openssl/ssl.h OPENSSL_init
> >                                check_lib openssl/ssl.h SSL_library_init
> -lssl -lcrypto -lws2_32 -lgdi32 ||
> >                                die "ERROR: openssl not found"; }
> > enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h ||
> disable qtkit_indev; }
> > +enabled libxml2             && require_pkg_config libxml-2.0
> libxml2/libxml/xmlversion.h xmlCheckVersion
> >
> > # libdc1394 check
> > if enabled libdc1394; then
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index f56ef16..d77ffaa 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -134,6 +134,7 @@ OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
> > OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
> > OBJS-$(CONFIG_DATA_MUXER)                += rawenc.o
> > OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o
> > +OBJS-$(CONFIG_DASH_DEMUXER)              += dashdec.o
> > OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
> > OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
> > OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
> > diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> > index 09e62c3..d57314b 100644
> > --- a/libavformat/allformats.c
> > +++ b/libavformat/allformats.c
> > @@ -96,7 +96,7 @@ static void register_all(void)
> >     REGISTER_DEMUXER (CINE,             cine);
> >     REGISTER_DEMUXER (CONCAT,           concat);
> >     REGISTER_MUXER   (CRC,              crc);
> > -    REGISTER_MUXER   (DASH,             dash);
> > +    REGISTER_MUXDEMUX(DASH,             dash);
> >     REGISTER_MUXDEMUX(DATA,             data);
> >     REGISTER_MUXDEMUX(DAUD,             daud);
> >     REGISTER_DEMUXER (DCSTR,            dcstr);
> > diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
> > new file mode 100644
> > index 0000000..a4d3ba2
> > --- /dev/null
> > +++ b/libavformat/dashdec.c
> > @@ -0,0 +1,1842 @@
> > +/*
> > + * 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"
> > +
> > +#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 {
> > +    /* t: 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@t + @d * (@r + 1)).
> > +     * */
> > +    int64_t t;
> > +    /* r: Element or Attribute Name
> > +     * specifies the repeat count of the number of following contiguous
> Segments with
> > +     * the same duration expressed by the value of @d. This value is
> zero-based
> > +     * (e.g. a value of three means four Segments in the contiguous
> series).
> > +     * */
> > +    int32_t r;
> > +    /* d: Element or Attribute Name
> > +     * specifies the Segment duration, in units of the value of the
> @timescale.
> > +     * */
> > +    int64_t d;
> > +};
> > +
> > +enum DASHTmplUrlType {
> > +    TMP_URL_TYPE_UNSPECIFIED,
> > +    TMP_URL_TYPE_NUMBER,
> > +    TMP_URL_TYPE_TIME,
> > +};
> > +
> > +/*
> > + * 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;
> > +    enum DASHTmplUrlType tmp_url_type;
> > +    AVIOContext pb;
> > +    AVIOContext *input;
> > +    AVFormatContext *parent;
> > +    AVFormatContext *ctx;
> > +    AVPacket pkt;
> > +    int rep_idx;
> > +    int rep_count;
> > +    int stream_index;
> > +
> > +    enum AVMediaType type;
> > +    int64_t target_duration;
> > +
> > +    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 fragment_duration;
> > +    int64_t fragment_timescale;
> > +
> > +    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;
> > +    int fix_multiple_stsd_order;
> > +    int64_t cur_timestamp;
> > +};
> > +
> > +typedef struct DASHContext {
> > +    const AVClass *class;
> > +    char *base_url;
> > +    struct representation *cur_video;
> > +    struct representation *cur_audio;
> > +    uint64_t media_presentation_duration_sec;
> > +    uint64_t suggested_presentation_delay_sec;
> > +    uint64_t presentation_delay_sec;
> > +    uint64_t availability_start_time_sec;
> > +    uint64_t publish_time_sec;
> > +    uint64_t minimum_update_period_sec;
> > +    uint64_t time_shift_buffer_depth_sec;
> > +    uint64_t min_buffer_time_sec;
> > +    uint64_t period_duration_sec;
> > +    uint64_t period_start_sec;
> > +    int is_live;
> > +    AVIOInterruptCB *interrupt_callback;
> > +    char *user_agent;                    ///< holds HTTP user agent set
> as an AVOption to the HTTP protocol context
> > +    char *cookies;                       ///< holds HTTP cookie values
> set in either the initial response or as an AVOption to the HTTP protocol
> context
> > +    char *headers;                       ///< holds HTTP headers set as
> an AVOption to the HTTP protocol context
> > +    AVDictionary *avio_opts;
> > +} DASHContext;
> > +
> > +static uint64_t GetCurrentTimeInSec(void)
>
> Code style: "gettime_sec(void)" or the like; not CamelCased.
>
> > +{
> > +    return  av_gettime() / 1000000;
> > +}
> > +
> > +static uint64_t GetUTCDateTimeInSec(AVFormatContext *s, const char
> *datetime)
>
> I still don't understand why av_parse_time isn't applicable here; it
> parses ISO-8601 dates just fine.
>
> > +{
> > +    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, "GetUTCDateTimeInSec get a wrong time
> format\n");
> > +       // return 0; /* parser error */
> > +    }
> > +    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 GetDurationInSec(AVFormatContext *s, const char
> *duration)
>
> Same question here; av_parse_time parses ISO-8601 durations if you set
> duration=1.
>
because i test use av_parse_time look like bellow:
1. add  string "PT2M4.0S" into libavutil/tests/parseutils.c, insert into
    static const char * const duration_string[] = {
        "2:34:56.79",
        "-1:23:45.67",
        "42.1729",
        "-1729.42",
        "12:34",
        "PT2M4.0S"
    };

2. make libavutil/tests/parseutils
3. ./libavutil/tests/parseutils

 output:
localhost:dash StevenLiu$ libavutil/tests/parseutils
objc[70892]: Class SDLTranslatorResponder is implemented in both
/usr/local/lib/libSDL-1.2.0.dylib (0x10d363660) and
/usr/local/lib/libSDL2-2.0.0.dylib (0x10a399db8). One of the two will be
used. Which one is undefined.
Testing av_parse_video_rate()
'-inf' -> -1/0 ERROR
'inf' -> 1/0 ERROR
'nan' -> 0/0 ERROR
'123/0' -> 1/0 ERROR
'-123 / 0' -> -1/0 ERROR
'' -> 0/0 ERROR
'/' -> 0/0 ERROR
' 123  /  321' -> 41/107 OK
'foo/foo' -> 0/0 ERROR
'foo/1' -> 0/0 ERROR
'1/foo' -> 1/0 ERROR
'0/0' -> 0/0 ERROR
'/0' -> 0/0 ERROR
'1/' -> 1/0 ERROR
'1' -> 1/1 OK
'0' -> 0/1 ERROR
'-123/123' -> -1/1 ERROR
'-foo' -> 0/0 ERROR
'123.23' -> 12323/100 OK
'.23' -> 23/100 OK
'-.23' -> -23/100 ERROR
'-0.234' -> -117/500 ERROR
'-0.0000001' -> -1/10000000 ERROR
'  21332.2324   ' -> 917286/43 OK
' -21332.2324   ' -> -917286/43 ERROR

Testing av_parse_color()
bikeshed -> R(80) G(64) B(140) A(59)
RaNdOm -> R(185) G(88) B(148) A(94)
Cannot find color 'foo'
foo -> error
red -> R(255) G(0) B(0) A(255)
Cannot find color 'Red '
Red  -> error
RED -> R(255) G(0) B(0) A(255)
Violet -> R(238) G(130) B(238) A(255)
Yellow -> R(255) G(255) B(0) A(255)
Red -> R(255) G(0) B(0) A(255)
0x000000 -> R(0) G(0) B(0) A(255)
Invalid 0xRRGGBB[AA] color string: '0000000'
0x0000000 -> error
0xff000000 -> R(255) G(0) B(0) A(0)
0x3e34ff -> R(62) G(52) B(255) A(255)
0x3e34ffaa -> R(62) G(52) B(255) A(170)
Invalid 0xRRGGBB[AA] color string: 'ffXXee'
0xffXXee -> error
Invalid 0xRRGGBB[AA] color string: 'foobar'
0xfoobar -> error
Invalid 0xRRGGBB[AA] color string: 'ffffeeeeeeee'
0xffffeeeeeeee -> error
#ff0000 -> R(255) G(0) B(0) A(255)
Invalid 0xRRGGBB[AA] color string: 'ffXX00'
#ffXX00 -> error
ff0000 -> R(255) G(0) B(0) A(255)
Cannot find color 'ffXX00'
ffXX00 -> error
Invalid alpha value specifier 'foo' in 'red@foo'
red@foo -> error
Invalid alpha value specifier '10' in 'random@10'
random@10 -> error
0xff0000@1.0 -> R(255) G(0) B(0) A(255)
Invalid alpha value specifier '' in 'red@'
red@ -> error
Invalid alpha value specifier '0xfff' in 'red@0xfff'
red@0xfff -> error
red@0xf -> R(255) G(0) B(0) A(15)
Invalid alpha value specifier '2' in 'red@2'
red@2 -> error
red@0.1 -> R(255) G(0) B(0) A(25)
Invalid alpha value specifier '-1' in 'red@-1'
red@-1 -> error
red@0.5 -> R(255) G(0) B(0) A(127)
red@1.0 -> R(255) G(0) B(0) A(255)
Invalid alpha value specifier '256' in 'red@256'
red@256 -> error
Invalid alpha value specifier '10foo' in 'red@10foo'
red@10foo -> error
Invalid alpha value specifier '-1.0' in 'red@-1.0'
red@-1.0 -> error
red@-0.0 -> R(255) G(0) B(0) A(0)

Testing av_small_strptime()
fmt:'%Y-%m-%d' spec:'2012-12-21' -> 2012-12-21 00:00:00
fmt:'%Y - %m - %d' spec:'2012-12-21' -> 2012-12-21 00:00:00
fmt:'%Y-%m-%d %H:%M:%S' spec:'2012-12-21 20:12:21' -> 2012-12-21 20:12:21
fmt:'  %Y - %m - %d %H : %M : %S' spec:'   2012 - 12 -  21   20 : 12 : 21'
-> 2012-12-21 20:12:21
fmt:'  %Y - %b - %d %H : %M : %S' spec:'   2012 - nOV -  21   20 : 12 : 21'
-> 2012-11-21 20:12:21
fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - nOVemBeR -  21   20 : 12
: 21' -> 2012-11-21 20:12:21
fmt:'  %Y - %B%d %H : %M : %S' spec:'   2012 - may21   20 : 12 : 21' ->
2012-05-21 20:12:21
fmt:'  %Y - %B%d %H : %M : %S' spec:'   2012 - mby21   20 : 12 : 21' ->
error
fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - JunE -  21   20 : 12 :
21' -> 2012-06-21 20:12:21
fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - Jane -  21   20 : 12 :
21' -> error
fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - January -  21   20 : 12 :
21' -> 2012-01-21 20:12:21

Testing av_parse_time()
(now is 2012-03-17 09:14:13.2 +0100, local time is UTC+1)
now                      ->     1331972053.200000 = 2012-03-17T08:14:13Z
12:35:46                 ->     1331984146.000000 = 2012-03-17T11:35:46Z
2000-12-20 0:02:47.5z    ->      977270567.500000 = 2000-12-20T00:02:47Z
2012 - 02-22  17:44:07   ->     1329929047.000000 = 2012-02-22T16:44:07Z
2000-12-20T010247.6      ->      977270567.600000 = 2000-12-20T00:02:47Z
2000-12-12 1:35:46+05:30 ->      976565146.000000 = 2000-12-11T20:05:46Z
2002-12-12 22:30:40-02   ->     1039739440.000000 = 2002-12-13T00:30:40Z
2:34:56.79               ->           +9296790000
-1:23:45.67              ->           -5025670000
42.1729                  ->             +42172900
-1729.42                 ->           -1729420000
12:34                    ->            +754000000
PT2M4.0S                 -> error

Testing av_get_known_color_name()
AliceBlue -> R(240) G(248) B(255) A(0)
AntiqueWhite -> R(250) G(235) B(215) A(0)
Aqua -> R(0) G(255) B(255) A(0)
Aquamarine -> R(127) G(255) B(212) A(0)
Azure -> R(240) G(255) B(255) A(0)
Beige -> R(245) G(245) B(220) A(0)
Bisque -> R(255) G(228) B(196) A(0)
Black -> R(0) G(0) B(0) A(0)
BlanchedAlmond -> R(255) G(235) B(205) A(0)
Blue -> R(0) G(0) B(255) A(0)
BlueViolet -> R(138) G(43) B(226) A(0)
Brown -> R(165) G(42) B(42) A(0)
BurlyWood -> R(222) G(184) B(135) A(0)
CadetBlue -> R(95) G(158) B(160) A(0)
Chartreuse -> R(127) G(255) B(0) A(0)
Chocolate -> R(210) G(105) B(30) A(0)
Coral -> R(255) G(127) B(80) A(0)
CornflowerBlue -> R(100) G(149) B(237) A(0)
Cornsilk -> R(255) G(248) B(220) A(0)
Crimson -> R(220) G(20) B(60) A(0)
Cyan -> R(0) G(255) B(255) A(0)
DarkBlue -> R(0) G(0) B(139) A(0)
DarkCyan -> R(0) G(139) B(139) A(0)
DarkGoldenRod -> R(184) G(134) B(11) A(0)
DarkGray -> R(169) G(169) B(169) A(0)
DarkGreen -> R(0) G(100) B(0) A(0)
DarkKhaki -> R(189) G(183) B(107) A(0)
DarkMagenta -> R(139) G(0) B(139) A(0)
DarkOliveGreen -> R(85) G(107) B(47) A(0)
Darkorange -> R(255) G(140) B(0) A(0)
DarkOrchid -> R(153) G(50) B(204) A(0)
DarkRed -> R(139) G(0) B(0) A(0)
DarkSalmon -> R(233) G(150) B(122) A(0)
DarkSeaGreen -> R(143) G(188) B(143) A(0)
DarkSlateBlue -> R(72) G(61) B(139) A(0)
DarkSlateGray -> R(47) G(79) B(79) A(0)
DarkTurquoise -> R(0) G(206) B(209) A(0)
DarkViolet -> R(148) G(0) B(211) A(0)
DeepPink -> R(255) G(20) B(147) A(0)
DeepSkyBlue -> R(0) G(191) B(255) A(0)
DimGray -> R(105) G(105) B(105) A(0)
DodgerBlue -> R(30) G(144) B(255) A(0)
FireBrick -> R(178) G(34) B(34) A(0)
FloralWhite -> R(255) G(250) B(240) A(0)
ForestGreen -> R(34) G(139) B(34) A(0)
Fuchsia -> R(255) G(0) B(255) A(0)
Gainsboro -> R(220) G(220) B(220) A(0)
GhostWhite -> R(248) G(248) B(255) A(0)
Gold -> R(255) G(215) B(0) A(0)
GoldenRod -> R(218) G(165) B(32) A(0)
Gray -> R(128) G(128) B(128) A(0)
Green -> R(0) G(128) B(0) A(0)
GreenYellow -> R(173) G(255) B(47) A(0)
HoneyDew -> R(240) G(255) B(240) A(0)
HotPink -> R(255) G(105) B(180) A(0)
IndianRed -> R(205) G(92) B(92) A(0)
Indigo -> R(75) G(0) B(130) A(0)
Ivory -> R(255) G(255) B(240) A(0)
Khaki -> R(240) G(230) B(140) A(0)
Lavender -> R(230) G(230) B(250) A(0)
LavenderBlush -> R(255) G(240) B(245) A(0)
LawnGreen -> R(124) G(252) B(0) A(0)
LemonChiffon -> R(255) G(250) B(205) A(0)
LightBlue -> R(173) G(216) B(230) A(0)
LightCoral -> R(240) G(128) B(128) A(0)
LightCyan -> R(224) G(255) B(255) A(0)
LightGoldenRodYellow -> R(250) G(250) B(210) A(0)
LightGreen -> R(144) G(238) B(144) A(0)
LightGrey -> R(211) G(211) B(211) A(0)
LightPink -> R(255) G(182) B(193) A(0)
LightSalmon -> R(255) G(160) B(122) A(0)
LightSeaGreen -> R(32) G(178) B(170) A(0)
LightSkyBlue -> R(135) G(206) B(250) A(0)
LightSlateGray -> R(119) G(136) B(153) A(0)
LightSteelBlue -> R(176) G(196) B(222) A(0)
LightYellow -> R(255) G(255) B(224) A(0)
Lime -> R(0) G(255) B(0) A(0)
LimeGreen -> R(50) G(205) B(50) A(0)
Linen -> R(250) G(240) B(230) A(0)
Magenta -> R(255) G(0) B(255) A(0)
Maroon -> R(128) G(0) B(0) A(0)
MediumAquaMarine -> R(102) G(205) B(170) A(0)
MediumBlue -> R(0) G(0) B(205) A(0)
MediumOrchid -> R(186) G(85) B(211) A(0)
MediumPurple -> R(147) G(112) B(216) A(0)
MediumSeaGreen -> R(60) G(179) B(113) A(0)
MediumSlateBlue -> R(123) G(104) B(238) A(0)
MediumSpringGreen -> R(0) G(250) B(154) A(0)
MediumTurquoise -> R(72) G(209) B(204) A(0)
MediumVioletRed -> R(199) G(21) B(133) A(0)
MidnightBlue -> R(25) G(25) B(112) A(0)
MintCream -> R(245) G(255) B(250) A(0)
MistyRose -> R(255) G(228) B(225) A(0)
Moccasin -> R(255) G(228) B(181) A(0)
NavajoWhite -> R(255) G(222) B(173) A(0)
Navy -> R(0) G(0) B(128) A(0)
OldLace -> R(253) G(245) B(230) A(0)
Olive -> R(128) G(128) B(0) A(0)
OliveDrab -> R(107) G(142) B(35) A(0)
Orange -> R(255) G(165) B(0) A(0)
OrangeRed -> R(255) G(69) B(0) A(0)


Orchid -> R(218) G(112) B(214) A(0)
PaleGoldenRod -> R(238) G(232) B(170) A(0)
PaleGreen -> R(152) G(251) B(152) A(0)
PaleTurquoise -> R(175) G(238) B(238) A(0)
PaleVioletRed -> R(216) G(112) B(147) A(0)
PapayaWhip -> R(255) G(239) B(213) A(0)
PeachPuff -> R(255) G(218) B(185) A(0)
Peru -> R(205) G(133) B(63) A(0)
Pink -> R(255) G(192) B(203) A(0)
Plum -> R(221) G(160) B(221) A(0)
PowderBlue -> R(176) G(224) B(230) A(0)
Purple -> R(128) G(0) B(128) A(0)
Red -> R(255) G(0) B(0) A(0)
RosyBrown -> R(188) G(143) B(143) A(0)
RoyalBlue -> R(65) G(105) B(225) A(0)
SaddleBrown -> R(139) G(69) B(19) A(0)
Salmon -> R(250) G(128) B(114) A(0)
SandyBrown -> R(244) G(164) B(96) A(0)
SeaGreen -> R(46) G(139) B(87) A(0)
SeaShell -> R(255) G(245) B(238) A(0)
Sienna -> R(160) G(82) B(45) A(0)
Silver -> R(192) G(192) B(192) A(0)
SkyBlue -> R(135) G(206) B(235) A(0)
SlateBlue -> R(106) G(90) B(205) A(0)
SlateGray -> R(112) G(128) B(144) A(0)
Snow -> R(255) G(250) B(250) A(0)
SpringGreen -> R(0) G(255) B(127) A(0)
SteelBlue -> R(70) G(130) B(180) A(0)
Tan -> R(210) G(180) B(140) A(0)
Teal -> R(0) G(128) B(128) A(0)
Thistle -> R(216) G(191) B(216) A(0)
Tomato -> R(255) G(99) B(71) A(0)
Turquoise -> R(64) G(224) B(208) A(0)
Violet -> R(238) G(130) B(238) A(0)
Wheat -> R(245) G(222) B(179) A(0)
White -> R(255) G(255) B(255) A(0)
WhiteSmoke -> R(245) G(245) B(245) A(0)
Yellow -> R(255) G(255) B(0) A(0)
YellowGreen -> R(154) G(205) B(50) A(0)

Testing av_find_info_tag()
0. tag1 found: val1
1. tag2 found: val2
2. tag3 found: val3
3. tag4 not found
4. tag41 found: value 41
5. 41 not found
6. random1 not found








>
> > +{
> > +    /* ISO-8601 duration parser */
> > +    uint32_t days = 0;
> > +    uint32_t hours = 0;
> > +    uint32_t mins = 0;
> > +    uint32_t secs = 0;
> > +    uint32_t size = 0;
> > +    float value = 0;
> > +    uint8_t 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, "GetDurationInSec 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 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;
> > +}
> > +
> > +/*
> > + * Used to reset a statically allocated AVPacket to a clean slate,
> > + * containing no data.
> > + */
> > +static void reset_packet(AVPacket *pkt)
> > +{
> > +    av_init_packet(pkt);
> > +    pkt->data = NULL;
> > +}
> > +
> > +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_packet_unref(&pls->pkt);
> > +    reset_packet(&pls->pkt);
> > +    av_freep(&pls->pb.buffer);
> > +    if (pls->input)
> > +        ff_format_io_close(pls->parent, &pls->input);
> > +    if (pls->ctx) {
> > +        pls->ctx->pb = NULL;
> > +        avformat_close_input(&pls->ctx);
> > +    }
> > +
> > +    av_free(pls->url_template);
> > +    av_free(pls);
> > +}
> > +
> > +static void update_options(char **dest, const char *name, void *src)
> > +{
> > +    av_freep(dest);
> > +    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
> > +    if (*dest)
> > +        av_freep(dest);
> > +}
> > +
> > +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, "http", NULL) &&
> !av_strstart(proto_name, "file", NULL))
> > +        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;
> > +
> > +    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
> > +    if (ret >= 0) {
> > +        // update cookies on http response with setcookies.
> > +        void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> > +        update_options(&c->cookies, "cookies", u);
> > +        av_dict_set(&opts, "cookies", c->cookies, 0);
> > +    }
> > +
> > +    av_dict_free(&tmp);
> > +
> > +    if (is_http)
> > +        *is_http = av_strstart(proto_name, "http", NULL);
> > +
> > +    return ret;
> > +
> > +}
> > +
> > +static char *strreplace(const char *str, const char *from, const char
> *to)
> > +{
> > +    /* Adjust each of the below values to suit your needs. */
> > +    /* Increment positions cache size initially by this number. */
> > +    size_t cache_sz_inc = 16;
> > +    /* Thereafter, each time capacity needs to be increased,
> > +     * multiply the increment by this factor. */
> > +    const size_t cache_sz_inc_factor = 3;
> > +    /* But never increment capacity by more than this number. */
> > +    const size_t cache_sz_inc_max = 1048576;
> > +
> > +    char *pret, *ret = NULL;
> > +    const char *pstr2, *pstr = str;
> > +    size_t i, count = 0;
> > +    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
> > +    size_t cache_sz = 0;
> > +    size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
> > +
> > +    /* Find all matches and cache their positions. */
> > +    while ((pstr2 = av_stristr(pstr, from))) {
> > +        count++;
> > +        /* Increase the cache size when necessary. */
> > +        if (cache_sz < count) {
> > +            cache_sz += cache_sz_inc;
> > +            pos_cache_tmp = av_realloc(pos_cache, sizeof(*pos_cache) *
> cache_sz);
> > +            if (!pos_cache_tmp) {
> > +                goto end_strreplace;
> > +            } else pos_cache = pos_cache_tmp;
> > +            cache_sz_inc *= cache_sz_inc_factor;
> > +            if (cache_sz_inc > cache_sz_inc_max) {
> > +                cache_sz_inc = cache_sz_inc_max;
> > +            }
> > +        }
> > +
> > +        pos_cache[count-1] = pstr2 - str;
> > +        pstr = pstr2 + fromlen;
> > +    }
> > +    orglen = pstr - str + strlen(pstr);
> > +    /* Allocate memory for the post-replacement string. */
> > +    if (count > 0) {
> > +        tolen = strlen(to);
> > +        retlen = orglen + (tolen - fromlen) * count;
> > +    } else {
> > +        retlen = orglen;
> > +    }
> > +    ret = av_malloc(retlen + 1);
> > +    if (!ret) {
> > +        goto end_strreplace;
> > +    }
> > +
> > +    if (!count) {
> > +        /* If no matches, then just duplicate the string. */
> > +        av_strlcpy(ret, str, retlen + 1);
> > +    } else {
> > +        /* Otherwise, duplicate the string whilst performing
> > +         * the replacements using the position cache. */
> > +        pret = ret;
> > +        memcpy(pret, str, pos_cache[0]);
> > +        pret += pos_cache[0];
> > +        for (i = 0; i < count; i++) {
> > +            memcpy(pret, to, tolen);
> > +            pret += tolen;
> > +            pstr = str + pos_cache[i] + fromlen;
> > +            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) -
> pos_cache[i] - fromlen;
> > +            memcpy(pret, pstr, cpylen);
> > +            pret += cpylen;
> > +        }
> > +        ret[retlen] = '\0';
> > +    }
> > +
> > +end_strreplace:
> > +    /* Free the cache and return the post-replacement string,
> > +     * which will be NULL in the event of an error. */
> > +    av_free(pos_cache);
> > +    return ret;
> > +}
> > +
> > +static char *replace_template_str(const char *url, const char *marker)
> > +{
> > +    char *prefix = NULL;
> > +    char *start = 0;
> > +    char *end = NULL;
> > +    char *tmp_url = NULL;
> > +    char *new_url = NULL;
> > +    int marker_len;
> > +
> > +    prefix = av_strdup(marker);
> > +    marker_len = strlen(prefix) - 1;
> > +    prefix[marker_len] = '\0';
> > +
> > +    start = av_stristr(url, prefix);
> > +    if (!start)
> > +        goto finish;
> > +    end = strchr(start + 1, '$');
> > +    if (!end)
> > +        goto finish;
> > +
> > +    tmp_url = av_mallocz(MAX_URL_SIZE);
> > +    if (!tmp_url) {
> > +        return NULL;
> > +    }
> > +
> > +    av_strlcpy(tmp_url, url, start - url+ 1);
> > +    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end -
> start - marker_len + 1);
> > +    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
> > +
> > +finish:
> > +    av_free(prefix);
> > +    new_url = strreplace(tmp_url ? tmp_url : url, marker, "%"PRId64);
>
> Since you're no longer using a printf function, this replacement doesn't
> make sense. You should replace with the actual value you want to substitute
> instead.
>
> > +    av_free(tmp_url);
> > +    return new_url;
> > +}
> > +
> > +static char *get_content_url(xmlNodePtr *baseurl_nodes, int
> n_baseurl_nodes, xmlChar *rep_id_val, xmlChar *rep_bandwidth_val, xmlChar
> *val)
> > +{
> > +    int i;
> > +    xmlChar *text;
> > +    char *url = NULL;
> > +    char *tmp_str = av_mallocz(MAX_URL_SIZE);
> > +    char *tmp_str_2 = NULL;
> > +
> > +    if (!tmp_str) {
> > +        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) {
> > +                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
> > +                if (!tmp_str_2) {
> > +                    av_free(tmp_str);
> > +                    return NULL;
> > +                }
> > +                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str,
> text);
> > +                av_free(tmp_str);
> > +                tmp_str = tmp_str_2;
> > +                xmlFree(text);
> > +            }
> > +        }
> > +    }
> > +    if (val)
> > +        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
> > +    if (rep_id_val) {
> > +        url = strreplace(tmp_str, "$RepresentationID$", (const
> char*)rep_id_val);
> > +        av_free(tmp_str);
> > +        tmp_str = url;
> > +    }
> > +    if (rep_bandwidth_val && tmp_str)
> > +        url = strreplace(tmp_str, "$Bandwidth$", (const
> char*)rep_bandwidth_val);
> > +    if (tmp_str != url)
> > +        av_free(tmp_str);
> > +    return url;
> > +}
> > +
> > +static xmlChar *get_val_from_nodes_tab(xmlNodePtr *nodes, const int
> n_nodes, const xmlChar *attrname)
> > +{
> > +    int i;
> > +    xmlChar *val;
> > +
> > +    for (i = 0; i < n_nodes; ++i) {
> > +        if (nodes[i]) {
> > +            val = xmlGetProp(nodes[i], attrname);
> > +            if (val)
> > +                return val;
> > +        }
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +static xmlNodePtr findChildNodeByName(xmlNodePtr rootnode, const
> xmlChar *nodename)
> > +{
> > +    xmlNodePtr node = rootnode;
> > +    if (!node) {
> > +        return NULL;
> > +    }
> > +
> > +    node = xmlFirstElementChild(node);
> > +    while (node) {
> > +        if (!xmlStrcmp(node->name, nodename)) {
> > +            return node;
> > +        }
> > +        node = xmlNextElementSibling(node);
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static enum AVMediaType get_content_type(xmlNodePtr node)
> > +{
> > +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> > +    int i = 0;
> > +    const char *attr;
> > +
> > +    if (node) {
> > +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> > +            attr = (i) ? "mimeType" : "contentType";
> > +            xmlChar *val = xmlGetProp(node, attr);
> > +            if (val) {
> > +                if (av_stristr((const char *)val, "video")) {
> > +                    type = AVMEDIA_TYPE_VIDEO;
> > +                } else if (av_stristr((const char *)val, "audio")) {
> > +                    type = AVMEDIA_TYPE_AUDIO;
> > +                }
> > +                xmlFree(val);
> > +            }
> > +            i++;
> > +        }
> > +    }
> > +    return type;
> > +}
> > +
> > +static int parse_manifest_segmenturlnode(AVFormatContext *s, struct
> representation *rep,
> > +                                          xmlNodePtr fragmenturl_node,
> > +                                          xmlNodePtr *baseurl_nodes,
> > +                                          xmlChar *rep_id_val,
> > +                                          xmlChar *rep_bandwidth_val)
> > +{
> > +    xmlChar *initialization_val = NULL;
> > +    xmlChar *media_val = NULL;
> > +
> > +    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
> *)"Initialization")) {
> > +        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
> > +        if (initialization_val) {
> > +            rep->init_section = av_mallocz(sizeof(struct fragment));
> > +            if (!rep->init_section) {
> > +                xmlFree(initialization_val);
> > +                return AVERROR(ENOMEM);
> > +            }
> > +            rep->init_section->url = get_content_url(baseurl_nodes, 4,
> rep_id_val, rep_bandwidth_val, initialization_val);
> > +            rep->init_section->size = -1;
> > +            xmlFree(initialization_val);
> > +        }
> > +    } else if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
> *)"SegmentURL")) {
> > +        media_val = xmlGetProp(fragmenturl_node, "media");
> > +        if (media_val) {
> > +            struct fragment *seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                xmlFree(media_val);
> > +                return AVERROR(ENOMEM);
> > +            }
> > +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
> rep_bandwidth_val, media_val);
> > +            seg->size = -1;
> > +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> > +            xmlFree(media_val);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int parse_manifest_segmenttimeline(AVFormatContext *s, struct
> representation *rep,
> > +                                           xmlNodePtr
> fragment_timeline_node)
> > +{
> > +    xmlAttrPtr attr = NULL;
> > +    xmlChar *val  = NULL;
> > +
> > +    if (!xmlStrcmp(fragment_timeline_node->name, (const xmlChar
> *)"S")) {
> > +        struct timeline *tml = av_mallocz(sizeof(struct timeline));
> > +        if (!tml) {
> > +            return AVERROR(ENOMEM);
> > +        }
> > +        attr = fragment_timeline_node->properties;
> > +        while (attr) {
> > +            val = xmlGetProp(fragment_timeline_node, attr->name);
> > +
> > +            if (!val) {
> > +                av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline
> attr->name = %s val is NULL\n", attr->name);
> > +                continue;
> > +            }
> > +
> > +            if (!xmlStrcmp(attr->name, (const xmlChar *)"t")) {
> > +                tml->t = (int64_t)atoll((const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> > +                tml->r =(int32_t) atoi((const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> > +                tml->d = (int64_t)atoll((const char *)val);
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)val);
> > +            }
> > +            attr = attr->next;
> > +            xmlFree(val);
> > +        }
> > +        dynarray_add(&rep->timelines, &rep->n_timelines, tml);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int parse_manifest_representation(AVFormatContext *s, const
> char *url,
> > +                                          xmlNodePtr node,
> > +                                          xmlNodePtr adaptionset_node,
> > +                                          xmlNodePtr mpd_baseurl_node,
> > +                                          xmlNodePtr
> period_baseurl_node,
> > +                                          xmlNodePtr
> fragment_template_node,
> > +                                          xmlNodePtr
> content_component_node,
> > +                                          xmlNodePtr
> adaptionset_baseurl_node)
> > +{
> > +    int32_t ret = 0;
> > +    int32_t audio_rep_idx = 0;
> > +    int32_t video_rep_idx = 0;
> > +    char *temp_string = NULL;
> > +    DASHContext *c = s->priv_data;
> > +    struct representation *rep = NULL;
> > +    struct fragment *seg = NULL;
> > +    xmlNodePtr representation_segmenttemplate_node = NULL;
> > +    xmlNodePtr representation_baseurl_node = NULL;
> > +    xmlNodePtr representation_segmentlist_node = NULL;
> > +    xmlNodePtr fragment_timeline_node = NULL;
> > +    xmlNodePtr fragment_templates_tab[2];
> > +    xmlChar *duration_val = NULL;
> > +    xmlChar *startnumber_val = NULL;
> > +    xmlChar *timescale_val = NULL;
> > +    xmlChar *initialization_val = NULL;
> > +    xmlChar *media_val = NULL;
> > +    xmlNodePtr baseurl_nodes[4];
> > +    xmlNodePtr representation_node = node;
> > +    xmlChar *rep_id_val = xmlGetProp(representation_node, "id");
> > +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node,
> "bandwidth");
> > +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> > +
> > +    // try get information from representation
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(representation_node);
> > +    // try get information from contentComponen
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(content_component_node);
> > +    // try get information from adaption set
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(adaptionset_node);
> > +    if (type == AVMEDIA_TYPE_UNKNOWN) {
> > +        av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported
> representation type\n", url);
> > +    } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type
> == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
> > +        // convert selected representation to our internal struct
> > +        rep = av_mallocz(sizeof(struct representation));
> > +        if (!rep) {
> > +            ret = AVERROR(ENOMEM);
> > +            goto end;
> > +        }
> > +        representation_segmenttemplate_node = findChildNodeByName(representation_node,
> "SegmentTemplate");
> > +        representation_baseurl_node = findChildNodeByName(representation_node,
> "BaseURL");
> > +        representation_segmentlist_node = findChildNodeByName(representation_node,
> "SegmentList");
> > +        reset_packet(&rep->pkt);
> > +
> > +        baseurl_nodes[0] = mpd_baseurl_node;
> > +        baseurl_nodes[1] = period_baseurl_node;
> > +        baseurl_nodes[2] = adaptionset_baseurl_node;
> > +        baseurl_nodes[3] = representation_baseurl_node;
> > +
> > +        if (representation_segmenttemplate_node ||
> fragment_template_node) {
> > +            fragment_timeline_node = NULL;
> > +            fragment_templates_tab[0] = representation_
> segmenttemplate_node;
> > +            fragment_templates_tab[1] = fragment_template_node;
> > +
> > +            duration_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "duration");
> > +            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "startNumber");
> > +            timescale_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "timescale");
> > +            initialization_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "initialization");
> > +            media_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "media");
> > +
> > +            if (initialization_val) {
> > +                rep->init_section = av_mallocz(sizeof(struct fragment));
> > +                if (!rep->init_section) {
> > +                    av_free(rep);
> > +                    ret = AVERROR(ENOMEM);
> > +                    goto end;
> > +                }
> > +                rep->init_section->url = get_content_url(baseurl_nodes,
> 4, rep_id_val, rep_bandwidth_val, initialization_val);
> > +                rep->init_section->size = -1;
> > +                xmlFree(initialization_val);
> > +            }
> > +
> > +            if (media_val) {
> > +                temp_string = get_content_url(baseurl_nodes, 4,
> rep_id_val, rep_bandwidth_val, media_val);
> > +                if (temp_string) {
> > +                    if (av_stristr(temp_string, "$Number")) {
> > +                        rep->url_template = av_strdup(temp_string);
>
> Since you're no longer replacing here, this if/else tree mostly doesn't
> make sense anymore. You should remove the av_free below and store the
> result of get_content_url() directly to rep->url_template, and just have a
> check here to set tmp_url_type. Also, you need to check for a NULL return
> from get_content_url.
>
> > +                        /* A URL template is provided from which
> clients build a chunk list
> > +                         * where the chunk URLs include chunk numbers
> (like index numbers).
> > +                         */
> > +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /*
> Number-Based. */
> > +                    } else if (av_stristr(temp_string, "$Time")) {
> > +                        rep->url_template = av_strdup(temp_string);
> > +                        /* A URL template is provided from which
> clients build a chunk list
> > +                         * where the chunk URLs include chunk start
> times.
> > +                         */
> > +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /*
> Time-Based. */
> > +                    } else {
> > +                        rep->url_template = temp_string;
> > +                        temp_string = NULL;
> > +                    }
> > +                    av_free(temp_string);
> > +                }
> > +                xmlFree(media_val);
> > +            }
> > +            if (duration_val) {
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)duration_val);
> > +                xmlFree(duration_val);
> > +            }
> > +            if (timescale_val) {
> > +                rep->fragment_timescale = (int64_t) atoll((const char
> *)timescale_val);
> > +                xmlFree(timescale_val);
> > +            }
> > +            if (startnumber_val) {
> > +                rep->first_seq_no = (int64_t) atoll((const char
> *)startnumber_val);
> > +                xmlFree(startnumber_val);
> > +            }
> > +
> > +            fragment_timeline_node = findChildNodeByName(
> representation_segmenttemplate_node, "SegmentTimeline");
> > +
> > +            if (!fragment_timeline_node)
> > +                fragment_timeline_node = findChildNodeByName(fragment_template_node,
> "SegmentTimeline");
> > +            if (fragment_timeline_node) {
> > +                fragment_timeline_node = xmlFirstElementChild(fragment_
> timeline_node);
> > +                while (fragment_timeline_node) {
> > +                    ret = parse_manifest_segmenttimeline(s, rep,
> fragment_timeline_node);
> > +                    if (ret < 0) {
> > +                        return ret;
> > +                    }
> > +                    fragment_timeline_node = xmlNextElementSibling(
> fragment_timeline_node);
> > +                }
> > +            }
> > +        } else if (representation_baseurl_node &&
> !representation_segmentlist_node) {
> > +            seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                ret = AVERROR(ENOMEM);
> > +                goto end;
> > +            }
> > +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
> rep_bandwidth_val, NULL);
> > +            seg->size = -1;
> > +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> > +        } else if (!representation_baseurl_node &&
> representation_segmentlist_node) {
> > +            // TODO: https://www.brendanlong.com/
> the-structure-of-an-mpeg-dash-mpd.html
> > +            // http://www-itec.uni-klu.ac.at/
> dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
> > +            xmlNodePtr fragmenturl_node = NULL;
> > +            duration_val = xmlGetProp(representation_segmentlist_node,
> "duration");
> > +            timescale_val = xmlGetProp(representation_segmentlist_node,
> "timescale");
> > +            if (duration_val) {
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)duration_val);
> > +                xmlFree(duration_val);
> > +            }
> > +            if (timescale_val) {
> > +                rep->fragment_timescale = (int64_t) atoll((const char
> *)timescale_val);
> > +                xmlFree(timescale_val);
> > +            }
> > +            fragmenturl_node = xmlFirstElementChild(
> representation_segmentlist_node);
> > +            while (fragmenturl_node) {
> > +                ret = parse_manifest_segmenturlnode(s, rep,
> fragmenturl_node, baseurl_nodes, rep_id_val, rep_bandwidth_val);
> > +                if (ret < 0) {
> > +                    return ret;
> > +                }
> > +                fragmenturl_node = xmlNextElementSibling(
> fragmenturl_node);
> > +            }
> > +        } else {
> > +            free_representation(rep);
> > +            rep = NULL;
> > +            av_log(s, AV_LOG_ERROR, "Unknown format of Representation
> node id[%s] \n", (const char *)rep_id_val);
> > +        }
> > +
> > +        if (rep) {
> > +            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
> > +                rep->fragment_timescale = 1;
> > +            if (type == AVMEDIA_TYPE_VIDEO) {
> > +                rep->rep_idx = video_rep_idx;
> > +                c->cur_video = rep;
> > +            } else {
> > +                rep->rep_idx = audio_rep_idx;
> > +                c->cur_audio = rep;
> > +            }
> > +        }
> > +    }
> > +
> > +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
> > +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
> > +
> > +end:
> > +    if (rep_id_val)
> > +        xmlFree(rep_id_val);
> > +    if (rep_bandwidth_val)
> > +        xmlFree(rep_bandwidth_val);
> > +
> > +    return ret;
> > +}
> > +
> > +static int parse_manifest_adaptationset(AVFormatContext *s, const char
> *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, xmlNodePtr
> period_baseurl_node)
> > +{
> > +    int ret = 0;
> > +    xmlNodePtr fragment_template_node = NULL;
> > +    xmlNodePtr content_component_node = NULL;
> > +    xmlNodePtr adaptionset_baseurl_node = NULL;
> > +    xmlNodePtr node = NULL;
> > +
> > +    node = xmlFirstElementChild(adaptionset_node);
> > +    while (node) {
> > +        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate"))
> {
> > +            fragment_template_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar
> *)"ContentComponent")) {
> > +            content_component_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
> > +            adaptionset_baseurl_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar
> *)"Representation")) {
> > +            ret = parse_manifest_representation(s, url, node,
> > +                                                 adaptionset_node,
> > +                                                 mpd_baseurl_node,
> > +                                                 period_baseurl_node,
> > +                                                 fragment_template_node,
> > +                                                 content_component_node,
> > +
>  adaptionset_baseurl_node);
> > +            if (ret < 0) {
> > +                return ret;
> > +            }
> > +        }
> > +        node = xmlNextElementSibling(node);
> > +    }
> > +    return 0;
> > +}
> > +
> > +
> > +static int parse_manifest(AVFormatContext *s, const char *url,
> AVIOContext *in)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    int close_in = 0;
> > +    uint8_t *new_url = NULL;
> > +    int64_t filesize = 0;
> > +    char *buffer = NULL;
> > +    AVDictionary *opts = NULL;
> > +    xmlDoc *doc = NULL;
> > +    xmlNodePtr root_element = NULL;
> > +    xmlNodePtr node = NULL;
> > +    xmlNodePtr period_node = NULL;
> > +    xmlNodePtr mpd_baseurl_node = NULL;
> > +    xmlNodePtr period_baseurl_node = NULL;
> > +    xmlNodePtr adaptionset_node = NULL;
> > +    xmlAttrPtr attr = NULL;
> > +    xmlChar *val  = NULL;
> > +    uint32_t perdiod_duration_sec = 0;
> > +    uint32_t perdiod_start_sec = 0;
> > +    int32_t audio_rep_idx = 0;
> > +    int32_t video_rep_idx = 0;
> > +
> > +    if (!in) {
> > +        close_in = 1;
> > +        /* This is XML manifest there is no need to set range header */
> > +        av_dict_set(&opts, "seekable", "0", 0);
> > +        // broker prior HTTP options that should be consistent across
> requests
> > +        av_dict_set(&opts, "user-agent", c->user_agent, 0);
> > +        av_dict_set(&opts, "cookies", c->cookies, 0);
> > +        av_dict_set(&opts, "headers", c->headers, 0);
> > +
> > +        ret = avio_open2(&in, url, AVIO_FLAG_READ,
> c->interrupt_callback, &opts);
> > +        av_dict_free(&opts);
> > +        if (ret < 0)
> > +            return ret;
> > +    }
> > +
> > +    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >=
> 0) {
> > +        c->base_url = av_strdup(new_url);
> > +    } else {
> > +        c->base_url = av_strdup(url);
> > +    }
> > +
> > +    filesize = avio_size(in);
> > +    if (filesize <= 0){
>
> Space between ) and { (this occurs a few times).
>
> > +        filesize = 8 * 1024;
> > +    }
> > +
> > +    buffer = av_mallocz(filesize);
> > +    if (!buffer) {
> > +        return AVERROR(ENOMEM);
> > +    }
> > +
> > +    filesize = avio_read(in, buffer, filesize);
> > +    if (filesize > 0) {
> > +        LIBXML_TEST_VERSION
> > +
> > +        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
> > +        root_element = xmlDocGetRootElement(doc);
> > +        node = root_element;
> > +
> > +        if (!node) {
> > +            ret = AVERROR_INVALIDDATA;
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> root node\n", url);
> > +            goto cleanup;
> > +        }
> > +
> > +        if (node->type != XML_ELEMENT_NODE ||
> > +            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
> > +            ret = AVERROR_INVALIDDATA;
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root
> node name[%s] type[%d]\n", url, node->name, (int)node->type);
> > +            goto cleanup;
> > +        }
> > +
> > +        val = xmlGetProp(node, "type");
> > +        if (!val) {
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> type attrib\n", url);
> > +            ret = AVERROR_INVALIDDATA;
> > +            goto cleanup;
> > +        }
> > +        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
> > +            c->is_live = 1;
> > +        xmlFree(val);
> > +
> > +        attr = node->properties;
> > +        while (attr) {
> > +            val = xmlGetProp(node, attr->name);
> > +
> > +            if (!xmlStrcmp(attr->name, (const xmlChar
> *)"availabilityStartTime")) {
> > +                c->availability_start_time_sec = GetUTCDateTimeInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"publishTime")) {
> > +                c->publish_time_sec = GetUTCDateTimeInSec(s, (const
> char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"minimumUpdatePeriod")) {
> > +                c->minimum_update_period_sec = GetDurationInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"timeShiftBufferDepth")) {
> > +                c->time_shift_buffer_depth_sec = GetDurationInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"minBufferTime")) {
> > +                c->min_buffer_time_sec = GetDurationInSec(s, (const
> char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"suggestedPresentationDelay")) {
> > +                c->suggested_presentation_delay_sec =
> GetDurationInSec(s, (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"mediaPresentationDuration")) {
> > +                c->media_presentation_duration_sec =
> GetDurationInSec(s, (const char *)val);
> > +            }
> > +            attr = attr->next;
> > +            xmlFree(val);
> > +        }
> > +
> > +        mpd_baseurl_node = findChildNodeByName(node, "BaseURL");
> > +
> > +        // at now we can handle only one period, with the longest
> duration
> > +        node = xmlFirstElementChild(node);
> > +        while (node) {
> > +            if (!xmlStrcmp(node->name, (const xmlChar *)"Period")) {
> > +                perdiod_duration_sec = 0;
> > +                perdiod_start_sec = 0;
> > +                attr = node->properties;
> > +                while (attr) {
> > +                    val = xmlGetProp(node, attr->name);
> > +                    if (!xmlStrcmp(attr->name, (const xmlChar
> *)"duration")) {
> > +                        perdiod_duration_sec = GetDurationInSec(s,
> (const char *)val);
> > +                    } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"start")) {
> > +                        perdiod_start_sec = GetDurationInSec(s, (const
> char *)val);
> > +                    }
> > +                    attr = attr->next;
> > +                    xmlFree(val);
> > +                }
> > +                if ((perdiod_duration_sec) >= (c->period_duration_sec))
> {
> > +                    period_node = node;
> > +                    c->period_duration_sec = perdiod_duration_sec;
> > +                    c->period_start_sec = perdiod_start_sec;
> > +                    if (c->period_start_sec > 0)
> > +                        c->media_presentation_duration_sec =
> c->period_duration_sec;
> > +                }
> > +            }
> > +            node = xmlNextElementSibling(node);
> > +        }
> > +        if (!period_node) {
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> Period node\n", url);
> > +            ret = AVERROR_INVALIDDATA;
> > +            goto cleanup;
> > +        }
> > +        // explore AdaptationSet
> > +        adaptionset_node = xmlFirstElementChild(period_node);
> > +        while (adaptionset_node) {
> > +            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar
> *)"BaseURL")) {
> > +                period_baseurl_node = adaptionset_node;
> > +            } else if (!xmlStrcmp(adaptionset_node->name, (const
> xmlChar *)"AdaptationSet")) {
> > +                parse_manifest_adaptationset(s, url, adaptionset_node,
> mpd_baseurl_node, period_baseurl_node);
> > +            }
> > +            adaptionset_node = xmlNextElementSibling(adaptionset_node);
> > +        }
> > +        if (c->cur_video) {
> > +            c->cur_video->rep_count = video_rep_idx;
> > +            c->cur_video->fix_multiple_stsd_order = 1;
> > +            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n",
> (int)c->cur_video->rep_idx);
> > +            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n",
> (int)video_rep_idx);
> > +        }
> > +        if (c->cur_audio) {
> > +            c->cur_audio->rep_count = audio_rep_idx;
> > +        }
> > +
> > +cleanup:
> > +        /*free the document */
> > +        xmlFreeDoc(doc);
> > +        xmlCleanupParser();
> > +    } else {
> > +        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
> > +        ret = AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    av_free(new_url);
> > +    av_free(buffer);
> > +    if (close_in) {
> > +        avio_close(in);
> > +    }
> > +    return ret;
> > +}
> > +
> > +static int64_t calc_cur_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec) - c->presentation_delay_sec) *
> pls->fragment_timescale) / pls->fragment_duration;
> > +    } else {
> > +        num = pls->first_seq_no;
> > +    }
> > +    return num;
> > +}
> > +
> > +static int64_t calc_min_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) *
> pls->fragment_timescale)  / pls->fragment_duration;
> > +    } else {
> > +        num = pls->first_seq_no;
> > +    }
> > +    return num;
> > +}
> > +
> > +static int64_t calc_max_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +    int64_t i = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec)) * pls->fragment_timescale)  /
> pls->fragment_duration;
> > +    } else {
> > +        if (pls->n_fragments) {
> > +            num = pls->first_seq_no + pls->n_fragments - 1;
> > +        } else if (pls->n_timelines) {
> > +            num = pls->first_seq_no + pls->n_timelines - 1;
> > +            for (i = 0; i < pls->n_timelines; ++i) {
> > +                num += pls->timelines[i]->r;
> > +            }
> > +        } else {
> > +            num = pls->first_seq_no + (c->media_presentation_duration_sec
> * pls->fragment_timescale) / pls->fragment_duration;
> > +        }
> > +    }
> > +
> > +    return num;
> > +}
> > +
> > +static int64_t get_fragment_start_time(struct representation *pls,
> int64_t cur_seq_no)
> > +{
> > +    int64_t i = 0;
> > +    int64_t j = 0;
> > +    int64_t num = 0;
> > +    int64_t startTime = 0;
> > +
> > +    if (pls->n_timelines) {
> > +        for (i = 0; i < pls->n_timelines; ++i) {
> > +            if (pls->timelines[i]->t > 0) {
> > +                startTime = pls->timelines[i]->t;
> > +            }
> > +            if (num == cur_seq_no)
> > +                goto finish;
> > +            startTime += pls->timelines[i]->d;
> > +            for (j = 0; j < pls->timelines[i]->r; ++j) {
> > +                num++;
> > +                if (num == cur_seq_no)
> > +                    goto finish;
> > +                startTime += pls->timelines[i]->d;
> > +            }
> > +            num++;
> > +        }
> > +    }
> > +
> > +finish:
> > +    return startTime;
> > +}
> > +
> > +static struct fragment *get_current_fragment(struct representation *pls)
> > +{
> > +    int64_t tmp_val = 0;
> > +    int64_t min_seq_no = 0;
> > +    int64_t max_seq_no = 0;
> > +    char buffer[1024];
> > +    char *tmp_str = NULL;
> > +    struct fragment *seg = NULL;
> > +    struct fragment *seg_ptr = NULL;
> > +    DASHContext *c = pls->parent->priv_data;
> > +
> > +    if (pls->n_fragments > 0) {
> > +        if (pls->cur_seq_no < pls->n_fragments) {
> > +            seg_ptr = pls->fragments[pls->cur_seq_no];
> > +            seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                return NULL;
> > +            }
> > +            seg->url = av_strdup(seg_ptr->url);
> > +            if (!seg->url) {
> > +                av_free(seg);
> > +                return NULL;
> > +            }
> > +            seg->size = seg_ptr->size;
> > +            seg->url_offset = seg_ptr->url_offset;
> > +            return seg;
> > +        }
> > +    }
> > +    if (c->is_live) {
> > +        while (1) {
> > +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> > +            max_seq_no = calc_max_seg_no(pls->parent, pls);
> > +
> > +            if (pls->cur_seq_no <= min_seq_no) {
> > +                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment:
> cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> > +                       (int64_t)pls->cur_seq_no, min_seq_no,
> max_seq_no, (int)pls->rep_idx);
> > +                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
> > +            } else if (pls->cur_seq_no > max_seq_no) {
> > +                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment:
> min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> > +                       min_seq_no, max_seq_no, (int)pls->rep_idx);
> > +                av_usleep(1000);
> > +                continue;
> > +            }
> > +            break;
> > +        }
> > +        seg = av_mallocz(sizeof(struct fragment));
> > +        if (!seg) {
> > +            return NULL;
> > +        }
> > +    } else if (pls->cur_seq_no <= pls->last_seq_no) {
> > +        seg = av_mallocz(sizeof(struct fragment));
> > +        if (!seg) {
> > +            return NULL;
> > +        }
> > +    }
> > +    if (seg) {
> > +        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
> > +            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ?
> pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
> > +            if (av_get_frame_filename(buffer, sizeof(buffer),
> pls->url_template, (int64_t)tmp_val) < 0) {
>
> It's not a security issue like with printf functions, but you shouldn't
> use av_get_frame_filename() with arbitrary format-string input either, as
> it will fail if the input already contained a '%' character. I also don't
> understand how this is supposed to work, since your source string
> (url_template) shouldn't contain a "%d"?
>
> > +                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment
> filename template %s\n", pls->url_template);
> > +                return NULL;
> > +            }
> > +
> > +            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
> > +                tmp_str = replace_template_str(buffer, "$Number$");
> > +            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME){
> > +                tmp_str = replace_template_str(buffer, "$Time$");
> > +            } else {
> > +                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type
> [%d]\n", pls->tmp_url_type);
>
> Since this shouldn't be possible, an av_assert0 is applicable; no need to
> log the value.
>
> > +                return NULL;
> > +            }
> > +
> > +            seg->url = av_strdup(tmp_str);
> > +            if (!seg->url) {
> > +                av_free(tmp_str);
> > +                return NULL;
> > +            }
> > +        } else {
> > +            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to
> resolve template url '%s'\n", pls->url_template);
> > +            seg->url = av_strdup(pls->url_template);
> > +            if (!seg->url) {
> > +                return NULL;
> > +            }
> > +        }
> > +        seg->size = -1;
> > +    }
> > +
> > +    av_free(tmp_str);
> > +    return seg;
> > +}
> > +
> > +enum ReadFromURLMode {
> > +    READ_NORMAL,
> > +    READ_COMPLETE,
> > +};
> > +
> > +static int read_from_url(struct representation *pls, struct fragment
> *seg,
> > +                         uint8_t *buf, int buf_size,
> > +                         enum ReadFromURLMode mode)
> > +{
> > +    int ret;
> > +
> > +    /* limit read if the fragment was only a part of a file */
> > +    if (seg->size >= 0)
> > +        buf_size = FFMIN(buf_size, pls->cur_seg_size -
> pls->cur_seg_offset);
> > +
> > +    if (mode == READ_COMPLETE) {
> > +        ret = avio_read(pls->input, buf, buf_size);
> > +        if (ret < buf_size) {
> > +            av_log(pls->parent, AV_LOG_WARNING, "Could not read
> complete fragment.\n");
> > +        }
> > +    } else {
> > +        ret = avio_read(pls->input, buf, buf_size);
> > +    }
> > +    if (ret > 0)
> > +        pls->cur_seg_offset += ret;
> > +
> > +    return ret;
> > +}
> > +
> > +static int open_input(DASHContext *c, struct representation *pls,
> struct fragment *seg)
> > +{
> > +    AVDictionary *opts = NULL;
> > +    char url[MAX_URL_SIZE];
> > +    int ret;
> > +
> > +    // broker prior HTTP options that should be consistent across
> requests
> > +    av_dict_set(&opts, "user-agent", c->user_agent, 0);
> > +    av_dict_set(&opts, "cookies", c->cookies, 0);
> > +    av_dict_set(&opts, "headers", c->headers, 0);
> > +    if (c->is_live) {
> > +        av_dict_set(&opts, "seekable", "0", 0);
> > +    }
> > +
> > +    if (seg->size >= 0) {
> > +        /* try to restrict the HTTP request to the part we want
> > +         * (if this is in fact a HTTP request) */
> > +        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
> > +        av_dict_set_int(&opts, "end_offset", seg->url_offset +
> seg->size, 0);
> > +    }
> > +
> > +    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
> > +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s',
> offset %"PRId64", playlist %d\n",
> > +           url, seg->url_offset, pls->rep_idx);
> > +    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts,
> NULL);
> > +    if (ret < 0) {
> > +        goto cleanup;
> > +    }
> > +
> > +    /* Seek to the requested position. If this was a HTTP request, the
> offset
> > +     * should already be where want it to, but this allows e.g. local
> testing
> > +     * without a HTTP server. */
> > +    if (!ret && seg->url_offset) {
> > +        int64_t seekret = avio_seek(pls->input, seg->url_offset,
> SEEK_SET);
> > +        if (seekret < 0) {
> > +            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset
> %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
> > +            ret = (int) seekret;
> > +            ff_format_io_close(pls->parent, &pls->input);
> > +        }
> > +    }
> > +
> > +cleanup:
> > +    av_dict_free(&opts);
> > +    pls->cur_seg_offset = 0;
> > +    pls->cur_seg_size = seg->size;
> > +    return ret;
> > +}
> > +
> > +static int update_init_section(struct representation *pls)
> > +{
> > +    static const int max_init_section_size = 1024*1024;
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t sec_size = 0;
> > +    int64_t urlsize = 0;
> > +    int ret = 0;
> > +
> > +    /* read init section only once per representation */
> > +    if (!pls->init_section || pls->init_sec_buf) {
> > +        return 0;
> > +    }
> > +
> > +    ret = open_input(c, pls, pls->init_section);
> > +    if (ret < 0) {
> > +        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an
> initialization section in playlist %d\n", pls->rep_idx);
> > +        return ret;
> > +    }
> > +
> > +    if (pls->init_section->size >= 0) {
> > +        sec_size = pls->init_section->size;
> > +    } else if ((urlsize = avio_size(pls->input)) >= 0) {
> > +        sec_size = urlsize;
> > +    } else {
> > +        sec_size = max_init_section_size;
> > +    }
> > +    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization
> section of size %"PRId64"\n", sec_size);
> > +    sec_size = FFMIN(sec_size, max_init_section_size);
> > +    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size,
> sec_size);
> > +    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf,
> pls->init_sec_buf_size, READ_COMPLETE);
> > +    ff_format_io_close(pls->parent, &pls->input);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
> > +        uint8_t **stsd_entries = NULL;
> > +        int *stsd_entries_size = NULL;
> > +        int i = 4;
> > +
> > +        while (i <= (ret - 4)) {
> > +            // find start stsd atom
> > +            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
> > +                /* 1B version
> > +                 * 3B flags
> > +                 * 4B num of entries */
> > +                int stsd_first_offset = i + 8;
> > +                int stsd_offset = 0;
> > +                int j = 0;
> > +                uint32_t stsd_count = AV_RB32(pls->init_sec_buf +
> stsd_first_offset);
> > +                stsd_first_offset += 4;
> > +                if (stsd_count != pls->rep_count) {
> > +                    i++;
> > +                    continue;
> > +                }
> > +                // find all stsd entries
> > +                stsd_entries = av_mallocz_array(stsd_count,
> sizeof(*stsd_entries));
> > +                stsd_entries_size = av_mallocz_array(stsd_count,
> sizeof(*stsd_entries_size));
> > +                for (j = 0; j < stsd_count; ++j) {
> > +                    /* 4B - size
> > +                     * 4B - format */
> > +                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf +
> stsd_first_offset + stsd_offset);
> > +                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
> > +                    memcpy(stsd_entries[j], pls->init_sec_buf +
> stsd_first_offset + stsd_offset, stsd_entries_size[j]);
> > +                    stsd_offset += stsd_entries_size[j];
> > +                }
> > +                // reorder stsd entries
> > +                // as first put stsd entry for current representation
> > +                j = pls->rep_idx;
> > +                stsd_offset = stsd_first_offset;
> > +                memcpy(pls->init_sec_buf + stsd_offset,
> stsd_entries[j], stsd_entries_size[j]);
> > +                stsd_offset += stsd_entries_size[j];
> > +                for (j = 0; j < stsd_count; ++j) {
> > +                    if (j != pls->rep_idx) {
> > +                        memcpy(pls->init_sec_buf + stsd_offset,
> stsd_entries[j], stsd_entries_size[j]);
> > +                        stsd_offset += stsd_entries_size[j];
> > +                    }
> > +                    av_free(stsd_entries[j]);
> > +                }
> > +                av_freep(&stsd_entries);
> > +                av_freep(&stsd_entries_size);
> > +                break;
> > +            }
> > +            i++;
> > +        }
> > +    }
> > +
> > +    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section
> size[%d]\n", pls, (int)ret);
> > +    pls->init_sec_data_len = ret;
> > +    pls->init_sec_buf_read_offset = 0;
> > +
> > +    return 0;
> > +}
> > +
> > +static int64_t seek_data(void *opaque, int64_t offset, int whence)
> > +{
> > +    struct representation *v = opaque;
> > +    if (v->n_fragments && !v->init_sec_data_len) {
> > +        return avio_seek(v->input, offset, whence);
> > +    }
> > +
> > +    return AVERROR(ENOSYS);
> > +}
> > +
> > +static int read_data(void *opaque, uint8_t *buf, int buf_size)
> > +{
> > +    int ret = 0;
> > +    struct representation *v = opaque;
> > +    DASHContext *c = v->parent->priv_data;
> > +
> > +restart:
> > +    if (!v->input) {
> > +        free_fragment(&v->cur_seg);
> > +        v->cur_seg = get_current_fragment(v);
> > +        if (!v->cur_seg) {
> > +            ret = AVERROR_EOF;
> > +            goto end;
> > +        }
> > +
> > +        /* load/update Media Initialization Section, if any */
> > +        ret = update_init_section(v);
> > +        if (ret)
> > +            goto end;
> > +
> > +        ret = open_input(c, v, v->cur_seg);
> > +        if (ret < 0) {
> > +            if (ff_check_interrupt(c->interrupt_callback)) {
> > +                goto end;
> > +                ret = AVERROR_EXIT;
> > +            }
> > +            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment
> of playlist %d\n", v->rep_idx);
> > +            v->cur_seq_no++;
> > +            goto restart;
> > +        }
> > +    }
> > +
> > +    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
> > +        /* Push init section out first before first actual fragment */
> > +        int copy_size = FFMIN(v->init_sec_data_len -
> v->init_sec_buf_read_offset, buf_size);
> > +        memcpy(buf, v->init_sec_buf, copy_size);
> > +        v->init_sec_buf_read_offset += copy_size;
> > +        ret = copy_size;
> > +        goto end;
> > +    }
> > +
> > +    /* check the v->cur_seg, if it is null, get current and double
> check if the new v->cur_seg*/
> > +    if (!v->cur_seg) {
> > +        v->cur_seg = get_current_fragment(v);
> > +    }
> > +    if (!v->cur_seg) {
> > +        ret = AVERROR_EOF;
> > +        goto end;
> > +    }
> > +    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
> > +    if (ret > 0)
> > +        goto end;
> > +    ff_format_io_close(v->parent, &v->input);
> > +    v->cur_seq_no++;
> > +    goto restart;
> > +
> > +end:
> > +    return ret;
> > +}
> > +
> > +static int save_avio_options(AVFormatContext *s)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    const char *opts[] = { "headers", "user_agent", "user-agent",
> "cookies", NULL }, **opt = opts;
> > +    uint8_t *buf = NULL;
> > +    int ret = 0;
> > +
> > +    while (*opt) {
> > +        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0)
> {
> > +            if (buf) {
> > +                ret = av_dict_set(&c->avio_opts, *opt, buf,
> AV_DICT_DONT_STRDUP_VAL);
> > +                if (ret < 0)
> > +                    return ret;
> > +            }
> > +        }
> > +        opt++;
> > +    }
> > +
> > +    return ret;
> > +}
> > +
> > +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const
> char *url,
> > +                          int flags, AVDictionary **opts)
> > +{
> > +    av_log(s, AV_LOG_ERROR,
> > +           "A HDS playlist item '%s' referred to an external file '%s'.
> "
> > +           "Opening this file was forbidden for security reasons\n",
> > +           s->filename, url);
> > +    return AVERROR(EPERM);
> > +}
> > +
> > +static int reopen_demux_for_component(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    AVInputFormat *in_fmt = NULL;
> > +    AVDictionary  *in_fmt_opts = NULL;
> > +    uint8_t *avio_ctx_buffer  = NULL;
> > +    int ret = 0;
> > +
> > +    if (pls->ctx) {
> > +        /* note: the internal buffer could have changed, and be !=
> avio_ctx_buffer */
> > +        av_freep(&pls->pb.buffer);
> > +        memset(&pls->pb, 0x00, sizeof(AVIOContext));
> > +        pls->ctx->pb = NULL;
> > +        avformat_close_input(&pls->ctx);
> > +        pls->ctx = NULL;
> > +    }
> > +    if (!(pls->ctx = avformat_alloc_context())) {
> > +        ret = AVERROR(ENOMEM);
> > +        goto fail;
> > +    }
> > +
> > +    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
> > +    if (!avio_ctx_buffer ){
> > +        ret = AVERROR(ENOMEM);
> > +        avformat_free_context(pls->ctx);
> > +        pls->ctx = NULL;
> > +        goto fail;
> > +    }
> > +    if (c->is_live) {
> > +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
> > +    } else {
> > +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
> > +    }
> > +    pls->pb.seekable = 0;
> > +
> > +    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
> > +        goto fail;
> > +
> > +    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
> > +    pls->ctx->probesize = 1024 * 4;
> > +    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
> > +    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
> > +    if (ret < 0) {
> > +        av_log(s, AV_LOG_ERROR, "Error when loading first fragment,
> playlist %d\n", (int)pls->rep_idx);
> > +        avformat_free_context(pls->ctx);
> > +        pls->ctx = NULL;
> > +        goto fail;
> > +    }
> > +
> > +    pls->ctx->pb = &pls->pb;
> > +    pls->ctx->io_open  = nested_io_open;
> > +
> > +    // provide additional information from mpd if available
> > +    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts);
> //pls->init_section->url
> > +    av_dict_free(&in_fmt_opts);
> > +    if (ret < 0)
> > +        goto fail;
> > +    if (pls->n_fragments) {
> > +        ret = avformat_find_stream_info(pls->ctx, NULL);
> > +        if (ret < 0)
> > +            goto fail;
> > +    }
> > +
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int open_demux_for_component(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    int ret = 0;
> > +    int i;
> > +
> > +    pls->parent = s;
> > +    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
> > +    pls->last_seq_no = calc_max_seg_no(s, pls);
> > +
> > +    ret = reopen_demux_for_component(s, pls);
> > +    if (ret < 0) {
> > +        goto fail;
> > +    }
> > +    for (i = 0; i < pls->ctx->nb_streams; i++) {
> > +        AVStream *st = avformat_new_stream(s, NULL);
> > +        AVStream *ist = pls->ctx->streams[i];
> > +        if (!st) {
> > +            ret = AVERROR(ENOMEM);
> > +            goto fail;
> > +        }
> > +        st->id = i;
> > +        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->
> codecpar);
> > +        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num,
> ist->time_base.den);
> > +    }
> > +
> > +    return 0;
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int dash_read_header(AVFormatContext *s)
> > +{
> > +    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    int stream_index = 0;
> > +
> > +    c->interrupt_callback = &s->interrupt_callback;
> > +    // if the URL context is good, read important options we must
> broker later
> > +    if (u) {
> > +        update_options(&c->user_agent, "user-agent", u);
> > +        update_options(&c->cookies, "cookies", u);
> > +        update_options(&c->headers, "headers", u);
> > +    }
> > +
> > +    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
> > +        goto fail;
> > +
> > +    if ((ret = save_avio_options(s)) < 0)
> > +        goto fail;
> > +
> > +    /* If this isn't a live stream, fill the total duration of the
> > +     * stream. */
> > +    if (!c->is_live) {
> > +        s->duration = (int64_t) c->media_presentation_duration_sec *
> AV_TIME_BASE;
> > +    }
> > +
> > +    /* Open the demuxer for curent video and current audio components
> if available */
> > +    if (!ret && c->cur_video) {
> > +        ret = open_demux_for_component(s, c->cur_video);
> > +        if (!ret) {
> > +            c->cur_video->stream_index = stream_index;
> > +            ++stream_index;
> > +        } else {
> > +            free_representation(c->cur_video);
> > +            c->cur_video = NULL;
> > +        }
> > +    }
> > +
> > +    if (!ret && c->cur_audio) {
> > +        ret = open_demux_for_component(s, c->cur_audio);
> > +        if (!ret) {
> > +            c->cur_audio->stream_index = stream_index;
> > +            ++stream_index;
> > +        } else {
> > +            free_representation(c->cur_audio);
> > +            c->cur_audio = NULL;
> > +        }
> > +    }
> > +
> > +    if (!stream_index) {
> > +        ret = AVERROR_INVALIDDATA;
> > +        goto fail;
> > +    }
> > +
> > +    /* Create a program */
> > +    if (!ret) {
> > +        AVProgram *program;
> > +        program = av_new_program(s, 0);
> > +        if (!program) {
> > +            goto fail;
> > +        }
> > +
> > +        if (c->cur_video) {
> > +            av_program_add_stream_index(s, 0,
> c->cur_video->stream_index);
> > +        }
> > +
> > +        if (c->cur_audio) {
> > +            av_program_add_stream_index(s, 0,
> c->cur_audio->stream_index);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    struct representation *cur = NULL;
> > +
> > +    if (!c->cur_audio && !c->cur_video) {
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    if (c->cur_audio && !c->cur_video) {
> > +        cur = c->cur_audio;
> > +    } else if (!c->cur_audio && c->cur_video) {
> > +        cur = c->cur_video;
> > +    } else if (c->cur_video->cur_timestamp <
> c->cur_audio->cur_timestamp) {
> > +        cur = c->cur_video;
> > +    } else {
> > +        cur = c->cur_audio;
> > +    }
> > +
> > +    if (cur->ctx) {
> > +        ret = av_read_frame(cur->ctx, &cur->pkt);
> > +        if (ret < 0) {
> > +            av_packet_unref(&cur->pkt);
> > +        } else {
> > +            /* If we got a packet, return it */
> > +            *pkt = cur->pkt;
> > +            cur->cur_timestamp = av_rescale(pkt->pts,
> (int64_t)cur->ctx->streams[0]->time_base.num * 90000,
> cur->ctx->streams[0]->time_base.den);
> > +            pkt->stream_index = cur->stream_index;
> > +            reset_packet(&cur->pkt);
> > +            return 0;
> > +        }
> > +    }
> > +    return AVERROR_EOF;
> > +}
> > +
> > +static int dash_close(AVFormatContext *s)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +
> > +    if (c->cur_audio) {
> > +        free_representation(c->cur_audio);
> > +    }
> > +
> > +    if (c->cur_video) {
> > +        free_representation(c->cur_video);
> > +    }
> > +
> > +    av_freep(&c->cookies);
> > +    av_freep(&c->user_agent);
> > +    av_dict_free(&c->avio_opts);
> > +    av_freep(&c->base_url);
> > +    return 0;
> > +}
> > +
> > +static int dash_seek(AVFormatContext *s, struct representation *pls,
> int64_t seek_pos_msec, int flags)
> > +{
> > +    int ret = 0;
> > +    int i = 0;
> > +    int j = 0;
> > +    int64_t duration = 0;
> > +
> > +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms],
> playlist %d\n", seek_pos_msec, pls->rep_idx);
> > +
> > +    // single fragment mode
> > +    if (pls->n_fragments) {
> > +        pls->cur_timestamp = 0;
> > +        pls->cur_seg_offset = 0;
> > +        ff_read_frame_flush(pls->ctx);
> > +        return av_seek_frame(pls->ctx, -1, seek_pos_msec*1000, flags);
> > +    }
> > +
> > +    if (pls->input)
> > +        ff_format_io_close(pls->parent, &pls->input);
> > +
> > +    // find the nearest fragment
> > +    if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
> > +        int64_t num = pls->first_seq_no;
> > +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
> SegmentTimeline start n_timelines[%d] last_seq_no[%"PRId64"], playlist
> %d.\n",
> > +               (int)pls->n_timelines, (int64_t)pls->last_seq_no,
> (int)pls->rep_idx);
> > +        for (i = 0; i < pls->n_timelines; i++) {
> > +            if (pls->timelines[i]->t > 0) {
> > +                duration = pls->timelines[i]->t;
> > +            }
> > +            duration += pls->timelines[i]->d;
> > +            if (seek_pos_msec < ((duration * 1000) /
> pls->fragment_timescale)) {
> > +                goto set_seq_num;
> > +            }
> > +            for (j = 0; j < pls->timelines[i]->r; j++) {
> > +                duration += pls->timelines[i]->d;
> > +                num++;
> > +                if (seek_pos_msec < ((duration * 1000) /
> pls->fragment_timescale)) {
> > +                    goto set_seq_num;
> > +                }
> > +            }
> > +            num++;
> > +        }
> > +
> > +set_seq_num:
> > +        pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no :
> num;
> > +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
> SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
> > +               (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
> > +    } else if (pls->fragment_duration > 0) {
> > +        pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec *
> pls->fragment_timescale) / pls->fragment_duration) / 1000;
> > +    } else {
> > +        av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing
> fragment_duration\n");
> > +        pls->cur_seq_no = pls->first_seq_no;
> > +    }
> > +    pls->cur_timestamp = 0;
> > +    pls->cur_seg_offset = 0;
> > +    pls->init_sec_buf_read_offset = 0;
> > +    ret = reopen_demux_for_component(s, pls);
> > +
> > +    return ret;
> > +}
> > +
> > +static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t
> timestamp, int flags)
> > +{
> > +    int ret = 0;
> > +    DASHContext *c = s->priv_data;
> > +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
> > +                                         s->streams[stream_index]->
> time_base.den,
> > +                                         flags & AVSEEK_FLAG_BACKWARD ?
> > +                                         AV_ROUND_DOWN : AV_ROUND_UP);
> > +    if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
> > +        return AVERROR(ENOSYS);
> > +    if (c->cur_audio) {
> > +        ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
> > +    }
> > +    if (!ret && c->cur_video) {
> > +        ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
> > +    }
> > +    return ret;
> > +}
> > +
> > +static int dash_probe(AVProbeData *p)
> > +{
> > +    if (!av_stristr(p->buf, "<MPD"))
> > +        return 0;
> > +
> > +    if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
> > +        return AVPROBE_SCORE_MAX;
> > +    }
> > +    if (av_stristr(p->buf, "dash:profile")) {
> > +        return AVPROBE_SCORE_MAX >> 1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +#define OFFSET(x) offsetof(DASHContext, x)
> > +#define FLAGS AV_OPT_FLAG_DECODING_PARAM
> > +static const AVOption dash_options[] = {
> > +    {NULL}
> > +};
> > +
> > +static const AVClass dash_class = {
> > +    .class_name = "dash",
> > +    .item_name  = av_default_item_name,
> > +    .option     = dash_options,
> > +    .version    = LIBAVUTIL_VERSION_INT,
> > +};
> > +
> > +AVInputFormat ff_dash_demuxer = {
> > +    .name           = "dash",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming
> over HTTP"),
> > +    .priv_class     = &dash_class,
> > +    .priv_data_size = sizeof(DASHContext),
> > +    .read_probe     = dash_probe,
> > +    .read_header    = dash_read_header,
> > +    .read_packet    = dash_read_packet,
> > +    .read_close     = dash_close,
> > +    .read_seek      = dash_read_seek,
> > +    .flags          = AVFMT_NO_BYTE_SEEK,
> > +};
> > --
> > 2.10.1 (Apple Git-78)
> >
> >
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Rodger Combs March 27, 2017, 7:07 a.m. UTC | #3
> On Mar 27, 2017, at 01:52, Steven Liu <lingjiujianke@gmail.com> wrote:
> 
> 2017-03-27 8:39 GMT+08:00 Rodger Combs <rodger.combs@gmail.com <mailto:rodger.combs@gmail.com>>:
> 
>> 
>>> On Mar 26, 2017, at 19:10, Steven Liu <lq@chinaffmpeg.org> wrote:
>>> 
>>> v2 fixed:
>>> 1. from autodetect to disabled
>>> 2. from camelCase code style to ffmpeg code style
>>> 3. from RepType to AVMediaType
>>> 4. fix variable typo
>>> 5. change time value from uint32_t to uint64_t
>>> 6. removed be used once API
>>> 7. change 'time(NULL)`, except it is not 2038-safe.' to av_gettime and
>>> av_timegm
>>> 8. merge complex free operation to free_fragment
>>> 9. use API from snprintf to av_asprintf
>>> 
>>> v3 fixed:
>>> 1. fix typo from --enabled-xml2 to --enable-xml2
>>> 
>>> v4 fixed:
>>> 1. from --enable-xml2 to --enable-libxml2
>>> 2. move system includes to top
>>> 3. remove nouse includes
>>> 4. rename enum name
>>> 5. add a trailing comma for the last entry enum
>>> 6. fix comment typo
>>> 7. add const to DASHContext class front
>>> 8. check sscanf if return arguments and give warning message when error
>>> 9. check validity before free seg->url and seg
>>> 10. check if the val is null, before use atoll
>>> 
>>> v5 fixed:
>>> 1. fix typo from mainifest to manifest
>>> 
>>> v6 fixed:
>>> 1. from realloc to av_realloc
>>> 2. from free to av_free
>>> 
>>> v7 fixed:
>>> 1. remove the -lxml2 from configure when require_pkg_config
>>> 
>>> v8 fixed:
>>> 1. fix replace filename template by av_asprintf secure problem
>>> 
>>> v9 modified:
>>> 1. make manifest parser clearly
>>> 
>>> Reviewed-by: Clément Bœsch <u@pkh.me>
>>> Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
>>> Reviewed-by: Carl Eugen Hoyos <cehoyos@ag.or.at>
>>> Reviewed-by: Rodger Combs <rodger.combs@gmail.com>
>>> Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
>>> Reviewed-by: Nicolas George <george@nsup.org>
>>> Reviewed-by: Ricardo Constantino <wiiaboo@gmail.com>
>>> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
>>> ---
>>> configure                |    4 +
>>> libavformat/Makefile     |    1 +
>>> libavformat/allformats.c |    2 +-
>>> libavformat/dashdec.c    | 1842 ++++++++++++++++++++++++++++++
>> ++++++++++++++++
>>> 4 files changed, 1848 insertions(+), 1 deletion(-)
>>> create mode 100644 libavformat/dashdec.c
>>> 
>>> diff --git a/configure b/configure
>>> index 9cc7e7d..4a6fe8a 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -273,6 +273,7 @@ External library support:
>>>  --enable-libxcb-shape    enable X11 grabbing shape rendering
>> [autodetect]
>>>  --enable-libxvid         enable Xvid encoding via xvidcore,
>>>                           native MPEG-4/Xvid encoder exists [no]
>>> +  --enable-libxml2            enable XML parsing using the C library
>> libxml2 [no]
>>>  --enable-libzimg         enable z.lib, needed for zscale filter [no]
>>>  --enable-libzmq          enable message passing via libzmq [no]
>>>  --enable-libzvbi         enable teletext support via libzvbi [no]
>>> @@ -1574,6 +1575,7 @@ EXTERNAL_LIBRARY_LIST="
>>>    libvpx
>>>    libwavpack
>>>    libwebp
>>> +    libxml2
>>>    libzimg
>>>    libzmq
>>>    libzvbi
>>> @@ -2906,6 +2908,7 @@ avi_muxer_select="riffenc"
>>> caf_demuxer_select="iso_media riffdec"
>>> caf_muxer_select="iso_media"
>>> dash_muxer_select="mp4_muxer"
>>> +dash_demuxer_deps="libxml2"
>>> dirac_demuxer_select="dirac_parser"
>>> dts_demuxer_select="dca_parser"
>>> dtshd_demuxer_select="dca_parser"
>>> @@ -5908,6 +5911,7 @@ enabled openssl           && { use_pkg_config
>> openssl openssl/ssl.h OPENSSL_init
>>>                               check_lib openssl/ssl.h SSL_library_init
>> -lssl -lcrypto -lws2_32 -lgdi32 ||
>>>                               die "ERROR: openssl not found"; }
>>> enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h ||
>> disable qtkit_indev; }
>>> +enabled libxml2             && require_pkg_config libxml-2.0
>> libxml2/libxml/xmlversion.h xmlCheckVersion
>>> 
>>> # libdc1394 check
>>> if enabled libdc1394; then
>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>> index f56ef16..d77ffaa 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -134,6 +134,7 @@ OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
>>> OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
>>> OBJS-$(CONFIG_DATA_MUXER)                += rawenc.o
>>> OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o
>>> +OBJS-$(CONFIG_DASH_DEMUXER)              += dashdec.o
>>> OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
>>> OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
>>> OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
>>> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
>>> index 09e62c3..d57314b 100644
>>> --- a/libavformat/allformats.c
>>> +++ b/libavformat/allformats.c
>>> @@ -96,7 +96,7 @@ static void register_all(void)
>>>    REGISTER_DEMUXER (CINE,             cine);
>>>    REGISTER_DEMUXER (CONCAT,           concat);
>>>    REGISTER_MUXER   (CRC,              crc);
>>> -    REGISTER_MUXER   (DASH,             dash);
>>> +    REGISTER_MUXDEMUX(DASH,             dash);
>>>    REGISTER_MUXDEMUX(DATA,             data);
>>>    REGISTER_MUXDEMUX(DAUD,             daud);
>>>    REGISTER_DEMUXER (DCSTR,            dcstr);
>>> diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
>>> new file mode 100644
>>> index 0000000..a4d3ba2
>>> --- /dev/null
>>> +++ b/libavformat/dashdec.c
>>> @@ -0,0 +1,1842 @@
>>> +/*
>>> + * 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"
>>> +
>>> +#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 {
>>> +    /* t: 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@t + @d * (@r + 1)).
>>> +     * */
>>> +    int64_t t;
>>> +    /* r: Element or Attribute Name
>>> +     * specifies the repeat count of the number of following contiguous
>> Segments with
>>> +     * the same duration expressed by the value of @d. This value is
>> zero-based
>>> +     * (e.g. a value of three means four Segments in the contiguous
>> series).
>>> +     * */
>>> +    int32_t r;
>>> +    /* d: Element or Attribute Name
>>> +     * specifies the Segment duration, in units of the value of the
>> @timescale.
>>> +     * */
>>> +    int64_t d;
>>> +};
>>> +
>>> +enum DASHTmplUrlType {
>>> +    TMP_URL_TYPE_UNSPECIFIED,
>>> +    TMP_URL_TYPE_NUMBER,
>>> +    TMP_URL_TYPE_TIME,
>>> +};
>>> +
>>> +/*
>>> + * 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;
>>> +    enum DASHTmplUrlType tmp_url_type;
>>> +    AVIOContext pb;
>>> +    AVIOContext *input;
>>> +    AVFormatContext *parent;
>>> +    AVFormatContext *ctx;
>>> +    AVPacket pkt;
>>> +    int rep_idx;
>>> +    int rep_count;
>>> +    int stream_index;
>>> +
>>> +    enum AVMediaType type;
>>> +    int64_t target_duration;
>>> +
>>> +    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 fragment_duration;
>>> +    int64_t fragment_timescale;
>>> +
>>> +    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;
>>> +    int fix_multiple_stsd_order;
>>> +    int64_t cur_timestamp;
>>> +};
>>> +
>>> +typedef struct DASHContext {
>>> +    const AVClass *class;
>>> +    char *base_url;
>>> +    struct representation *cur_video;
>>> +    struct representation *cur_audio;
>>> +    uint64_t media_presentation_duration_sec;
>>> +    uint64_t suggested_presentation_delay_sec;
>>> +    uint64_t presentation_delay_sec;
>>> +    uint64_t availability_start_time_sec;
>>> +    uint64_t publish_time_sec;
>>> +    uint64_t minimum_update_period_sec;
>>> +    uint64_t time_shift_buffer_depth_sec;
>>> +    uint64_t min_buffer_time_sec;
>>> +    uint64_t period_duration_sec;
>>> +    uint64_t period_start_sec;
>>> +    int is_live;
>>> +    AVIOInterruptCB *interrupt_callback;
>>> +    char *user_agent;                    ///< holds HTTP user agent set
>> as an AVOption to the HTTP protocol context
>>> +    char *cookies;                       ///< holds HTTP cookie values
>> set in either the initial response or as an AVOption to the HTTP protocol
>> context
>>> +    char *headers;                       ///< holds HTTP headers set as
>> an AVOption to the HTTP protocol context
>>> +    AVDictionary *avio_opts;
>>> +} DASHContext;
>>> +
>>> +static uint64_t GetCurrentTimeInSec(void)
>> 
>> Code style: "gettime_sec(void)" or the like; not CamelCased.
>> 
>>> +{
>>> +    return  av_gettime() / 1000000;
>>> +}
>>> +
>>> +static uint64_t GetUTCDateTimeInSec(AVFormatContext *s, const char
>> *datetime)
>> 
>> I still don't understand why av_parse_time isn't applicable here; it
>> parses ISO-8601 dates just fine.
>> 
>>> +{
>>> +    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, "GetUTCDateTimeInSec get a wrong time
>> format\n");
>>> +       // return 0; /* parser error */
>>> +    }
>>> +    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 GetDurationInSec(AVFormatContext *s, const char
>> *duration)
>> 
>> Same question here; av_parse_time parses ISO-8601 durations if you set
>> duration=1.
>> 
> because i test use av_parse_time look like bellow:

Ah, sorry, you're right; it doesn't support it. Though I think it'd be preferable to add that functionality to the lavu function (since it's reasonably useful in general), rather than having it just implemented here.

> 1. add  string "PT2M4.0S" into libavutil/tests/parseutils.c, insert into
>    static const char * const duration_string[] = {
>        "2:34:56.79",
>        "-1:23:45.67",
>        "42.1729",
>        "-1729.42",
>        "12:34",
>        "PT2M4.0S"
>    };
> 
> 2. make libavutil/tests/parseutils
> 3. ./libavutil/tests/parseutils
> 
> output:
> localhost:dash StevenLiu$ libavutil/tests/parseutils
> objc[70892]: Class SDLTranslatorResponder is implemented in both
> /usr/local/lib/libSDL-1.2.0.dylib (0x10d363660) and
> /usr/local/lib/libSDL2-2.0.0.dylib (0x10a399db8). One of the two will be
> used. Which one is undefined.
> Testing av_parse_video_rate()
> '-inf' -> -1/0 ERROR
> 'inf' -> 1/0 ERROR
> 'nan' -> 0/0 ERROR
> '123/0' -> 1/0 ERROR
> '-123 / 0' -> -1/0 ERROR
> '' -> 0/0 ERROR
> '/' -> 0/0 ERROR
> ' 123  /  321' -> 41/107 OK
> 'foo/foo' -> 0/0 ERROR
> 'foo/1' -> 0/0 ERROR
> '1/foo' -> 1/0 ERROR
> '0/0' -> 0/0 ERROR
> '/0' -> 0/0 ERROR
> '1/' -> 1/0 ERROR
> '1' -> 1/1 OK
> '0' -> 0/1 ERROR
> '-123/123' -> -1/1 ERROR
> '-foo' -> 0/0 ERROR
> '123.23' -> 12323/100 OK
> '.23' -> 23/100 OK
> '-.23' -> -23/100 ERROR
> '-0.234' -> -117/500 ERROR
> '-0.0000001' -> -1/10000000 ERROR
> '  21332.2324   ' -> 917286/43 OK
> ' -21332.2324   ' -> -917286/43 ERROR
> 
> Testing av_parse_color()
> bikeshed -> R(80) G(64) B(140) A(59)
> RaNdOm -> R(185) G(88) B(148) A(94)
> Cannot find color 'foo'
> foo -> error
> red -> R(255) G(0) B(0) A(255)
> Cannot find color 'Red '
> Red  -> error
> RED -> R(255) G(0) B(0) A(255)
> Violet -> R(238) G(130) B(238) A(255)
> Yellow -> R(255) G(255) B(0) A(255)
> Red -> R(255) G(0) B(0) A(255)
> 0x000000 -> R(0) G(0) B(0) A(255)
> Invalid 0xRRGGBB[AA] color string: '0000000'
> 0x0000000 -> error
> 0xff000000 -> R(255) G(0) B(0) A(0)
> 0x3e34ff -> R(62) G(52) B(255) A(255)
> 0x3e34ffaa -> R(62) G(52) B(255) A(170)
> Invalid 0xRRGGBB[AA] color string: 'ffXXee'
> 0xffXXee -> error
> Invalid 0xRRGGBB[AA] color string: 'foobar'
> 0xfoobar -> error
> Invalid 0xRRGGBB[AA] color string: 'ffffeeeeeeee'
> 0xffffeeeeeeee -> error
> #ff0000 -> R(255) G(0) B(0) A(255)
> Invalid 0xRRGGBB[AA] color string: 'ffXX00'
> #ffXX00 -> error
> ff0000 -> R(255) G(0) B(0) A(255)
> Cannot find color 'ffXX00'
> ffXX00 -> error
> Invalid alpha value specifier 'foo' in 'red@foo'
> red@foo -> error
> Invalid alpha value specifier '10' in 'random@10'
> random@10 -> error
> 0xff0000@1.0 -> R(255) G(0) B(0) A(255)
> Invalid alpha value specifier '' in 'red@'
> red@ -> error
> Invalid alpha value specifier '0xfff' in 'red@0xfff'
> red@0xfff -> error
> red@0xf -> R(255) G(0) B(0) A(15)
> Invalid alpha value specifier '2' in 'red@2'
> red@2 -> error
> red@0.1 -> R(255) G(0) B(0) A(25)
> Invalid alpha value specifier '-1' in 'red@-1'
> red@-1 -> error
> red@0.5 -> R(255) G(0) B(0) A(127)
> red@1.0 -> R(255) G(0) B(0) A(255)
> Invalid alpha value specifier '256' in 'red@256'
> red@256 -> error
> Invalid alpha value specifier '10foo' in 'red@10foo'
> red@10foo -> error
> Invalid alpha value specifier '-1.0' in 'red@-1.0'
> red@-1.0 -> error
> red@-0.0 -> R(255) G(0) B(0) A(0)
> 
> Testing av_small_strptime()
> fmt:'%Y-%m-%d' spec:'2012-12-21' -> 2012-12-21 00:00:00
> fmt:'%Y - %m - %d' spec:'2012-12-21' -> 2012-12-21 00:00:00
> fmt:'%Y-%m-%d %H:%M:%S' spec:'2012-12-21 20:12:21' -> 2012-12-21 20:12:21
> fmt:'  %Y - %m - %d %H : %M : %S' spec:'   2012 - 12 -  21   20 : 12 : 21'
> -> 2012-12-21 20:12:21
> fmt:'  %Y - %b - %d %H : %M : %S' spec:'   2012 - nOV -  21   20 : 12 : 21'
> -> 2012-11-21 20:12:21
> fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - nOVemBeR -  21   20 : 12
> : 21' -> 2012-11-21 20:12:21
> fmt:'  %Y - %B%d %H : %M : %S' spec:'   2012 - may21   20 : 12 : 21' ->
> 2012-05-21 20:12:21
> fmt:'  %Y - %B%d %H : %M : %S' spec:'   2012 - mby21   20 : 12 : 21' ->
> error
> fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - JunE -  21   20 : 12 :
> 21' -> 2012-06-21 20:12:21
> fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - Jane -  21   20 : 12 :
> 21' -> error
> fmt:'  %Y - %B - %d %H : %M : %S' spec:'   2012 - January -  21   20 : 12 :
> 21' -> 2012-01-21 20:12:21
> 
> Testing av_parse_time()
> (now is 2012-03-17 09:14:13.2 +0100, local time is UTC+1)
> now                      ->     1331972053.200000 = 2012-03-17T08:14:13Z
> 12:35:46                 ->     1331984146.000000 = 2012-03-17T11:35:46Z
> 2000-12-20 0:02:47.5z    ->      977270567.500000 = 2000-12-20T00:02:47Z
> 2012 - 02-22  17:44:07   ->     1329929047.000000 = 2012-02-22T16:44:07Z
> 2000-12-20T010247.6      ->      977270567.600000 = 2000-12-20T00:02:47Z
> 2000-12-12 1:35:46+05:30 ->      976565146.000000 = 2000-12-11T20:05:46Z
> 2002-12-12 22:30:40-02   ->     1039739440.000000 = 2002-12-13T00:30:40Z
> 2:34:56.79               ->           +9296790000
> -1:23:45.67              ->           -5025670000
> 42.1729                  ->             +42172900
> -1729.42                 ->           -1729420000
> 12:34                    ->            +754000000
> PT2M4.0S                 -> error
> 
> Testing av_get_known_color_name()
> AliceBlue -> R(240) G(248) B(255) A(0)
> AntiqueWhite -> R(250) G(235) B(215) A(0)
> Aqua -> R(0) G(255) B(255) A(0)
> Aquamarine -> R(127) G(255) B(212) A(0)
> Azure -> R(240) G(255) B(255) A(0)
> Beige -> R(245) G(245) B(220) A(0)
> Bisque -> R(255) G(228) B(196) A(0)
> Black -> R(0) G(0) B(0) A(0)
> BlanchedAlmond -> R(255) G(235) B(205) A(0)
> Blue -> R(0) G(0) B(255) A(0)
> BlueViolet -> R(138) G(43) B(226) A(0)
> Brown -> R(165) G(42) B(42) A(0)
> BurlyWood -> R(222) G(184) B(135) A(0)
> CadetBlue -> R(95) G(158) B(160) A(0)
> Chartreuse -> R(127) G(255) B(0) A(0)
> Chocolate -> R(210) G(105) B(30) A(0)
> Coral -> R(255) G(127) B(80) A(0)
> CornflowerBlue -> R(100) G(149) B(237) A(0)
> Cornsilk -> R(255) G(248) B(220) A(0)
> Crimson -> R(220) G(20) B(60) A(0)
> Cyan -> R(0) G(255) B(255) A(0)
> DarkBlue -> R(0) G(0) B(139) A(0)
> DarkCyan -> R(0) G(139) B(139) A(0)
> DarkGoldenRod -> R(184) G(134) B(11) A(0)
> DarkGray -> R(169) G(169) B(169) A(0)
> DarkGreen -> R(0) G(100) B(0) A(0)
> DarkKhaki -> R(189) G(183) B(107) A(0)
> DarkMagenta -> R(139) G(0) B(139) A(0)
> DarkOliveGreen -> R(85) G(107) B(47) A(0)
> Darkorange -> R(255) G(140) B(0) A(0)
> DarkOrchid -> R(153) G(50) B(204) A(0)
> DarkRed -> R(139) G(0) B(0) A(0)
> DarkSalmon -> R(233) G(150) B(122) A(0)
> DarkSeaGreen -> R(143) G(188) B(143) A(0)
> DarkSlateBlue -> R(72) G(61) B(139) A(0)
> DarkSlateGray -> R(47) G(79) B(79) A(0)
> DarkTurquoise -> R(0) G(206) B(209) A(0)
> DarkViolet -> R(148) G(0) B(211) A(0)
> DeepPink -> R(255) G(20) B(147) A(0)
> DeepSkyBlue -> R(0) G(191) B(255) A(0)
> DimGray -> R(105) G(105) B(105) A(0)
> DodgerBlue -> R(30) G(144) B(255) A(0)
> FireBrick -> R(178) G(34) B(34) A(0)
> FloralWhite -> R(255) G(250) B(240) A(0)
> ForestGreen -> R(34) G(139) B(34) A(0)
> Fuchsia -> R(255) G(0) B(255) A(0)
> Gainsboro -> R(220) G(220) B(220) A(0)
> GhostWhite -> R(248) G(248) B(255) A(0)
> Gold -> R(255) G(215) B(0) A(0)
> GoldenRod -> R(218) G(165) B(32) A(0)
> Gray -> R(128) G(128) B(128) A(0)
> Green -> R(0) G(128) B(0) A(0)
> GreenYellow -> R(173) G(255) B(47) A(0)
> HoneyDew -> R(240) G(255) B(240) A(0)
> HotPink -> R(255) G(105) B(180) A(0)
> IndianRed -> R(205) G(92) B(92) A(0)
> Indigo -> R(75) G(0) B(130) A(0)
> Ivory -> R(255) G(255) B(240) A(0)
> Khaki -> R(240) G(230) B(140) A(0)
> Lavender -> R(230) G(230) B(250) A(0)
> LavenderBlush -> R(255) G(240) B(245) A(0)
> LawnGreen -> R(124) G(252) B(0) A(0)
> LemonChiffon -> R(255) G(250) B(205) A(0)
> LightBlue -> R(173) G(216) B(230) A(0)
> LightCoral -> R(240) G(128) B(128) A(0)
> LightCyan -> R(224) G(255) B(255) A(0)
> LightGoldenRodYellow -> R(250) G(250) B(210) A(0)
> LightGreen -> R(144) G(238) B(144) A(0)
> LightGrey -> R(211) G(211) B(211) A(0)
> LightPink -> R(255) G(182) B(193) A(0)
> LightSalmon -> R(255) G(160) B(122) A(0)
> LightSeaGreen -> R(32) G(178) B(170) A(0)
> LightSkyBlue -> R(135) G(206) B(250) A(0)
> LightSlateGray -> R(119) G(136) B(153) A(0)
> LightSteelBlue -> R(176) G(196) B(222) A(0)
> LightYellow -> R(255) G(255) B(224) A(0)
> Lime -> R(0) G(255) B(0) A(0)
> LimeGreen -> R(50) G(205) B(50) A(0)
> Linen -> R(250) G(240) B(230) A(0)
> Magenta -> R(255) G(0) B(255) A(0)
> Maroon -> R(128) G(0) B(0) A(0)
> MediumAquaMarine -> R(102) G(205) B(170) A(0)
> MediumBlue -> R(0) G(0) B(205) A(0)
> MediumOrchid -> R(186) G(85) B(211) A(0)
> MediumPurple -> R(147) G(112) B(216) A(0)
> MediumSeaGreen -> R(60) G(179) B(113) A(0)
> MediumSlateBlue -> R(123) G(104) B(238) A(0)
> MediumSpringGreen -> R(0) G(250) B(154) A(0)
> MediumTurquoise -> R(72) G(209) B(204) A(0)
> MediumVioletRed -> R(199) G(21) B(133) A(0)
> MidnightBlue -> R(25) G(25) B(112) A(0)
> MintCream -> R(245) G(255) B(250) A(0)
> MistyRose -> R(255) G(228) B(225) A(0)
> Moccasin -> R(255) G(228) B(181) A(0)
> NavajoWhite -> R(255) G(222) B(173) A(0)
> Navy -> R(0) G(0) B(128) A(0)
> OldLace -> R(253) G(245) B(230) A(0)
> Olive -> R(128) G(128) B(0) A(0)
> OliveDrab -> R(107) G(142) B(35) A(0)
> Orange -> R(255) G(165) B(0) A(0)
> OrangeRed -> R(255) G(69) B(0) A(0)
> 
> 
> Orchid -> R(218) G(112) B(214) A(0)
> PaleGoldenRod -> R(238) G(232) B(170) A(0)
> PaleGreen -> R(152) G(251) B(152) A(0)
> PaleTurquoise -> R(175) G(238) B(238) A(0)
> PaleVioletRed -> R(216) G(112) B(147) A(0)
> PapayaWhip -> R(255) G(239) B(213) A(0)
> PeachPuff -> R(255) G(218) B(185) A(0)
> Peru -> R(205) G(133) B(63) A(0)
> Pink -> R(255) G(192) B(203) A(0)
> Plum -> R(221) G(160) B(221) A(0)
> PowderBlue -> R(176) G(224) B(230) A(0)
> Purple -> R(128) G(0) B(128) A(0)
> Red -> R(255) G(0) B(0) A(0)
> RosyBrown -> R(188) G(143) B(143) A(0)
> RoyalBlue -> R(65) G(105) B(225) A(0)
> SaddleBrown -> R(139) G(69) B(19) A(0)
> Salmon -> R(250) G(128) B(114) A(0)
> SandyBrown -> R(244) G(164) B(96) A(0)
> SeaGreen -> R(46) G(139) B(87) A(0)
> SeaShell -> R(255) G(245) B(238) A(0)
> Sienna -> R(160) G(82) B(45) A(0)
> Silver -> R(192) G(192) B(192) A(0)
> SkyBlue -> R(135) G(206) B(235) A(0)
> SlateBlue -> R(106) G(90) B(205) A(0)
> SlateGray -> R(112) G(128) B(144) A(0)
> Snow -> R(255) G(250) B(250) A(0)
> SpringGreen -> R(0) G(255) B(127) A(0)
> SteelBlue -> R(70) G(130) B(180) A(0)
> Tan -> R(210) G(180) B(140) A(0)
> Teal -> R(0) G(128) B(128) A(0)
> Thistle -> R(216) G(191) B(216) A(0)
> Tomato -> R(255) G(99) B(71) A(0)
> Turquoise -> R(64) G(224) B(208) A(0)
> Violet -> R(238) G(130) B(238) A(0)
> Wheat -> R(245) G(222) B(179) A(0)
> White -> R(255) G(255) B(255) A(0)
> WhiteSmoke -> R(245) G(245) B(245) A(0)
> Yellow -> R(255) G(255) B(0) A(0)
> YellowGreen -> R(154) G(205) B(50) A(0)
> 
> Testing av_find_info_tag()
> 0. tag1 found: val1
> 1. tag2 found: val2
> 2. tag3 found: val3
> 3. tag4 not found
> 4. tag41 found: value 41
> 5. 41 not found
> 6. random1 not found
> 
> 
> 
> 
> 
> 
> 
> 
>> 
>>> +{
>>> +    /* ISO-8601 duration parser */
>>> +    uint32_t days = 0;
>>> +    uint32_t hours = 0;
>>> +    uint32_t mins = 0;
>>> +    uint32_t secs = 0;
>>> +    uint32_t size = 0;
>>> +    float value = 0;
>>> +    uint8_t 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, "GetDurationInSec 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 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;
>>> +}
>>> +
>>> +/*
>>> + * Used to reset a statically allocated AVPacket to a clean slate,
>>> + * containing no data.
>>> + */
>>> +static void reset_packet(AVPacket *pkt)
>>> +{
>>> +    av_init_packet(pkt);
>>> +    pkt->data = NULL;
>>> +}
>>> +
>>> +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_packet_unref(&pls->pkt);
>>> +    reset_packet(&pls->pkt);
>>> +    av_freep(&pls->pb.buffer);
>>> +    if (pls->input)
>>> +        ff_format_io_close(pls->parent, &pls->input);
>>> +    if (pls->ctx) {
>>> +        pls->ctx->pb = NULL;
>>> +        avformat_close_input(&pls->ctx);
>>> +    }
>>> +
>>> +    av_free(pls->url_template);
>>> +    av_free(pls);
>>> +}
>>> +
>>> +static void update_options(char **dest, const char *name, void *src)
>>> +{
>>> +    av_freep(dest);
>>> +    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
>>> +    if (*dest)
>>> +        av_freep(dest);
>>> +}
>>> +
>>> +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, "http", NULL) &&
>> !av_strstart(proto_name, "file", NULL))
>>> +        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;
>>> +
>>> +    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
>>> +    if (ret >= 0) {
>>> +        // update cookies on http response with setcookies.
>>> +        void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
>>> +        update_options(&c->cookies, "cookies", u);
>>> +        av_dict_set(&opts, "cookies", c->cookies, 0);
>>> +    }
>>> +
>>> +    av_dict_free(&tmp);
>>> +
>>> +    if (is_http)
>>> +        *is_http = av_strstart(proto_name, "http", NULL);
>>> +
>>> +    return ret;
>>> +
>>> +}
>>> +
>>> +static char *strreplace(const char *str, const char *from, const char
>> *to)
>>> +{
>>> +    /* Adjust each of the below values to suit your needs. */
>>> +    /* Increment positions cache size initially by this number. */
>>> +    size_t cache_sz_inc = 16;
>>> +    /* Thereafter, each time capacity needs to be increased,
>>> +     * multiply the increment by this factor. */
>>> +    const size_t cache_sz_inc_factor = 3;
>>> +    /* But never increment capacity by more than this number. */
>>> +    const size_t cache_sz_inc_max = 1048576;
>>> +
>>> +    char *pret, *ret = NULL;
>>> +    const char *pstr2, *pstr = str;
>>> +    size_t i, count = 0;
>>> +    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
>>> +    size_t cache_sz = 0;
>>> +    size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
>>> +
>>> +    /* Find all matches and cache their positions. */
>>> +    while ((pstr2 = av_stristr(pstr, from))) {
>>> +        count++;
>>> +        /* Increase the cache size when necessary. */
>>> +        if (cache_sz < count) {
>>> +            cache_sz += cache_sz_inc;
>>> +            pos_cache_tmp = av_realloc(pos_cache, sizeof(*pos_cache) *
>> cache_sz);
>>> +            if (!pos_cache_tmp) {
>>> +                goto end_strreplace;
>>> +            } else pos_cache = pos_cache_tmp;
>>> +            cache_sz_inc *= cache_sz_inc_factor;
>>> +            if (cache_sz_inc > cache_sz_inc_max) {
>>> +                cache_sz_inc = cache_sz_inc_max;
>>> +            }
>>> +        }
>>> +
>>> +        pos_cache[count-1] = pstr2 - str;
>>> +        pstr = pstr2 + fromlen;
>>> +    }
>>> +    orglen = pstr - str + strlen(pstr);
>>> +    /* Allocate memory for the post-replacement string. */
>>> +    if (count > 0) {
>>> +        tolen = strlen(to);
>>> +        retlen = orglen + (tolen - fromlen) * count;
>>> +    } else {
>>> +        retlen = orglen;
>>> +    }
>>> +    ret = av_malloc(retlen + 1);
>>> +    if (!ret) {
>>> +        goto end_strreplace;
>>> +    }
>>> +
>>> +    if (!count) {
>>> +        /* If no matches, then just duplicate the string. */
>>> +        av_strlcpy(ret, str, retlen + 1);
>>> +    } else {
>>> +        /* Otherwise, duplicate the string whilst performing
>>> +         * the replacements using the position cache. */
>>> +        pret = ret;
>>> +        memcpy(pret, str, pos_cache[0]);
>>> +        pret += pos_cache[0];
>>> +        for (i = 0; i < count; i++) {
>>> +            memcpy(pret, to, tolen);
>>> +            pret += tolen;
>>> +            pstr = str + pos_cache[i] + fromlen;
>>> +            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) -
>> pos_cache[i] - fromlen;
>>> +            memcpy(pret, pstr, cpylen);
>>> +            pret += cpylen;
>>> +        }
>>> +        ret[retlen] = '\0';
>>> +    }
>>> +
>>> +end_strreplace:
>>> +    /* Free the cache and return the post-replacement string,
>>> +     * which will be NULL in the event of an error. */
>>> +    av_free(pos_cache);
>>> +    return ret;
>>> +}
>>> +
>>> +static char *replace_template_str(const char *url, const char *marker)
>>> +{
>>> +    char *prefix = NULL;
>>> +    char *start = 0;
>>> +    char *end = NULL;
>>> +    char *tmp_url = NULL;
>>> +    char *new_url = NULL;
>>> +    int marker_len;
>>> +
>>> +    prefix = av_strdup(marker);
>>> +    marker_len = strlen(prefix) - 1;
>>> +    prefix[marker_len] = '\0';
>>> +
>>> +    start = av_stristr(url, prefix);
>>> +    if (!start)
>>> +        goto finish;
>>> +    end = strchr(start + 1, '$');
>>> +    if (!end)
>>> +        goto finish;
>>> +
>>> +    tmp_url = av_mallocz(MAX_URL_SIZE);
>>> +    if (!tmp_url) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    av_strlcpy(tmp_url, url, start - url+ 1);
>>> +    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end -
>> start - marker_len + 1);
>>> +    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
>>> +
>>> +finish:
>>> +    av_free(prefix);
>>> +    new_url = strreplace(tmp_url ? tmp_url : url, marker, "%"PRId64);
>> 
>> Since you're no longer using a printf function, this replacement doesn't
>> make sense. You should replace with the actual value you want to substitute
>> instead.
>> 
>>> +    av_free(tmp_url);
>>> +    return new_url;
>>> +}
>>> +
>>> +static char *get_content_url(xmlNodePtr *baseurl_nodes, int
>> n_baseurl_nodes, xmlChar *rep_id_val, xmlChar *rep_bandwidth_val, xmlChar
>> *val)
>>> +{
>>> +    int i;
>>> +    xmlChar *text;
>>> +    char *url = NULL;
>>> +    char *tmp_str = av_mallocz(MAX_URL_SIZE);
>>> +    char *tmp_str_2 = NULL;
>>> +
>>> +    if (!tmp_str) {
>>> +        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) {
>>> +                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
>>> +                if (!tmp_str_2) {
>>> +                    av_free(tmp_str);
>>> +                    return NULL;
>>> +                }
>>> +                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str,
>> text);
>>> +                av_free(tmp_str);
>>> +                tmp_str = tmp_str_2;
>>> +                xmlFree(text);
>>> +            }
>>> +        }
>>> +    }
>>> +    if (val)
>>> +        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
>>> +    if (rep_id_val) {
>>> +        url = strreplace(tmp_str, "$RepresentationID$", (const
>> char*)rep_id_val);
>>> +        av_free(tmp_str);
>>> +        tmp_str = url;
>>> +    }
>>> +    if (rep_bandwidth_val && tmp_str)
>>> +        url = strreplace(tmp_str, "$Bandwidth$", (const
>> char*)rep_bandwidth_val);
>>> +    if (tmp_str != url)
>>> +        av_free(tmp_str);
>>> +    return url;
>>> +}
>>> +
>>> +static xmlChar *get_val_from_nodes_tab(xmlNodePtr *nodes, const int
>> n_nodes, const xmlChar *attrname)
>>> +{
>>> +    int i;
>>> +    xmlChar *val;
>>> +
>>> +    for (i = 0; i < n_nodes; ++i) {
>>> +        if (nodes[i]) {
>>> +            val = xmlGetProp(nodes[i], attrname);
>>> +            if (val)
>>> +                return val;
>>> +        }
>>> +    }
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +static xmlNodePtr findChildNodeByName(xmlNodePtr rootnode, const
>> xmlChar *nodename)
>>> +{
>>> +    xmlNodePtr node = rootnode;
>>> +    if (!node) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    node = xmlFirstElementChild(node);
>>> +    while (node) {
>>> +        if (!xmlStrcmp(node->name, nodename)) {
>>> +            return node;
>>> +        }
>>> +        node = xmlNextElementSibling(node);
>>> +    }
>>> +    return NULL;
>>> +}
>>> +
>>> +static enum AVMediaType get_content_type(xmlNodePtr node)
>>> +{
>>> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
>>> +    int i = 0;
>>> +    const char *attr;
>>> +
>>> +    if (node) {
>>> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
>>> +            attr = (i) ? "mimeType" : "contentType";
>>> +            xmlChar *val = xmlGetProp(node, attr);
>>> +            if (val) {
>>> +                if (av_stristr((const char *)val, "video")) {
>>> +                    type = AVMEDIA_TYPE_VIDEO;
>>> +                } else if (av_stristr((const char *)val, "audio")) {
>>> +                    type = AVMEDIA_TYPE_AUDIO;
>>> +                }
>>> +                xmlFree(val);
>>> +            }
>>> +            i++;
>>> +        }
>>> +    }
>>> +    return type;
>>> +}
>>> +
>>> +static int parse_manifest_segmenturlnode(AVFormatContext *s, struct
>> representation *rep,
>>> +                                          xmlNodePtr fragmenturl_node,
>>> +                                          xmlNodePtr *baseurl_nodes,
>>> +                                          xmlChar *rep_id_val,
>>> +                                          xmlChar *rep_bandwidth_val)
>>> +{
>>> +    xmlChar *initialization_val = NULL;
>>> +    xmlChar *media_val = NULL;
>>> +
>>> +    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
>> *)"Initialization")) {
>>> +        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
>>> +        if (initialization_val) {
>>> +            rep->init_section = av_mallocz(sizeof(struct fragment));
>>> +            if (!rep->init_section) {
>>> +                xmlFree(initialization_val);
>>> +                return AVERROR(ENOMEM);
>>> +            }
>>> +            rep->init_section->url = get_content_url(baseurl_nodes, 4,
>> rep_id_val, rep_bandwidth_val, initialization_val);
>>> +            rep->init_section->size = -1;
>>> +            xmlFree(initialization_val);
>>> +        }
>>> +    } else if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
>> *)"SegmentURL")) {
>>> +        media_val = xmlGetProp(fragmenturl_node, "media");
>>> +        if (media_val) {
>>> +            struct fragment *seg = av_mallocz(sizeof(struct fragment));
>>> +            if (!seg) {
>>> +                xmlFree(media_val);
>>> +                return AVERROR(ENOMEM);
>>> +            }
>>> +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
>> rep_bandwidth_val, media_val);
>>> +            seg->size = -1;
>>> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
>>> +            xmlFree(media_val);
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int parse_manifest_segmenttimeline(AVFormatContext *s, struct
>> representation *rep,
>>> +                                           xmlNodePtr
>> fragment_timeline_node)
>>> +{
>>> +    xmlAttrPtr attr = NULL;
>>> +    xmlChar *val  = NULL;
>>> +
>>> +    if (!xmlStrcmp(fragment_timeline_node->name, (const xmlChar
>> *)"S")) {
>>> +        struct timeline *tml = av_mallocz(sizeof(struct timeline));
>>> +        if (!tml) {
>>> +            return AVERROR(ENOMEM);
>>> +        }
>>> +        attr = fragment_timeline_node->properties;
>>> +        while (attr) {
>>> +            val = xmlGetProp(fragment_timeline_node, attr->name);
>>> +
>>> +            if (!val) {
>>> +                av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline
>> attr->name = %s val is NULL\n", attr->name);
>>> +                continue;
>>> +            }
>>> +
>>> +            if (!xmlStrcmp(attr->name, (const xmlChar *)"t")) {
>>> +                tml->t = (int64_t)atoll((const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
>>> +                tml->r =(int32_t) atoi((const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
>>> +                tml->d = (int64_t)atoll((const char *)val);
>>> +                rep->fragment_duration = (int64_t) atoll((const char
>> *)val);
>>> +            }
>>> +            attr = attr->next;
>>> +            xmlFree(val);
>>> +        }
>>> +        dynarray_add(&rep->timelines, &rep->n_timelines, tml);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int parse_manifest_representation(AVFormatContext *s, const
>> char *url,
>>> +                                          xmlNodePtr node,
>>> +                                          xmlNodePtr adaptionset_node,
>>> +                                          xmlNodePtr mpd_baseurl_node,
>>> +                                          xmlNodePtr
>> period_baseurl_node,
>>> +                                          xmlNodePtr
>> fragment_template_node,
>>> +                                          xmlNodePtr
>> content_component_node,
>>> +                                          xmlNodePtr
>> adaptionset_baseurl_node)
>>> +{
>>> +    int32_t ret = 0;
>>> +    int32_t audio_rep_idx = 0;
>>> +    int32_t video_rep_idx = 0;
>>> +    char *temp_string = NULL;
>>> +    DASHContext *c = s->priv_data;
>>> +    struct representation *rep = NULL;
>>> +    struct fragment *seg = NULL;
>>> +    xmlNodePtr representation_segmenttemplate_node = NULL;
>>> +    xmlNodePtr representation_baseurl_node = NULL;
>>> +    xmlNodePtr representation_segmentlist_node = NULL;
>>> +    xmlNodePtr fragment_timeline_node = NULL;
>>> +    xmlNodePtr fragment_templates_tab[2];
>>> +    xmlChar *duration_val = NULL;
>>> +    xmlChar *startnumber_val = NULL;
>>> +    xmlChar *timescale_val = NULL;
>>> +    xmlChar *initialization_val = NULL;
>>> +    xmlChar *media_val = NULL;
>>> +    xmlNodePtr baseurl_nodes[4];
>>> +    xmlNodePtr representation_node = node;
>>> +    xmlChar *rep_id_val = xmlGetProp(representation_node, "id");
>>> +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node,
>> "bandwidth");
>>> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
>>> +
>>> +    // try get information from representation
>>> +    if (type == AVMEDIA_TYPE_UNKNOWN)
>>> +        type = get_content_type(representation_node);
>>> +    // try get information from contentComponen
>>> +    if (type == AVMEDIA_TYPE_UNKNOWN)
>>> +        type = get_content_type(content_component_node);
>>> +    // try get information from adaption set
>>> +    if (type == AVMEDIA_TYPE_UNKNOWN)
>>> +        type = get_content_type(adaptionset_node);
>>> +    if (type == AVMEDIA_TYPE_UNKNOWN) {
>>> +        av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported
>> representation type\n", url);
>>> +    } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type
>> == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
>>> +        // convert selected representation to our internal struct
>>> +        rep = av_mallocz(sizeof(struct representation));
>>> +        if (!rep) {
>>> +            ret = AVERROR(ENOMEM);
>>> +            goto end;
>>> +        }
>>> +        representation_segmenttemplate_node = findChildNodeByName(representation_node,
>> "SegmentTemplate");
>>> +        representation_baseurl_node = findChildNodeByName(representation_node,
>> "BaseURL");
>>> +        representation_segmentlist_node = findChildNodeByName(representation_node,
>> "SegmentList");
>>> +        reset_packet(&rep->pkt);
>>> +
>>> +        baseurl_nodes[0] = mpd_baseurl_node;
>>> +        baseurl_nodes[1] = period_baseurl_node;
>>> +        baseurl_nodes[2] = adaptionset_baseurl_node;
>>> +        baseurl_nodes[3] = representation_baseurl_node;
>>> +
>>> +        if (representation_segmenttemplate_node ||
>> fragment_template_node) {
>>> +            fragment_timeline_node = NULL;
>>> +            fragment_templates_tab[0] = representation_
>> segmenttemplate_node;
>>> +            fragment_templates_tab[1] = fragment_template_node;
>>> +
>>> +            duration_val = get_val_from_nodes_tab(fragment_templates_tab,
>> 2, "duration");
>>> +            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab,
>> 2, "startNumber");
>>> +            timescale_val = get_val_from_nodes_tab(fragment_templates_tab,
>> 2, "timescale");
>>> +            initialization_val = get_val_from_nodes_tab(fragment_templates_tab,
>> 2, "initialization");
>>> +            media_val = get_val_from_nodes_tab(fragment_templates_tab,
>> 2, "media");
>>> +
>>> +            if (initialization_val) {
>>> +                rep->init_section = av_mallocz(sizeof(struct fragment));
>>> +                if (!rep->init_section) {
>>> +                    av_free(rep);
>>> +                    ret = AVERROR(ENOMEM);
>>> +                    goto end;
>>> +                }
>>> +                rep->init_section->url = get_content_url(baseurl_nodes,
>> 4, rep_id_val, rep_bandwidth_val, initialization_val);
>>> +                rep->init_section->size = -1;
>>> +                xmlFree(initialization_val);
>>> +            }
>>> +
>>> +            if (media_val) {
>>> +                temp_string = get_content_url(baseurl_nodes, 4,
>> rep_id_val, rep_bandwidth_val, media_val);
>>> +                if (temp_string) {
>>> +                    if (av_stristr(temp_string, "$Number")) {
>>> +                        rep->url_template = av_strdup(temp_string);
>> 
>> Since you're no longer replacing here, this if/else tree mostly doesn't
>> make sense anymore. You should remove the av_free below and store the
>> result of get_content_url() directly to rep->url_template, and just have a
>> check here to set tmp_url_type. Also, you need to check for a NULL return
>> from get_content_url.
>> 
>>> +                        /* A URL template is provided from which
>> clients build a chunk list
>>> +                         * where the chunk URLs include chunk numbers
>> (like index numbers).
>>> +                         */
>>> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /*
>> Number-Based. */
>>> +                    } else if (av_stristr(temp_string, "$Time")) {
>>> +                        rep->url_template = av_strdup(temp_string);
>>> +                        /* A URL template is provided from which
>> clients build a chunk list
>>> +                         * where the chunk URLs include chunk start
>> times.
>>> +                         */
>>> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /*
>> Time-Based. */
>>> +                    } else {
>>> +                        rep->url_template = temp_string;
>>> +                        temp_string = NULL;
>>> +                    }
>>> +                    av_free(temp_string);
>>> +                }
>>> +                xmlFree(media_val);
>>> +            }
>>> +            if (duration_val) {
>>> +                rep->fragment_duration = (int64_t) atoll((const char
>> *)duration_val);
>>> +                xmlFree(duration_val);
>>> +            }
>>> +            if (timescale_val) {
>>> +                rep->fragment_timescale = (int64_t) atoll((const char
>> *)timescale_val);
>>> +                xmlFree(timescale_val);
>>> +            }
>>> +            if (startnumber_val) {
>>> +                rep->first_seq_no = (int64_t) atoll((const char
>> *)startnumber_val);
>>> +                xmlFree(startnumber_val);
>>> +            }
>>> +
>>> +            fragment_timeline_node = findChildNodeByName(
>> representation_segmenttemplate_node, "SegmentTimeline");
>>> +
>>> +            if (!fragment_timeline_node)
>>> +                fragment_timeline_node = findChildNodeByName(fragment_template_node,
>> "SegmentTimeline");
>>> +            if (fragment_timeline_node) {
>>> +                fragment_timeline_node = xmlFirstElementChild(fragment_
>> timeline_node);
>>> +                while (fragment_timeline_node) {
>>> +                    ret = parse_manifest_segmenttimeline(s, rep,
>> fragment_timeline_node);
>>> +                    if (ret < 0) {
>>> +                        return ret;
>>> +                    }
>>> +                    fragment_timeline_node = xmlNextElementSibling(
>> fragment_timeline_node);
>>> +                }
>>> +            }
>>> +        } else if (representation_baseurl_node &&
>> !representation_segmentlist_node) {
>>> +            seg = av_mallocz(sizeof(struct fragment));
>>> +            if (!seg) {
>>> +                ret = AVERROR(ENOMEM);
>>> +                goto end;
>>> +            }
>>> +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
>> rep_bandwidth_val, NULL);
>>> +            seg->size = -1;
>>> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
>>> +        } else if (!representation_baseurl_node &&
>> representation_segmentlist_node) {
>>> +            // TODO: https://www.brendanlong.com/
>> the-structure-of-an-mpeg-dash-mpd.html
>>> +            // http://www-itec.uni-klu.ac.at/
>> dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
>>> +            xmlNodePtr fragmenturl_node = NULL;
>>> +            duration_val = xmlGetProp(representation_segmentlist_node,
>> "duration");
>>> +            timescale_val = xmlGetProp(representation_segmentlist_node,
>> "timescale");
>>> +            if (duration_val) {
>>> +                rep->fragment_duration = (int64_t) atoll((const char
>> *)duration_val);
>>> +                xmlFree(duration_val);
>>> +            }
>>> +            if (timescale_val) {
>>> +                rep->fragment_timescale = (int64_t) atoll((const char
>> *)timescale_val);
>>> +                xmlFree(timescale_val);
>>> +            }
>>> +            fragmenturl_node = xmlFirstElementChild(
>> representation_segmentlist_node);
>>> +            while (fragmenturl_node) {
>>> +                ret = parse_manifest_segmenturlnode(s, rep,
>> fragmenturl_node, baseurl_nodes, rep_id_val, rep_bandwidth_val);
>>> +                if (ret < 0) {
>>> +                    return ret;
>>> +                }
>>> +                fragmenturl_node = xmlNextElementSibling(
>> fragmenturl_node);
>>> +            }
>>> +        } else {
>>> +            free_representation(rep);
>>> +            rep = NULL;
>>> +            av_log(s, AV_LOG_ERROR, "Unknown format of Representation
>> node id[%s] \n", (const char *)rep_id_val);
>>> +        }
>>> +
>>> +        if (rep) {
>>> +            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
>>> +                rep->fragment_timescale = 1;
>>> +            if (type == AVMEDIA_TYPE_VIDEO) {
>>> +                rep->rep_idx = video_rep_idx;
>>> +                c->cur_video = rep;
>>> +            } else {
>>> +                rep->rep_idx = audio_rep_idx;
>>> +                c->cur_audio = rep;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
>>> +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
>>> +
>>> +end:
>>> +    if (rep_id_val)
>>> +        xmlFree(rep_id_val);
>>> +    if (rep_bandwidth_val)
>>> +        xmlFree(rep_bandwidth_val);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int parse_manifest_adaptationset(AVFormatContext *s, const char
>> *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, xmlNodePtr
>> period_baseurl_node)
>>> +{
>>> +    int ret = 0;
>>> +    xmlNodePtr fragment_template_node = NULL;
>>> +    xmlNodePtr content_component_node = NULL;
>>> +    xmlNodePtr adaptionset_baseurl_node = NULL;
>>> +    xmlNodePtr node = NULL;
>>> +
>>> +    node = xmlFirstElementChild(adaptionset_node);
>>> +    while (node) {
>>> +        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate"))
>> {
>>> +            fragment_template_node = node;
>>> +        } else if (!xmlStrcmp(node->name, (const xmlChar
>> *)"ContentComponent")) {
>>> +            content_component_node = node;
>>> +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
>>> +            adaptionset_baseurl_node = node;
>>> +        } else if (!xmlStrcmp(node->name, (const xmlChar
>> *)"Representation")) {
>>> +            ret = parse_manifest_representation(s, url, node,
>>> +                                                 adaptionset_node,
>>> +                                                 mpd_baseurl_node,
>>> +                                                 period_baseurl_node,
>>> +                                                 fragment_template_node,
>>> +                                                 content_component_node,
>>> +
>> adaptionset_baseurl_node);
>>> +            if (ret < 0) {
>>> +                return ret;
>>> +            }
>>> +        }
>>> +        node = xmlNextElementSibling(node);
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +
>>> +static int parse_manifest(AVFormatContext *s, const char *url,
>> AVIOContext *in)
>>> +{
>>> +    DASHContext *c = s->priv_data;
>>> +    int ret = 0;
>>> +    int close_in = 0;
>>> +    uint8_t *new_url = NULL;
>>> +    int64_t filesize = 0;
>>> +    char *buffer = NULL;
>>> +    AVDictionary *opts = NULL;
>>> +    xmlDoc *doc = NULL;
>>> +    xmlNodePtr root_element = NULL;
>>> +    xmlNodePtr node = NULL;
>>> +    xmlNodePtr period_node = NULL;
>>> +    xmlNodePtr mpd_baseurl_node = NULL;
>>> +    xmlNodePtr period_baseurl_node = NULL;
>>> +    xmlNodePtr adaptionset_node = NULL;
>>> +    xmlAttrPtr attr = NULL;
>>> +    xmlChar *val  = NULL;
>>> +    uint32_t perdiod_duration_sec = 0;
>>> +    uint32_t perdiod_start_sec = 0;
>>> +    int32_t audio_rep_idx = 0;
>>> +    int32_t video_rep_idx = 0;
>>> +
>>> +    if (!in) {
>>> +        close_in = 1;
>>> +        /* This is XML manifest there is no need to set range header */
>>> +        av_dict_set(&opts, "seekable", "0", 0);
>>> +        // broker prior HTTP options that should be consistent across
>> requests
>>> +        av_dict_set(&opts, "user-agent", c->user_agent, 0);
>>> +        av_dict_set(&opts, "cookies", c->cookies, 0);
>>> +        av_dict_set(&opts, "headers", c->headers, 0);
>>> +
>>> +        ret = avio_open2(&in, url, AVIO_FLAG_READ,
>> c->interrupt_callback, &opts);
>>> +        av_dict_free(&opts);
>>> +        if (ret < 0)
>>> +            return ret;
>>> +    }
>>> +
>>> +    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >=
>> 0) {
>>> +        c->base_url = av_strdup(new_url);
>>> +    } else {
>>> +        c->base_url = av_strdup(url);
>>> +    }
>>> +
>>> +    filesize = avio_size(in);
>>> +    if (filesize <= 0){
>> 
>> Space between ) and { (this occurs a few times).
>> 
>>> +        filesize = 8 * 1024;
>>> +    }
>>> +
>>> +    buffer = av_mallocz(filesize);
>>> +    if (!buffer) {
>>> +        return AVERROR(ENOMEM);
>>> +    }
>>> +
>>> +    filesize = avio_read(in, buffer, filesize);
>>> +    if (filesize > 0) {
>>> +        LIBXML_TEST_VERSION
>>> +
>>> +        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
>>> +        root_element = xmlDocGetRootElement(doc);
>>> +        node = root_element;
>>> +
>>> +        if (!node) {
>>> +            ret = AVERROR_INVALIDDATA;
>>> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
>> root node\n", url);
>>> +            goto cleanup;
>>> +        }
>>> +
>>> +        if (node->type != XML_ELEMENT_NODE ||
>>> +            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
>>> +            ret = AVERROR_INVALIDDATA;
>>> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root
>> node name[%s] type[%d]\n", url, node->name, (int)node->type);
>>> +            goto cleanup;
>>> +        }
>>> +
>>> +        val = xmlGetProp(node, "type");
>>> +        if (!val) {
>>> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
>> type attrib\n", url);
>>> +            ret = AVERROR_INVALIDDATA;
>>> +            goto cleanup;
>>> +        }
>>> +        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
>>> +            c->is_live = 1;
>>> +        xmlFree(val);
>>> +
>>> +        attr = node->properties;
>>> +        while (attr) {
>>> +            val = xmlGetProp(node, attr->name);
>>> +
>>> +            if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"availabilityStartTime")) {
>>> +                c->availability_start_time_sec = GetUTCDateTimeInSec(s,
>> (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"publishTime")) {
>>> +                c->publish_time_sec = GetUTCDateTimeInSec(s, (const
>> char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"minimumUpdatePeriod")) {
>>> +                c->minimum_update_period_sec = GetDurationInSec(s,
>> (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"timeShiftBufferDepth")) {
>>> +                c->time_shift_buffer_depth_sec = GetDurationInSec(s,
>> (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"minBufferTime")) {
>>> +                c->min_buffer_time_sec = GetDurationInSec(s, (const
>> char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"suggestedPresentationDelay")) {
>>> +                c->suggested_presentation_delay_sec =
>> GetDurationInSec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"mediaPresentationDuration")) {
>>> +                c->media_presentation_duration_sec =
>> GetDurationInSec(s, (const char *)val);
>>> +            }
>>> +            attr = attr->next;
>>> +            xmlFree(val);
>>> +        }
>>> +
>>> +        mpd_baseurl_node = findChildNodeByName(node, "BaseURL");
>>> +
>>> +        // at now we can handle only one period, with the longest
>> duration
>>> +        node = xmlFirstElementChild(node);
>>> +        while (node) {
>>> +            if (!xmlStrcmp(node->name, (const xmlChar *)"Period")) {
>>> +                perdiod_duration_sec = 0;
>>> +                perdiod_start_sec = 0;
>>> +                attr = node->properties;
>>> +                while (attr) {
>>> +                    val = xmlGetProp(node, attr->name);
>>> +                    if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"duration")) {
>>> +                        perdiod_duration_sec = GetDurationInSec(s,
>> (const char *)val);
>>> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar
>> *)"start")) {
>>> +                        perdiod_start_sec = GetDurationInSec(s, (const
>> char *)val);
>>> +                    }
>>> +                    attr = attr->next;
>>> +                    xmlFree(val);
>>> +                }
>>> +                if ((perdiod_duration_sec) >= (c->period_duration_sec))
>> {
>>> +                    period_node = node;
>>> +                    c->period_duration_sec = perdiod_duration_sec;
>>> +                    c->period_start_sec = perdiod_start_sec;
>>> +                    if (c->period_start_sec > 0)
>>> +                        c->media_presentation_duration_sec =
>> c->period_duration_sec;
>>> +                }
>>> +            }
>>> +            node = xmlNextElementSibling(node);
>>> +        }
>>> +        if (!period_node) {
>>> +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
>> Period node\n", url);
>>> +            ret = AVERROR_INVALIDDATA;
>>> +            goto cleanup;
>>> +        }
>>> +        // explore AdaptationSet
>>> +        adaptionset_node = xmlFirstElementChild(period_node);
>>> +        while (adaptionset_node) {
>>> +            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar
>> *)"BaseURL")) {
>>> +                period_baseurl_node = adaptionset_node;
>>> +            } else if (!xmlStrcmp(adaptionset_node->name, (const
>> xmlChar *)"AdaptationSet")) {
>>> +                parse_manifest_adaptationset(s, url, adaptionset_node,
>> mpd_baseurl_node, period_baseurl_node);
>>> +            }
>>> +            adaptionset_node = xmlNextElementSibling(adaptionset_node);
>>> +        }
>>> +        if (c->cur_video) {
>>> +            c->cur_video->rep_count = video_rep_idx;
>>> +            c->cur_video->fix_multiple_stsd_order = 1;
>>> +            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n",
>> (int)c->cur_video->rep_idx);
>>> +            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n",
>> (int)video_rep_idx);
>>> +        }
>>> +        if (c->cur_audio) {
>>> +            c->cur_audio->rep_count = audio_rep_idx;
>>> +        }
>>> +
>>> +cleanup:
>>> +        /*free the document */
>>> +        xmlFreeDoc(doc);
>>> +        xmlCleanupParser();
>>> +    } else {
>>> +        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
>>> +        ret = AVERROR_INVALIDDATA;
>>> +    }
>>> +
>>> +    av_free(new_url);
>>> +    av_free(buffer);
>>> +    if (close_in) {
>>> +        avio_close(in);
>>> +    }
>>> +    return ret;
>>> +}
>>> +
>>> +static int64_t calc_cur_seg_no(AVFormatContext *s, struct
>> representation *pls)
>>> +{
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +    int64_t num = 0;
>>> +
>>> +    if (c->is_live) {
>>> +        if (!pls->fragment_duration) {
>>> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
>> value in live mode\n");
>>> +            return pls->first_seq_no;
>>> +        }
>>> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
>> c->availability_start_time_sec) - c->presentation_delay_sec) *
>> pls->fragment_timescale) / pls->fragment_duration;
>>> +    } else {
>>> +        num = pls->first_seq_no;
>>> +    }
>>> +    return num;
>>> +}
>>> +
>>> +static int64_t calc_min_seg_no(AVFormatContext *s, struct
>> representation *pls)
>>> +{
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +    int64_t num = 0;
>>> +
>>> +    if (c->is_live) {
>>> +        if (!pls->fragment_duration) {
>>> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
>> value in live mode\n");
>>> +            return pls->first_seq_no;
>>> +        }
>>> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
>> c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) *
>> pls->fragment_timescale)  / pls->fragment_duration;
>>> +    } else {
>>> +        num = pls->first_seq_no;
>>> +    }
>>> +    return num;
>>> +}
>>> +
>>> +static int64_t calc_max_seg_no(AVFormatContext *s, struct
>> representation *pls)
>>> +{
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +    int64_t num = 0;
>>> +    int64_t i = 0;
>>> +
>>> +    if (c->is_live) {
>>> +        if (!pls->fragment_duration) {
>>> +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
>> value in live mode\n");
>>> +            return pls->first_seq_no;
>>> +        }
>>> +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
>> c->availability_start_time_sec)) * pls->fragment_timescale)  /
>> pls->fragment_duration;
>>> +    } else {
>>> +        if (pls->n_fragments) {
>>> +            num = pls->first_seq_no + pls->n_fragments - 1;
>>> +        } else if (pls->n_timelines) {
>>> +            num = pls->first_seq_no + pls->n_timelines - 1;
>>> +            for (i = 0; i < pls->n_timelines; ++i) {
>>> +                num += pls->timelines[i]->r;
>>> +            }
>>> +        } else {
>>> +            num = pls->first_seq_no + (c->media_presentation_duration_sec
>> * pls->fragment_timescale) / pls->fragment_duration;
>>> +        }
>>> +    }
>>> +
>>> +    return num;
>>> +}
>>> +
>>> +static int64_t get_fragment_start_time(struct representation *pls,
>> int64_t cur_seq_no)
>>> +{
>>> +    int64_t i = 0;
>>> +    int64_t j = 0;
>>> +    int64_t num = 0;
>>> +    int64_t startTime = 0;
>>> +
>>> +    if (pls->n_timelines) {
>>> +        for (i = 0; i < pls->n_timelines; ++i) {
>>> +            if (pls->timelines[i]->t > 0) {
>>> +                startTime = pls->timelines[i]->t;
>>> +            }
>>> +            if (num == cur_seq_no)
>>> +                goto finish;
>>> +            startTime += pls->timelines[i]->d;
>>> +            for (j = 0; j < pls->timelines[i]->r; ++j) {
>>> +                num++;
>>> +                if (num == cur_seq_no)
>>> +                    goto finish;
>>> +                startTime += pls->timelines[i]->d;
>>> +            }
>>> +            num++;
>>> +        }
>>> +    }
>>> +
>>> +finish:
>>> +    return startTime;
>>> +}
>>> +
>>> +static struct fragment *get_current_fragment(struct representation *pls)
>>> +{
>>> +    int64_t tmp_val = 0;
>>> +    int64_t min_seq_no = 0;
>>> +    int64_t max_seq_no = 0;
>>> +    char buffer[1024];
>>> +    char *tmp_str = NULL;
>>> +    struct fragment *seg = NULL;
>>> +    struct fragment *seg_ptr = NULL;
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +
>>> +    if (pls->n_fragments > 0) {
>>> +        if (pls->cur_seq_no < pls->n_fragments) {
>>> +            seg_ptr = pls->fragments[pls->cur_seq_no];
>>> +            seg = av_mallocz(sizeof(struct fragment));
>>> +            if (!seg) {
>>> +                return NULL;
>>> +            }
>>> +            seg->url = av_strdup(seg_ptr->url);
>>> +            if (!seg->url) {
>>> +                av_free(seg);
>>> +                return NULL;
>>> +            }
>>> +            seg->size = seg_ptr->size;
>>> +            seg->url_offset = seg_ptr->url_offset;
>>> +            return seg;
>>> +        }
>>> +    }
>>> +    if (c->is_live) {
>>> +        while (1) {
>>> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
>>> +            max_seq_no = calc_max_seg_no(pls->parent, pls);
>>> +
>>> +            if (pls->cur_seq_no <= min_seq_no) {
>>> +                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment:
>> cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
>>> +                       (int64_t)pls->cur_seq_no, min_seq_no,
>> max_seq_no, (int)pls->rep_idx);
>>> +                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
>>> +            } else if (pls->cur_seq_no > max_seq_no) {
>>> +                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment:
>> min[%"PRId64"] max[%"PRId64"], playlist %d\n",
>>> +                       min_seq_no, max_seq_no, (int)pls->rep_idx);
>>> +                av_usleep(1000);
>>> +                continue;
>>> +            }
>>> +            break;
>>> +        }
>>> +        seg = av_mallocz(sizeof(struct fragment));
>>> +        if (!seg) {
>>> +            return NULL;
>>> +        }
>>> +    } else if (pls->cur_seq_no <= pls->last_seq_no) {
>>> +        seg = av_mallocz(sizeof(struct fragment));
>>> +        if (!seg) {
>>> +            return NULL;
>>> +        }
>>> +    }
>>> +    if (seg) {
>>> +        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
>>> +            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ?
>> pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
>>> +            if (av_get_frame_filename(buffer, sizeof(buffer),
>> pls->url_template, (int64_t)tmp_val) < 0) {
>> 
>> It's not a security issue like with printf functions, but you shouldn't
>> use av_get_frame_filename() with arbitrary format-string input either, as
>> it will fail if the input already contained a '%' character. I also don't
>> understand how this is supposed to work, since your source string
>> (url_template) shouldn't contain a "%d"?
>> 
>>> +                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment
>> filename template %s\n", pls->url_template);
>>> +                return NULL;
>>> +            }
>>> +
>>> +            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
>>> +                tmp_str = replace_template_str(buffer, "$Number$");
>>> +            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME){
>>> +                tmp_str = replace_template_str(buffer, "$Time$");
>>> +            } else {
>>> +                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type
>> [%d]\n", pls->tmp_url_type);
>> 
>> Since this shouldn't be possible, an av_assert0 is applicable; no need to
>> log the value.
>> 
>>> +                return NULL;
>>> +            }
>>> +
>>> +            seg->url = av_strdup(tmp_str);
>>> +            if (!seg->url) {
>>> +                av_free(tmp_str);
>>> +                return NULL;
>>> +            }
>>> +        } else {
>>> +            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to
>> resolve template url '%s'\n", pls->url_template);
>>> +            seg->url = av_strdup(pls->url_template);
>>> +            if (!seg->url) {
>>> +                return NULL;
>>> +            }
>>> +        }
>>> +        seg->size = -1;
>>> +    }
>>> +
>>> +    av_free(tmp_str);
>>> +    return seg;
>>> +}
>>> +
>>> +enum ReadFromURLMode {
>>> +    READ_NORMAL,
>>> +    READ_COMPLETE,
>>> +};
>>> +
>>> +static int read_from_url(struct representation *pls, struct fragment
>> *seg,
>>> +                         uint8_t *buf, int buf_size,
>>> +                         enum ReadFromURLMode mode)
>>> +{
>>> +    int ret;
>>> +
>>> +    /* limit read if the fragment was only a part of a file */
>>> +    if (seg->size >= 0)
>>> +        buf_size = FFMIN(buf_size, pls->cur_seg_size -
>> pls->cur_seg_offset);
>>> +
>>> +    if (mode == READ_COMPLETE) {
>>> +        ret = avio_read(pls->input, buf, buf_size);
>>> +        if (ret < buf_size) {
>>> +            av_log(pls->parent, AV_LOG_WARNING, "Could not read
>> complete fragment.\n");
>>> +        }
>>> +    } else {
>>> +        ret = avio_read(pls->input, buf, buf_size);
>>> +    }
>>> +    if (ret > 0)
>>> +        pls->cur_seg_offset += ret;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int open_input(DASHContext *c, struct representation *pls,
>> struct fragment *seg)
>>> +{
>>> +    AVDictionary *opts = NULL;
>>> +    char url[MAX_URL_SIZE];
>>> +    int ret;
>>> +
>>> +    // broker prior HTTP options that should be consistent across
>> requests
>>> +    av_dict_set(&opts, "user-agent", c->user_agent, 0);
>>> +    av_dict_set(&opts, "cookies", c->cookies, 0);
>>> +    av_dict_set(&opts, "headers", c->headers, 0);
>>> +    if (c->is_live) {
>>> +        av_dict_set(&opts, "seekable", "0", 0);
>>> +    }
>>> +
>>> +    if (seg->size >= 0) {
>>> +        /* try to restrict the HTTP request to the part we want
>>> +         * (if this is in fact a HTTP request) */
>>> +        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
>>> +        av_dict_set_int(&opts, "end_offset", seg->url_offset +
>> seg->size, 0);
>>> +    }
>>> +
>>> +    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
>>> +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s',
>> offset %"PRId64", playlist %d\n",
>>> +           url, seg->url_offset, pls->rep_idx);
>>> +    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts,
>> NULL);
>>> +    if (ret < 0) {
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    /* Seek to the requested position. If this was a HTTP request, the
>> offset
>>> +     * should already be where want it to, but this allows e.g. local
>> testing
>>> +     * without a HTTP server. */
>>> +    if (!ret && seg->url_offset) {
>>> +        int64_t seekret = avio_seek(pls->input, seg->url_offset,
>> SEEK_SET);
>>> +        if (seekret < 0) {
>>> +            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset
>> %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
>>> +            ret = (int) seekret;
>>> +            ff_format_io_close(pls->parent, &pls->input);
>>> +        }
>>> +    }
>>> +
>>> +cleanup:
>>> +    av_dict_free(&opts);
>>> +    pls->cur_seg_offset = 0;
>>> +    pls->cur_seg_size = seg->size;
>>> +    return ret;
>>> +}
>>> +
>>> +static int update_init_section(struct representation *pls)
>>> +{
>>> +    static const int max_init_section_size = 1024*1024;
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +    int64_t sec_size = 0;
>>> +    int64_t urlsize = 0;
>>> +    int ret = 0;
>>> +
>>> +    /* read init section only once per representation */
>>> +    if (!pls->init_section || pls->init_sec_buf) {
>>> +        return 0;
>>> +    }
>>> +
>>> +    ret = open_input(c, pls, pls->init_section);
>>> +    if (ret < 0) {
>>> +        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an
>> initialization section in playlist %d\n", pls->rep_idx);
>>> +        return ret;
>>> +    }
>>> +
>>> +    if (pls->init_section->size >= 0) {
>>> +        sec_size = pls->init_section->size;
>>> +    } else if ((urlsize = avio_size(pls->input)) >= 0) {
>>> +        sec_size = urlsize;
>>> +    } else {
>>> +        sec_size = max_init_section_size;
>>> +    }
>>> +    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization
>> section of size %"PRId64"\n", sec_size);
>>> +    sec_size = FFMIN(sec_size, max_init_section_size);
>>> +    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size,
>> sec_size);
>>> +    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf,
>> pls->init_sec_buf_size, READ_COMPLETE);
>>> +    ff_format_io_close(pls->parent, &pls->input);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
>>> +        uint8_t **stsd_entries = NULL;
>>> +        int *stsd_entries_size = NULL;
>>> +        int i = 4;
>>> +
>>> +        while (i <= (ret - 4)) {
>>> +            // find start stsd atom
>>> +            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
>>> +                /* 1B version
>>> +                 * 3B flags
>>> +                 * 4B num of entries */
>>> +                int stsd_first_offset = i + 8;
>>> +                int stsd_offset = 0;
>>> +                int j = 0;
>>> +                uint32_t stsd_count = AV_RB32(pls->init_sec_buf +
>> stsd_first_offset);
>>> +                stsd_first_offset += 4;
>>> +                if (stsd_count != pls->rep_count) {
>>> +                    i++;
>>> +                    continue;
>>> +                }
>>> +                // find all stsd entries
>>> +                stsd_entries = av_mallocz_array(stsd_count,
>> sizeof(*stsd_entries));
>>> +                stsd_entries_size = av_mallocz_array(stsd_count,
>> sizeof(*stsd_entries_size));
>>> +                for (j = 0; j < stsd_count; ++j) {
>>> +                    /* 4B - size
>>> +                     * 4B - format */
>>> +                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf +
>> stsd_first_offset + stsd_offset);
>>> +                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
>>> +                    memcpy(stsd_entries[j], pls->init_sec_buf +
>> stsd_first_offset + stsd_offset, stsd_entries_size[j]);
>>> +                    stsd_offset += stsd_entries_size[j];
>>> +                }
>>> +                // reorder stsd entries
>>> +                // as first put stsd entry for current representation
>>> +                j = pls->rep_idx;
>>> +                stsd_offset = stsd_first_offset;
>>> +                memcpy(pls->init_sec_buf + stsd_offset,
>> stsd_entries[j], stsd_entries_size[j]);
>>> +                stsd_offset += stsd_entries_size[j];
>>> +                for (j = 0; j < stsd_count; ++j) {
>>> +                    if (j != pls->rep_idx) {
>>> +                        memcpy(pls->init_sec_buf + stsd_offset,
>> stsd_entries[j], stsd_entries_size[j]);
>>> +                        stsd_offset += stsd_entries_size[j];
>>> +                    }
>>> +                    av_free(stsd_entries[j]);
>>> +                }
>>> +                av_freep(&stsd_entries);
>>> +                av_freep(&stsd_entries_size);
>>> +                break;
>>> +            }
>>> +            i++;
>>> +        }
>>> +    }
>>> +
>>> +    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section
>> size[%d]\n", pls, (int)ret);
>>> +    pls->init_sec_data_len = ret;
>>> +    pls->init_sec_buf_read_offset = 0;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int64_t seek_data(void *opaque, int64_t offset, int whence)
>>> +{
>>> +    struct representation *v = opaque;
>>> +    if (v->n_fragments && !v->init_sec_data_len) {
>>> +        return avio_seek(v->input, offset, whence);
>>> +    }
>>> +
>>> +    return AVERROR(ENOSYS);
>>> +}
>>> +
>>> +static int read_data(void *opaque, uint8_t *buf, int buf_size)
>>> +{
>>> +    int ret = 0;
>>> +    struct representation *v = opaque;
>>> +    DASHContext *c = v->parent->priv_data;
>>> +
>>> +restart:
>>> +    if (!v->input) {
>>> +        free_fragment(&v->cur_seg);
>>> +        v->cur_seg = get_current_fragment(v);
>>> +        if (!v->cur_seg) {
>>> +            ret = AVERROR_EOF;
>>> +            goto end;
>>> +        }
>>> +
>>> +        /* load/update Media Initialization Section, if any */
>>> +        ret = update_init_section(v);
>>> +        if (ret)
>>> +            goto end;
>>> +
>>> +        ret = open_input(c, v, v->cur_seg);
>>> +        if (ret < 0) {
>>> +            if (ff_check_interrupt(c->interrupt_callback)) {
>>> +                goto end;
>>> +                ret = AVERROR_EXIT;
>>> +            }
>>> +            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment
>> of playlist %d\n", v->rep_idx);
>>> +            v->cur_seq_no++;
>>> +            goto restart;
>>> +        }
>>> +    }
>>> +
>>> +    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
>>> +        /* Push init section out first before first actual fragment */
>>> +        int copy_size = FFMIN(v->init_sec_data_len -
>> v->init_sec_buf_read_offset, buf_size);
>>> +        memcpy(buf, v->init_sec_buf, copy_size);
>>> +        v->init_sec_buf_read_offset += copy_size;
>>> +        ret = copy_size;
>>> +        goto end;
>>> +    }
>>> +
>>> +    /* check the v->cur_seg, if it is null, get current and double
>> check if the new v->cur_seg*/
>>> +    if (!v->cur_seg) {
>>> +        v->cur_seg = get_current_fragment(v);
>>> +    }
>>> +    if (!v->cur_seg) {
>>> +        ret = AVERROR_EOF;
>>> +        goto end;
>>> +    }
>>> +    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
>>> +    if (ret > 0)
>>> +        goto end;
>>> +    ff_format_io_close(v->parent, &v->input);
>>> +    v->cur_seq_no++;
>>> +    goto restart;
>>> +
>>> +end:
>>> +    return ret;
>>> +}
>>> +
>>> +static int save_avio_options(AVFormatContext *s)
>>> +{
>>> +    DASHContext *c = s->priv_data;
>>> +    const char *opts[] = { "headers", "user_agent", "user-agent",
>> "cookies", NULL }, **opt = opts;
>>> +    uint8_t *buf = NULL;
>>> +    int ret = 0;
>>> +
>>> +    while (*opt) {
>>> +        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0)
>> {
>>> +            if (buf) {
>>> +                ret = av_dict_set(&c->avio_opts, *opt, buf,
>> AV_DICT_DONT_STRDUP_VAL);
>>> +                if (ret < 0)
>>> +                    return ret;
>>> +            }
>>> +        }
>>> +        opt++;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const
>> char *url,
>>> +                          int flags, AVDictionary **opts)
>>> +{
>>> +    av_log(s, AV_LOG_ERROR,
>>> +           "A HDS playlist item '%s' referred to an external file '%s'.
>> "
>>> +           "Opening this file was forbidden for security reasons\n",
>>> +           s->filename, url);
>>> +    return AVERROR(EPERM);
>>> +}
>>> +
>>> +static int reopen_demux_for_component(AVFormatContext *s, struct
>> representation *pls)
>>> +{
>>> +    DASHContext *c = s->priv_data;
>>> +    AVInputFormat *in_fmt = NULL;
>>> +    AVDictionary  *in_fmt_opts = NULL;
>>> +    uint8_t *avio_ctx_buffer  = NULL;
>>> +    int ret = 0;
>>> +
>>> +    if (pls->ctx) {
>>> +        /* note: the internal buffer could have changed, and be !=
>> avio_ctx_buffer */
>>> +        av_freep(&pls->pb.buffer);
>>> +        memset(&pls->pb, 0x00, sizeof(AVIOContext));
>>> +        pls->ctx->pb = NULL;
>>> +        avformat_close_input(&pls->ctx);
>>> +        pls->ctx = NULL;
>>> +    }
>>> +    if (!(pls->ctx = avformat_alloc_context())) {
>>> +        ret = AVERROR(ENOMEM);
>>> +        goto fail;
>>> +    }
>>> +
>>> +    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
>>> +    if (!avio_ctx_buffer ){
>>> +        ret = AVERROR(ENOMEM);
>>> +        avformat_free_context(pls->ctx);
>>> +        pls->ctx = NULL;
>>> +        goto fail;
>>> +    }
>>> +    if (c->is_live) {
>>> +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
>> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
>>> +    } else {
>>> +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
>> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
>>> +    }
>>> +    pls->pb.seekable = 0;
>>> +
>>> +    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
>>> +        goto fail;
>>> +
>>> +    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
>>> +    pls->ctx->probesize = 1024 * 4;
>>> +    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
>>> +    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
>>> +    if (ret < 0) {
>>> +        av_log(s, AV_LOG_ERROR, "Error when loading first fragment,
>> playlist %d\n", (int)pls->rep_idx);
>>> +        avformat_free_context(pls->ctx);
>>> +        pls->ctx = NULL;
>>> +        goto fail;
>>> +    }
>>> +
>>> +    pls->ctx->pb = &pls->pb;
>>> +    pls->ctx->io_open  = nested_io_open;
>>> +
>>> +    // provide additional information from mpd if available
>>> +    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts);
>> //pls->init_section->url
>>> +    av_dict_free(&in_fmt_opts);
>>> +    if (ret < 0)
>>> +        goto fail;
>>> +    if (pls->n_fragments) {
>>> +        ret = avformat_find_stream_info(pls->ctx, NULL);
>>> +        if (ret < 0)
>>> +            goto fail;
>>> +    }
>>> +
>>> +fail:
>>> +    return ret;
>>> +}
>>> +
>>> +static int open_demux_for_component(AVFormatContext *s, struct
>> representation *pls)
>>> +{
>>> +    int ret = 0;
>>> +    int i;
>>> +
>>> +    pls->parent = s;
>>> +    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
>>> +    pls->last_seq_no = calc_max_seg_no(s, pls);
>>> +
>>> +    ret = reopen_demux_for_component(s, pls);
>>> +    if (ret < 0) {
>>> +        goto fail;
>>> +    }
>>> +    for (i = 0; i < pls->ctx->nb_streams; i++) {
>>> +        AVStream *st = avformat_new_stream(s, NULL);
>>> +        AVStream *ist = pls->ctx->streams[i];
>>> +        if (!st) {
>>> +            ret = AVERROR(ENOMEM);
>>> +            goto fail;
>>> +        }
>>> +        st->id = i;
>>> +        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->
>> codecpar);
>>> +        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num,
>> ist->time_base.den);
>>> +    }
>>> +
>>> +    return 0;
>>> +fail:
>>> +    return ret;
>>> +}
>>> +
>>> +static int dash_read_header(AVFormatContext *s)
>>> +{
>>> +    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
>>> +    DASHContext *c = s->priv_data;
>>> +    int ret = 0;
>>> +    int stream_index = 0;
>>> +
>>> +    c->interrupt_callback = &s->interrupt_callback;
>>> +    // if the URL context is good, read important options we must
>> broker later
>>> +    if (u) {
>>> +        update_options(&c->user_agent, "user-agent", u);
>>> +        update_options(&c->cookies, "cookies", u);
>>> +        update_options(&c->headers, "headers", u);
>>> +    }
>>> +
>>> +    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
>>> +        goto fail;
>>> +
>>> +    if ((ret = save_avio_options(s)) < 0)
>>> +        goto fail;
>>> +
>>> +    /* If this isn't a live stream, fill the total duration of the
>>> +     * stream. */
>>> +    if (!c->is_live) {
>>> +        s->duration = (int64_t) c->media_presentation_duration_sec *
>> AV_TIME_BASE;
>>> +    }
>>> +
>>> +    /* Open the demuxer for curent video and current audio components
>> if available */
>>> +    if (!ret && c->cur_video) {
>>> +        ret = open_demux_for_component(s, c->cur_video);
>>> +        if (!ret) {
>>> +            c->cur_video->stream_index = stream_index;
>>> +            ++stream_index;
>>> +        } else {
>>> +            free_representation(c->cur_video);
>>> +            c->cur_video = NULL;
>>> +        }
>>> +    }
>>> +
>>> +    if (!ret && c->cur_audio) {
>>> +        ret = open_demux_for_component(s, c->cur_audio);
>>> +        if (!ret) {
>>> +            c->cur_audio->stream_index = stream_index;
>>> +            ++stream_index;
>>> +        } else {
>>> +            free_representation(c->cur_audio);
>>> +            c->cur_audio = NULL;
>>> +        }
>>> +    }
>>> +
>>> +    if (!stream_index) {
>>> +        ret = AVERROR_INVALIDDATA;
>>> +        goto fail;
>>> +    }
>>> +
>>> +    /* Create a program */
>>> +    if (!ret) {
>>> +        AVProgram *program;
>>> +        program = av_new_program(s, 0);
>>> +        if (!program) {
>>> +            goto fail;
>>> +        }
>>> +
>>> +        if (c->cur_video) {
>>> +            av_program_add_stream_index(s, 0,
>> c->cur_video->stream_index);
>>> +        }
>>> +
>>> +        if (c->cur_audio) {
>>> +            av_program_add_stream_index(s, 0,
>> c->cur_audio->stream_index);
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +fail:
>>> +    return ret;
>>> +}
>>> +
>>> +static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
>>> +{
>>> +    DASHContext *c = s->priv_data;
>>> +    int ret = 0;
>>> +    struct representation *cur = NULL;
>>> +
>>> +    if (!c->cur_audio && !c->cur_video) {
>>> +        return AVERROR_INVALIDDATA;
>>> +    }
>>> +    if (c->cur_audio && !c->cur_video) {
>>> +        cur = c->cur_audio;
>>> +    } else if (!c->cur_audio && c->cur_video) {
>>> +        cur = c->cur_video;
>>> +    } else if (c->cur_video->cur_timestamp <
>> c->cur_audio->cur_timestamp) {
>>> +        cur = c->cur_video;
>>> +    } else {
>>> +        cur = c->cur_audio;
>>> +    }
>>> +
>>> +    if (cur->ctx) {
>>> +        ret = av_read_frame(cur->ctx, &cur->pkt);
>>> +        if (ret < 0) {
>>> +            av_packet_unref(&cur->pkt);
>>> +        } else {
>>> +            /* If we got a packet, return it */
>>> +            *pkt = cur->pkt;
>>> +            cur->cur_timestamp = av_rescale(pkt->pts,
>> (int64_t)cur->ctx->streams[0]->time_base.num * 90000,
>> cur->ctx->streams[0]->time_base.den);
>>> +            pkt->stream_index = cur->stream_index;
>>> +            reset_packet(&cur->pkt);
>>> +            return 0;
>>> +        }
>>> +    }
>>> +    return AVERROR_EOF;
>>> +}
>>> +
>>> +static int dash_close(AVFormatContext *s)
>>> +{
>>> +    DASHContext *c = s->priv_data;
>>> +
>>> +    if (c->cur_audio) {
>>> +        free_representation(c->cur_audio);
>>> +    }
>>> +
>>> +    if (c->cur_video) {
>>> +        free_representation(c->cur_video);
>>> +    }
>>> +
>>> +    av_freep(&c->cookies);
>>> +    av_freep(&c->user_agent);
>>> +    av_dict_free(&c->avio_opts);
>>> +    av_freep(&c->base_url);
>>> +    return 0;
>>> +}
>>> +
>>> +static int dash_seek(AVFormatContext *s, struct representation *pls,
>> int64_t seek_pos_msec, int flags)
>>> +{
>>> +    int ret = 0;
>>> +    int i = 0;
>>> +    int j = 0;
>>> +    int64_t duration = 0;
>>> +
>>> +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms],
>> playlist %d\n", seek_pos_msec, pls->rep_idx);
>>> +
>>> +    // single fragment mode
>>> +    if (pls->n_fragments) {
>>> +        pls->cur_timestamp = 0;
>>> +        pls->cur_seg_offset = 0;
>>> +        ff_read_frame_flush(pls->ctx);
>>> +        return av_seek_frame(pls->ctx, -1, seek_pos_msec*1000, flags);
>>> +    }
>>> +
>>> +    if (pls->input)
>>> +        ff_format_io_close(pls->parent, &pls->input);
>>> +
>>> +    // find the nearest fragment
>>> +    if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
>>> +        int64_t num = pls->first_seq_no;
>>> +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
>> SegmentTimeline start n_timelines[%d] last_seq_no[%"PRId64"], playlist
>> %d.\n",
>>> +               (int)pls->n_timelines, (int64_t)pls->last_seq_no,
>> (int)pls->rep_idx);
>>> +        for (i = 0; i < pls->n_timelines; i++) {
>>> +            if (pls->timelines[i]->t > 0) {
>>> +                duration = pls->timelines[i]->t;
>>> +            }
>>> +            duration += pls->timelines[i]->d;
>>> +            if (seek_pos_msec < ((duration * 1000) /
>> pls->fragment_timescale)) {
>>> +                goto set_seq_num;
>>> +            }
>>> +            for (j = 0; j < pls->timelines[i]->r; j++) {
>>> +                duration += pls->timelines[i]->d;
>>> +                num++;
>>> +                if (seek_pos_msec < ((duration * 1000) /
>> pls->fragment_timescale)) {
>>> +                    goto set_seq_num;
>>> +                }
>>> +            }
>>> +            num++;
>>> +        }
>>> +
>>> +set_seq_num:
>>> +        pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no :
>> num;
>>> +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
>> SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
>>> +               (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
>>> +    } else if (pls->fragment_duration > 0) {
>>> +        pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec *
>> pls->fragment_timescale) / pls->fragment_duration) / 1000;
>>> +    } else {
>>> +        av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing
>> fragment_duration\n");
>>> +        pls->cur_seq_no = pls->first_seq_no;
>>> +    }
>>> +    pls->cur_timestamp = 0;
>>> +    pls->cur_seg_offset = 0;
>>> +    pls->init_sec_buf_read_offset = 0;
>>> +    ret = reopen_demux_for_component(s, pls);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t
>> timestamp, int flags)
>>> +{
>>> +    int ret = 0;
>>> +    DASHContext *c = s->priv_data;
>>> +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
>>> +                                         s->streams[stream_index]->
>> time_base.den,
>>> +                                         flags & AVSEEK_FLAG_BACKWARD ?
>>> +                                         AV_ROUND_DOWN : AV_ROUND_UP);
>>> +    if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
>>> +        return AVERROR(ENOSYS);
>>> +    if (c->cur_audio) {
>>> +        ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
>>> +    }
>>> +    if (!ret && c->cur_video) {
>>> +        ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
>>> +    }
>>> +    return ret;
>>> +}
>>> +
>>> +static int dash_probe(AVProbeData *p)
>>> +{
>>> +    if (!av_stristr(p->buf, "<MPD"))
>>> +        return 0;
>>> +
>>> +    if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
>>> +        av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
>>> +        av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
>>> +        av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
>>> +        return AVPROBE_SCORE_MAX;
>>> +    }
>>> +    if (av_stristr(p->buf, "dash:profile")) {
>>> +        return AVPROBE_SCORE_MAX >> 1;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +#define OFFSET(x) offsetof(DASHContext, x)
>>> +#define FLAGS AV_OPT_FLAG_DECODING_PARAM
>>> +static const AVOption dash_options[] = {
>>> +    {NULL}
>>> +};
>>> +
>>> +static const AVClass dash_class = {
>>> +    .class_name = "dash",
>>> +    .item_name  = av_default_item_name,
>>> +    .option     = dash_options,
>>> +    .version    = LIBAVUTIL_VERSION_INT,
>>> +};
>>> +
>>> +AVInputFormat ff_dash_demuxer = {
>>> +    .name           = "dash",
>>> +    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming
>> over HTTP"),
>>> +    .priv_class     = &dash_class,
>>> +    .priv_data_size = sizeof(DASHContext),
>>> +    .read_probe     = dash_probe,
>>> +    .read_header    = dash_read_header,
>>> +    .read_packet    = dash_read_packet,
>>> +    .read_close     = dash_close,
>>> +    .read_seek      = dash_read_seek,
>>> +    .flags          = AVFMT_NO_BYTE_SEEK,
>>> +};
>>> --
>>> 2.10.1 (Apple Git-78)
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
>> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel <http://ffmpeg.org/mailman/listinfo/ffmpeg-devel>
Steven Liu March 28, 2017, 8:42 a.m. UTC | #4
2017-03-27 8:39 GMT+08:00 Rodger Combs <rodger.combs@gmail.com>:

>
> > On Mar 26, 2017, at 19:10, Steven Liu <lq@chinaffmpeg.org> wrote:
> >
> > v2 fixed:
> > 1. from autodetect to disabled
> > 2. from camelCase code style to ffmpeg code style
> > 3. from RepType to AVMediaType
> > 4. fix variable typo
> > 5. change time value from uint32_t to uint64_t
> > 6. removed be used once API
> > 7. change 'time(NULL)`, except it is not 2038-safe.' to av_gettime and
> > av_timegm
> > 8. merge complex free operation to free_fragment
> > 9. use API from snprintf to av_asprintf
> >
> > v3 fixed:
> > 1. fix typo from --enabled-xml2 to --enable-xml2
> >
> > v4 fixed:
> > 1. from --enable-xml2 to --enable-libxml2
> > 2. move system includes to top
> > 3. remove nouse includes
> > 4. rename enum name
> > 5. add a trailing comma for the last entry enum
> > 6. fix comment typo
> > 7. add const to DASHContext class front
> > 8. check sscanf if return arguments and give warning message when error
> > 9. check validity before free seg->url and seg
> > 10. check if the val is null, before use atoll
> >
> > v5 fixed:
> > 1. fix typo from mainifest to manifest
> >
> > v6 fixed:
> > 1. from realloc to av_realloc
> > 2. from free to av_free
> >
> > v7 fixed:
> > 1. remove the -lxml2 from configure when require_pkg_config
> >
> > v8 fixed:
> > 1. fix replace filename template by av_asprintf secure problem
> >
> > v9 modified:
> > 1. make manifest parser clearly
> >
> > Reviewed-by: Clément Bœsch <u@pkh.me>
> > Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
> > Reviewed-by: Carl Eugen Hoyos <cehoyos@ag.or.at>
> > Reviewed-by: Rodger Combs <rodger.combs@gmail.com>
> > Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
> > Reviewed-by: Nicolas George <george@nsup.org>
> > Reviewed-by: Ricardo Constantino <wiiaboo@gmail.com>
> > Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> > ---
> > configure                |    4 +
> > libavformat/Makefile     |    1 +
> > libavformat/allformats.c |    2 +-
> > libavformat/dashdec.c    | 1842 ++++++++++++++++++++++++++++++
> ++++++++++++++++
> > 4 files changed, 1848 insertions(+), 1 deletion(-)
> > create mode 100644 libavformat/dashdec.c
> >
> > diff --git a/configure b/configure
> > index 9cc7e7d..4a6fe8a 100755
> > --- a/configure
> > +++ b/configure
> > @@ -273,6 +273,7 @@ External library support:
> >   --enable-libxcb-shape    enable X11 grabbing shape rendering
> [autodetect]
> >   --enable-libxvid         enable Xvid encoding via xvidcore,
> >                            native MPEG-4/Xvid encoder exists [no]
> > +  --enable-libxml2            enable XML parsing using the C library
> libxml2 [no]
> >   --enable-libzimg         enable z.lib, needed for zscale filter [no]
> >   --enable-libzmq          enable message passing via libzmq [no]
> >   --enable-libzvbi         enable teletext support via libzvbi [no]
> > @@ -1574,6 +1575,7 @@ EXTERNAL_LIBRARY_LIST="
> >     libvpx
> >     libwavpack
> >     libwebp
> > +    libxml2
> >     libzimg
> >     libzmq
> >     libzvbi
> > @@ -2906,6 +2908,7 @@ avi_muxer_select="riffenc"
> > caf_demuxer_select="iso_media riffdec"
> > caf_muxer_select="iso_media"
> > dash_muxer_select="mp4_muxer"
> > +dash_demuxer_deps="libxml2"
> > dirac_demuxer_select="dirac_parser"
> > dts_demuxer_select="dca_parser"
> > dtshd_demuxer_select="dca_parser"
> > @@ -5908,6 +5911,7 @@ enabled openssl           && { use_pkg_config
> openssl openssl/ssl.h OPENSSL_init
> >                                check_lib openssl/ssl.h SSL_library_init
> -lssl -lcrypto -lws2_32 -lgdi32 ||
> >                                die "ERROR: openssl not found"; }
> > enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h ||
> disable qtkit_indev; }
> > +enabled libxml2             && require_pkg_config libxml-2.0
> libxml2/libxml/xmlversion.h xmlCheckVersion
> >
> > # libdc1394 check
> > if enabled libdc1394; then
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index f56ef16..d77ffaa 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -134,6 +134,7 @@ OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
> > OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
> > OBJS-$(CONFIG_DATA_MUXER)                += rawenc.o
> > OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o
> > +OBJS-$(CONFIG_DASH_DEMUXER)              += dashdec.o
> > OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
> > OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
> > OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
> > diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> > index 09e62c3..d57314b 100644
> > --- a/libavformat/allformats.c
> > +++ b/libavformat/allformats.c
> > @@ -96,7 +96,7 @@ static void register_all(void)
> >     REGISTER_DEMUXER (CINE,             cine);
> >     REGISTER_DEMUXER (CONCAT,           concat);
> >     REGISTER_MUXER   (CRC,              crc);
> > -    REGISTER_MUXER   (DASH,             dash);
> > +    REGISTER_MUXDEMUX(DASH,             dash);
> >     REGISTER_MUXDEMUX(DATA,             data);
> >     REGISTER_MUXDEMUX(DAUD,             daud);
> >     REGISTER_DEMUXER (DCSTR,            dcstr);
> > diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
> > new file mode 100644
> > index 0000000..a4d3ba2
> > --- /dev/null
> > +++ b/libavformat/dashdec.c
> > @@ -0,0 +1,1842 @@
> > +/*
> > + * 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"
> > +
> > +#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 {
> > +    /* t: 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@t + @d * (@r + 1)).
> > +     * */
> > +    int64_t t;
> > +    /* r: Element or Attribute Name
> > +     * specifies the repeat count of the number of following contiguous
> Segments with
> > +     * the same duration expressed by the value of @d. This value is
> zero-based
> > +     * (e.g. a value of three means four Segments in the contiguous
> series).
> > +     * */
> > +    int32_t r;
> > +    /* d: Element or Attribute Name
> > +     * specifies the Segment duration, in units of the value of the
> @timescale.
> > +     * */
> > +    int64_t d;
> > +};
> > +
> > +enum DASHTmplUrlType {
> > +    TMP_URL_TYPE_UNSPECIFIED,
> > +    TMP_URL_TYPE_NUMBER,
> > +    TMP_URL_TYPE_TIME,
> > +};
> > +
> > +/*
> > + * 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;
> > +    enum DASHTmplUrlType tmp_url_type;
> > +    AVIOContext pb;
> > +    AVIOContext *input;
> > +    AVFormatContext *parent;
> > +    AVFormatContext *ctx;
> > +    AVPacket pkt;
> > +    int rep_idx;
> > +    int rep_count;
> > +    int stream_index;
> > +
> > +    enum AVMediaType type;
> > +    int64_t target_duration;
> > +
> > +    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 fragment_duration;
> > +    int64_t fragment_timescale;
> > +
> > +    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;
> > +    int fix_multiple_stsd_order;
> > +    int64_t cur_timestamp;
> > +};
> > +
> > +typedef struct DASHContext {
> > +    const AVClass *class;
> > +    char *base_url;
> > +    struct representation *cur_video;
> > +    struct representation *cur_audio;
> > +    uint64_t media_presentation_duration_sec;
> > +    uint64_t suggested_presentation_delay_sec;
> > +    uint64_t presentation_delay_sec;
> > +    uint64_t availability_start_time_sec;
> > +    uint64_t publish_time_sec;
> > +    uint64_t minimum_update_period_sec;
> > +    uint64_t time_shift_buffer_depth_sec;
> > +    uint64_t min_buffer_time_sec;
> > +    uint64_t period_duration_sec;
> > +    uint64_t period_start_sec;
> > +    int is_live;
> > +    AVIOInterruptCB *interrupt_callback;
> > +    char *user_agent;                    ///< holds HTTP user agent set
> as an AVOption to the HTTP protocol context
> > +    char *cookies;                       ///< holds HTTP cookie values
> set in either the initial response or as an AVOption to the HTTP protocol
> context
> > +    char *headers;                       ///< holds HTTP headers set as
> an AVOption to the HTTP protocol context
> > +    AVDictionary *avio_opts;
> > +} DASHContext;
> > +
> > +static uint64_t GetCurrentTimeInSec(void)
>
> Code style: "gettime_sec(void)" or the like; not CamelCased.
>
> > +{
> > +    return  av_gettime() / 1000000;
> > +}
> > +
> > +static uint64_t GetUTCDateTimeInSec(AVFormatContext *s, const char
> *datetime)
>
> I still don't understand why av_parse_time isn't applicable here; it
> parses ISO-8601 dates just fine.
>
> > +{
> > +    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, "GetUTCDateTimeInSec get a wrong time
> format\n");
> > +       // return 0; /* parser error */
> > +    }
> > +    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 GetDurationInSec(AVFormatContext *s, const char
> *duration)
>
> Same question here; av_parse_time parses ISO-8601 durations if you set
> duration=1.
>
> > +{
> > +    /* ISO-8601 duration parser */
> > +    uint32_t days = 0;
> > +    uint32_t hours = 0;
> > +    uint32_t mins = 0;
> > +    uint32_t secs = 0;
> > +    uint32_t size = 0;
> > +    float value = 0;
> > +    uint8_t 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, "GetDurationInSec 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 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;
> > +}
> > +
> > +/*
> > + * Used to reset a statically allocated AVPacket to a clean slate,
> > + * containing no data.
> > + */
> > +static void reset_packet(AVPacket *pkt)
> > +{
> > +    av_init_packet(pkt);
> > +    pkt->data = NULL;
> > +}
> > +
> > +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_packet_unref(&pls->pkt);
> > +    reset_packet(&pls->pkt);
> > +    av_freep(&pls->pb.buffer);
> > +    if (pls->input)
> > +        ff_format_io_close(pls->parent, &pls->input);
> > +    if (pls->ctx) {
> > +        pls->ctx->pb = NULL;
> > +        avformat_close_input(&pls->ctx);
> > +    }
> > +
> > +    av_free(pls->url_template);
> > +    av_free(pls);
> > +}
> > +
> > +static void update_options(char **dest, const char *name, void *src)
> > +{
> > +    av_freep(dest);
> > +    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
> > +    if (*dest)
> > +        av_freep(dest);
> > +}
> > +
> > +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, "http", NULL) &&
> !av_strstart(proto_name, "file", NULL))
> > +        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;
> > +
> > +    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
> > +    if (ret >= 0) {
> > +        // update cookies on http response with setcookies.
> > +        void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> > +        update_options(&c->cookies, "cookies", u);
> > +        av_dict_set(&opts, "cookies", c->cookies, 0);
> > +    }
> > +
> > +    av_dict_free(&tmp);
> > +
> > +    if (is_http)
> > +        *is_http = av_strstart(proto_name, "http", NULL);
> > +
> > +    return ret;
> > +
> > +}
> > +
> > +static char *strreplace(const char *str, const char *from, const char
> *to)
> > +{
> > +    /* Adjust each of the below values to suit your needs. */
> > +    /* Increment positions cache size initially by this number. */
> > +    size_t cache_sz_inc = 16;
> > +    /* Thereafter, each time capacity needs to be increased,
> > +     * multiply the increment by this factor. */
> > +    const size_t cache_sz_inc_factor = 3;
> > +    /* But never increment capacity by more than this number. */
> > +    const size_t cache_sz_inc_max = 1048576;
> > +
> > +    char *pret, *ret = NULL;
> > +    const char *pstr2, *pstr = str;
> > +    size_t i, count = 0;
> > +    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
> > +    size_t cache_sz = 0;
> > +    size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
> > +
> > +    /* Find all matches and cache their positions. */
> > +    while ((pstr2 = av_stristr(pstr, from))) {
> > +        count++;
> > +        /* Increase the cache size when necessary. */
> > +        if (cache_sz < count) {
> > +            cache_sz += cache_sz_inc;
> > +            pos_cache_tmp = av_realloc(pos_cache, sizeof(*pos_cache) *
> cache_sz);
> > +            if (!pos_cache_tmp) {
> > +                goto end_strreplace;
> > +            } else pos_cache = pos_cache_tmp;
> > +            cache_sz_inc *= cache_sz_inc_factor;
> > +            if (cache_sz_inc > cache_sz_inc_max) {
> > +                cache_sz_inc = cache_sz_inc_max;
> > +            }
> > +        }
> > +
> > +        pos_cache[count-1] = pstr2 - str;
> > +        pstr = pstr2 + fromlen;
> > +    }
> > +    orglen = pstr - str + strlen(pstr);
> > +    /* Allocate memory for the post-replacement string. */
> > +    if (count > 0) {
> > +        tolen = strlen(to);
> > +        retlen = orglen + (tolen - fromlen) * count;
> > +    } else {
> > +        retlen = orglen;
> > +    }
> > +    ret = av_malloc(retlen + 1);
> > +    if (!ret) {
> > +        goto end_strreplace;
> > +    }
> > +
> > +    if (!count) {
> > +        /* If no matches, then just duplicate the string. */
> > +        av_strlcpy(ret, str, retlen + 1);
> > +    } else {
> > +        /* Otherwise, duplicate the string whilst performing
> > +         * the replacements using the position cache. */
> > +        pret = ret;
> > +        memcpy(pret, str, pos_cache[0]);
> > +        pret += pos_cache[0];
> > +        for (i = 0; i < count; i++) {
> > +            memcpy(pret, to, tolen);
> > +            pret += tolen;
> > +            pstr = str + pos_cache[i] + fromlen;
> > +            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) -
> pos_cache[i] - fromlen;
> > +            memcpy(pret, pstr, cpylen);
> > +            pret += cpylen;
> > +        }
> > +        ret[retlen] = '\0';
> > +    }
> > +
> > +end_strreplace:
> > +    /* Free the cache and return the post-replacement string,
> > +     * which will be NULL in the event of an error. */
> > +    av_free(pos_cache);
> > +    return ret;
> > +}
> > +
> > +static char *replace_template_str(const char *url, const char *marker)
> > +{
> > +    char *prefix = NULL;
> > +    char *start = 0;
> > +    char *end = NULL;
> > +    char *tmp_url = NULL;
> > +    char *new_url = NULL;
> > +    int marker_len;
> > +
> > +    prefix = av_strdup(marker);
> > +    marker_len = strlen(prefix) - 1;
> > +    prefix[marker_len] = '\0';
> > +
> > +    start = av_stristr(url, prefix);
> > +    if (!start)
> > +        goto finish;
> > +    end = strchr(start + 1, '$');
> > +    if (!end)
> > +        goto finish;
> > +
> > +    tmp_url = av_mallocz(MAX_URL_SIZE);
> > +    if (!tmp_url) {
> > +        return NULL;
> > +    }
> > +
> > +    av_strlcpy(tmp_url, url, start - url+ 1);
> > +    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end -
> start - marker_len + 1);
> > +    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
> > +
> > +finish:
> > +    av_free(prefix);
> > +    new_url = strreplace(tmp_url ? tmp_url : url, marker, "%"PRId64);
>
> Since you're no longer using a printf function, this replacement doesn't
> make sense. You should replace with the actual value you want to substitute
> instead.
>
> > +    av_free(tmp_url);
> > +    return new_url;
> > +}
> > +
> > +static char *get_content_url(xmlNodePtr *baseurl_nodes, int
> n_baseurl_nodes, xmlChar *rep_id_val, xmlChar *rep_bandwidth_val, xmlChar
> *val)
> > +{
> > +    int i;
> > +    xmlChar *text;
> > +    char *url = NULL;
> > +    char *tmp_str = av_mallocz(MAX_URL_SIZE);
> > +    char *tmp_str_2 = NULL;
> > +
> > +    if (!tmp_str) {
> > +        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) {
> > +                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
> > +                if (!tmp_str_2) {
> > +                    av_free(tmp_str);
> > +                    return NULL;
> > +                }
> > +                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str,
> text);
> > +                av_free(tmp_str);
> > +                tmp_str = tmp_str_2;
> > +                xmlFree(text);
> > +            }
> > +        }
> > +    }
> > +    if (val)
> > +        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
> > +    if (rep_id_val) {
> > +        url = strreplace(tmp_str, "$RepresentationID$", (const
> char*)rep_id_val);
> > +        av_free(tmp_str);
> > +        tmp_str = url;
> > +    }
> > +    if (rep_bandwidth_val && tmp_str)
> > +        url = strreplace(tmp_str, "$Bandwidth$", (const
> char*)rep_bandwidth_val);
> > +    if (tmp_str != url)
> > +        av_free(tmp_str);
> > +    return url;
> > +}
> > +
> > +static xmlChar *get_val_from_nodes_tab(xmlNodePtr *nodes, const int
> n_nodes, const xmlChar *attrname)
> > +{
> > +    int i;
> > +    xmlChar *val;
> > +
> > +    for (i = 0; i < n_nodes; ++i) {
> > +        if (nodes[i]) {
> > +            val = xmlGetProp(nodes[i], attrname);
> > +            if (val)
> > +                return val;
> > +        }
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +static xmlNodePtr findChildNodeByName(xmlNodePtr rootnode, const
> xmlChar *nodename)
> > +{
> > +    xmlNodePtr node = rootnode;
> > +    if (!node) {
> > +        return NULL;
> > +    }
> > +
> > +    node = xmlFirstElementChild(node);
> > +    while (node) {
> > +        if (!xmlStrcmp(node->name, nodename)) {
> > +            return node;
> > +        }
> > +        node = xmlNextElementSibling(node);
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static enum AVMediaType get_content_type(xmlNodePtr node)
> > +{
> > +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> > +    int i = 0;
> > +    const char *attr;
> > +
> > +    if (node) {
> > +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> > +            attr = (i) ? "mimeType" : "contentType";
> > +            xmlChar *val = xmlGetProp(node, attr);
> > +            if (val) {
> > +                if (av_stristr((const char *)val, "video")) {
> > +                    type = AVMEDIA_TYPE_VIDEO;
> > +                } else if (av_stristr((const char *)val, "audio")) {
> > +                    type = AVMEDIA_TYPE_AUDIO;
> > +                }
> > +                xmlFree(val);
> > +            }
> > +            i++;
> > +        }
> > +    }
> > +    return type;
> > +}
> > +
> > +static int parse_manifest_segmenturlnode(AVFormatContext *s, struct
> representation *rep,
> > +                                          xmlNodePtr fragmenturl_node,
> > +                                          xmlNodePtr *baseurl_nodes,
> > +                                          xmlChar *rep_id_val,
> > +                                          xmlChar *rep_bandwidth_val)
> > +{
> > +    xmlChar *initialization_val = NULL;
> > +    xmlChar *media_val = NULL;
> > +
> > +    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
> *)"Initialization")) {
> > +        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
> > +        if (initialization_val) {
> > +            rep->init_section = av_mallocz(sizeof(struct fragment));
> > +            if (!rep->init_section) {
> > +                xmlFree(initialization_val);
> > +                return AVERROR(ENOMEM);
> > +            }
> > +            rep->init_section->url = get_content_url(baseurl_nodes, 4,
> rep_id_val, rep_bandwidth_val, initialization_val);
> > +            rep->init_section->size = -1;
> > +            xmlFree(initialization_val);
> > +        }
> > +    } else if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar
> *)"SegmentURL")) {
> > +        media_val = xmlGetProp(fragmenturl_node, "media");
> > +        if (media_val) {
> > +            struct fragment *seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                xmlFree(media_val);
> > +                return AVERROR(ENOMEM);
> > +            }
> > +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
> rep_bandwidth_val, media_val);
> > +            seg->size = -1;
> > +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> > +            xmlFree(media_val);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int parse_manifest_segmenttimeline(AVFormatContext *s, struct
> representation *rep,
> > +                                           xmlNodePtr
> fragment_timeline_node)
> > +{
> > +    xmlAttrPtr attr = NULL;
> > +    xmlChar *val  = NULL;
> > +
> > +    if (!xmlStrcmp(fragment_timeline_node->name, (const xmlChar
> *)"S")) {
> > +        struct timeline *tml = av_mallocz(sizeof(struct timeline));
> > +        if (!tml) {
> > +            return AVERROR(ENOMEM);
> > +        }
> > +        attr = fragment_timeline_node->properties;
> > +        while (attr) {
> > +            val = xmlGetProp(fragment_timeline_node, attr->name);
> > +
> > +            if (!val) {
> > +                av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline
> attr->name = %s val is NULL\n", attr->name);
> > +                continue;
> > +            }
> > +
> > +            if (!xmlStrcmp(attr->name, (const xmlChar *)"t")) {
> > +                tml->t = (int64_t)atoll((const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> > +                tml->r =(int32_t) atoi((const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> > +                tml->d = (int64_t)atoll((const char *)val);
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)val);
> > +            }
> > +            attr = attr->next;
> > +            xmlFree(val);
> > +        }
> > +        dynarray_add(&rep->timelines, &rep->n_timelines, tml);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int parse_manifest_representation(AVFormatContext *s, const
> char *url,
> > +                                          xmlNodePtr node,
> > +                                          xmlNodePtr adaptionset_node,
> > +                                          xmlNodePtr mpd_baseurl_node,
> > +                                          xmlNodePtr
> period_baseurl_node,
> > +                                          xmlNodePtr
> fragment_template_node,
> > +                                          xmlNodePtr
> content_component_node,
> > +                                          xmlNodePtr
> adaptionset_baseurl_node)
> > +{
> > +    int32_t ret = 0;
> > +    int32_t audio_rep_idx = 0;
> > +    int32_t video_rep_idx = 0;
> > +    char *temp_string = NULL;
> > +    DASHContext *c = s->priv_data;
> > +    struct representation *rep = NULL;
> > +    struct fragment *seg = NULL;
> > +    xmlNodePtr representation_segmenttemplate_node = NULL;
> > +    xmlNodePtr representation_baseurl_node = NULL;
> > +    xmlNodePtr representation_segmentlist_node = NULL;
> > +    xmlNodePtr fragment_timeline_node = NULL;
> > +    xmlNodePtr fragment_templates_tab[2];
> > +    xmlChar *duration_val = NULL;
> > +    xmlChar *startnumber_val = NULL;
> > +    xmlChar *timescale_val = NULL;
> > +    xmlChar *initialization_val = NULL;
> > +    xmlChar *media_val = NULL;
> > +    xmlNodePtr baseurl_nodes[4];
> > +    xmlNodePtr representation_node = node;
> > +    xmlChar *rep_id_val = xmlGetProp(representation_node, "id");
> > +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node,
> "bandwidth");
> > +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> > +
> > +    // try get information from representation
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(representation_node);
> > +    // try get information from contentComponen
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(content_component_node);
> > +    // try get information from adaption set
> > +    if (type == AVMEDIA_TYPE_UNKNOWN)
> > +        type = get_content_type(adaptionset_node);
> > +    if (type == AVMEDIA_TYPE_UNKNOWN) {
> > +        av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported
> representation type\n", url);
> > +    } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type
> == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
> > +        // convert selected representation to our internal struct
> > +        rep = av_mallocz(sizeof(struct representation));
> > +        if (!rep) {
> > +            ret = AVERROR(ENOMEM);
> > +            goto end;
> > +        }
> > +        representation_segmenttemplate_node = findChildNodeByName(representation_node,
> "SegmentTemplate");
> > +        representation_baseurl_node = findChildNodeByName(representation_node,
> "BaseURL");
> > +        representation_segmentlist_node = findChildNodeByName(representation_node,
> "SegmentList");
> > +        reset_packet(&rep->pkt);
> > +
> > +        baseurl_nodes[0] = mpd_baseurl_node;
> > +        baseurl_nodes[1] = period_baseurl_node;
> > +        baseurl_nodes[2] = adaptionset_baseurl_node;
> > +        baseurl_nodes[3] = representation_baseurl_node;
> > +
> > +        if (representation_segmenttemplate_node ||
> fragment_template_node) {
> > +            fragment_timeline_node = NULL;
> > +            fragment_templates_tab[0] = representation_
> segmenttemplate_node;
> > +            fragment_templates_tab[1] = fragment_template_node;
> > +
> > +            duration_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "duration");
> > +            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "startNumber");
> > +            timescale_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "timescale");
> > +            initialization_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "initialization");
> > +            media_val = get_val_from_nodes_tab(fragment_templates_tab,
> 2, "media");
> > +
> > +            if (initialization_val) {
> > +                rep->init_section = av_mallocz(sizeof(struct fragment));
> > +                if (!rep->init_section) {
> > +                    av_free(rep);
> > +                    ret = AVERROR(ENOMEM);
> > +                    goto end;
> > +                }
> > +                rep->init_section->url = get_content_url(baseurl_nodes,
> 4, rep_id_val, rep_bandwidth_val, initialization_val);
> > +                rep->init_section->size = -1;
> > +                xmlFree(initialization_val);
> > +            }
> > +
> > +            if (media_val) {
> > +                temp_string = get_content_url(baseurl_nodes, 4,
> rep_id_val, rep_bandwidth_val, media_val);
> > +                if (temp_string) {
> > +                    if (av_stristr(temp_string, "$Number")) {
> > +                        rep->url_template = av_strdup(temp_string);
>
> Since you're no longer replacing here, this if/else tree mostly doesn't
> make sense anymore. You should remove the av_free below and store the
> result of get_content_url() directly to rep->url_template, and just have a
> check here to set tmp_url_type. Also, you need to check for a NULL return
> from get_content_url.
>
> > +                        /* A URL template is provided from which
> clients build a chunk list
> > +                         * where the chunk URLs include chunk numbers
> (like index numbers).
> > +                         */
> > +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /*
> Number-Based. */
> > +                    } else if (av_stristr(temp_string, "$Time")) {
> > +                        rep->url_template = av_strdup(temp_string);
> > +                        /* A URL template is provided from which
> clients build a chunk list
> > +                         * where the chunk URLs include chunk start
> times.
> > +                         */
> > +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /*
> Time-Based. */
> > +                    } else {
> > +                        rep->url_template = temp_string;
> > +                        temp_string = NULL;
> > +                    }
> > +                    av_free(temp_string);
> > +                }
> > +                xmlFree(media_val);
> > +            }
> > +            if (duration_val) {
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)duration_val);
> > +                xmlFree(duration_val);
> > +            }
> > +            if (timescale_val) {
> > +                rep->fragment_timescale = (int64_t) atoll((const char
> *)timescale_val);
> > +                xmlFree(timescale_val);
> > +            }
> > +            if (startnumber_val) {
> > +                rep->first_seq_no = (int64_t) atoll((const char
> *)startnumber_val);
> > +                xmlFree(startnumber_val);
> > +            }
> > +
> > +            fragment_timeline_node = findChildNodeByName(
> representation_segmenttemplate_node, "SegmentTimeline");
> > +
> > +            if (!fragment_timeline_node)
> > +                fragment_timeline_node = findChildNodeByName(fragment_template_node,
> "SegmentTimeline");
> > +            if (fragment_timeline_node) {
> > +                fragment_timeline_node = xmlFirstElementChild(fragment_
> timeline_node);
> > +                while (fragment_timeline_node) {
> > +                    ret = parse_manifest_segmenttimeline(s, rep,
> fragment_timeline_node);
> > +                    if (ret < 0) {
> > +                        return ret;
> > +                    }
> > +                    fragment_timeline_node = xmlNextElementSibling(
> fragment_timeline_node);
> > +                }
> > +            }
> > +        } else if (representation_baseurl_node &&
> !representation_segmentlist_node) {
> > +            seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                ret = AVERROR(ENOMEM);
> > +                goto end;
> > +            }
> > +            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val,
> rep_bandwidth_val, NULL);
> > +            seg->size = -1;
> > +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> > +        } else if (!representation_baseurl_node &&
> representation_segmentlist_node) {
> > +            // TODO: https://www.brendanlong.com/
> the-structure-of-an-mpeg-dash-mpd.html
> > +            // http://www-itec.uni-klu.ac.at/
> dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
> > +            xmlNodePtr fragmenturl_node = NULL;
> > +            duration_val = xmlGetProp(representation_segmentlist_node,
> "duration");
> > +            timescale_val = xmlGetProp(representation_segmentlist_node,
> "timescale");
> > +            if (duration_val) {
> > +                rep->fragment_duration = (int64_t) atoll((const char
> *)duration_val);
> > +                xmlFree(duration_val);
> > +            }
> > +            if (timescale_val) {
> > +                rep->fragment_timescale = (int64_t) atoll((const char
> *)timescale_val);
> > +                xmlFree(timescale_val);
> > +            }
> > +            fragmenturl_node = xmlFirstElementChild(
> representation_segmentlist_node);
> > +            while (fragmenturl_node) {
> > +                ret = parse_manifest_segmenturlnode(s, rep,
> fragmenturl_node, baseurl_nodes, rep_id_val, rep_bandwidth_val);
> > +                if (ret < 0) {
> > +                    return ret;
> > +                }
> > +                fragmenturl_node = xmlNextElementSibling(
> fragmenturl_node);
> > +            }
> > +        } else {
> > +            free_representation(rep);
> > +            rep = NULL;
> > +            av_log(s, AV_LOG_ERROR, "Unknown format of Representation
> node id[%s] \n", (const char *)rep_id_val);
> > +        }
> > +
> > +        if (rep) {
> > +            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
> > +                rep->fragment_timescale = 1;
> > +            if (type == AVMEDIA_TYPE_VIDEO) {
> > +                rep->rep_idx = video_rep_idx;
> > +                c->cur_video = rep;
> > +            } else {
> > +                rep->rep_idx = audio_rep_idx;
> > +                c->cur_audio = rep;
> > +            }
> > +        }
> > +    }
> > +
> > +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
> > +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
> > +
> > +end:
> > +    if (rep_id_val)
> > +        xmlFree(rep_id_val);
> > +    if (rep_bandwidth_val)
> > +        xmlFree(rep_bandwidth_val);
> > +
> > +    return ret;
> > +}
> > +
> > +static int parse_manifest_adaptationset(AVFormatContext *s, const char
> *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, xmlNodePtr
> period_baseurl_node)
> > +{
> > +    int ret = 0;
> > +    xmlNodePtr fragment_template_node = NULL;
> > +    xmlNodePtr content_component_node = NULL;
> > +    xmlNodePtr adaptionset_baseurl_node = NULL;
> > +    xmlNodePtr node = NULL;
> > +
> > +    node = xmlFirstElementChild(adaptionset_node);
> > +    while (node) {
> > +        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate"))
> {
> > +            fragment_template_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar
> *)"ContentComponent")) {
> > +            content_component_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
> > +            adaptionset_baseurl_node = node;
> > +        } else if (!xmlStrcmp(node->name, (const xmlChar
> *)"Representation")) {
> > +            ret = parse_manifest_representation(s, url, node,
> > +                                                 adaptionset_node,
> > +                                                 mpd_baseurl_node,
> > +                                                 period_baseurl_node,
> > +                                                 fragment_template_node,
> > +                                                 content_component_node,
> > +
>  adaptionset_baseurl_node);
> > +            if (ret < 0) {
> > +                return ret;
> > +            }
> > +        }
> > +        node = xmlNextElementSibling(node);
> > +    }
> > +    return 0;
> > +}
> > +
> > +
> > +static int parse_manifest(AVFormatContext *s, const char *url,
> AVIOContext *in)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    int close_in = 0;
> > +    uint8_t *new_url = NULL;
> > +    int64_t filesize = 0;
> > +    char *buffer = NULL;
> > +    AVDictionary *opts = NULL;
> > +    xmlDoc *doc = NULL;
> > +    xmlNodePtr root_element = NULL;
> > +    xmlNodePtr node = NULL;
> > +    xmlNodePtr period_node = NULL;
> > +    xmlNodePtr mpd_baseurl_node = NULL;
> > +    xmlNodePtr period_baseurl_node = NULL;
> > +    xmlNodePtr adaptionset_node = NULL;
> > +    xmlAttrPtr attr = NULL;
> > +    xmlChar *val  = NULL;
> > +    uint32_t perdiod_duration_sec = 0;
> > +    uint32_t perdiod_start_sec = 0;
> > +    int32_t audio_rep_idx = 0;
> > +    int32_t video_rep_idx = 0;
> > +
> > +    if (!in) {
> > +        close_in = 1;
> > +        /* This is XML manifest there is no need to set range header */
> > +        av_dict_set(&opts, "seekable", "0", 0);
> > +        // broker prior HTTP options that should be consistent across
> requests
> > +        av_dict_set(&opts, "user-agent", c->user_agent, 0);
> > +        av_dict_set(&opts, "cookies", c->cookies, 0);
> > +        av_dict_set(&opts, "headers", c->headers, 0);
> > +
> > +        ret = avio_open2(&in, url, AVIO_FLAG_READ,
> c->interrupt_callback, &opts);
> > +        av_dict_free(&opts);
> > +        if (ret < 0)
> > +            return ret;
> > +    }
> > +
> > +    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >=
> 0) {
> > +        c->base_url = av_strdup(new_url);
> > +    } else {
> > +        c->base_url = av_strdup(url);
> > +    }
> > +
> > +    filesize = avio_size(in);
> > +    if (filesize <= 0){
>
> Space between ) and { (this occurs a few times).
>
> > +        filesize = 8 * 1024;
> > +    }
> > +
> > +    buffer = av_mallocz(filesize);
> > +    if (!buffer) {
> > +        return AVERROR(ENOMEM);
> > +    }
> > +
> > +    filesize = avio_read(in, buffer, filesize);
> > +    if (filesize > 0) {
> > +        LIBXML_TEST_VERSION
> > +
> > +        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
> > +        root_element = xmlDocGetRootElement(doc);
> > +        node = root_element;
> > +
> > +        if (!node) {
> > +            ret = AVERROR_INVALIDDATA;
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> root node\n", url);
> > +            goto cleanup;
> > +        }
> > +
> > +        if (node->type != XML_ELEMENT_NODE ||
> > +            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
> > +            ret = AVERROR_INVALIDDATA;
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root
> node name[%s] type[%d]\n", url, node->name, (int)node->type);
> > +            goto cleanup;
> > +        }
> > +
> > +        val = xmlGetProp(node, "type");
> > +        if (!val) {
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> type attrib\n", url);
> > +            ret = AVERROR_INVALIDDATA;
> > +            goto cleanup;
> > +        }
> > +        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
> > +            c->is_live = 1;
> > +        xmlFree(val);
> > +
> > +        attr = node->properties;
> > +        while (attr) {
> > +            val = xmlGetProp(node, attr->name);
> > +
> > +            if (!xmlStrcmp(attr->name, (const xmlChar
> *)"availabilityStartTime")) {
> > +                c->availability_start_time_sec = GetUTCDateTimeInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"publishTime")) {
> > +                c->publish_time_sec = GetUTCDateTimeInSec(s, (const
> char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"minimumUpdatePeriod")) {
> > +                c->minimum_update_period_sec = GetDurationInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"timeShiftBufferDepth")) {
> > +                c->time_shift_buffer_depth_sec = GetDurationInSec(s,
> (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"minBufferTime")) {
> > +                c->min_buffer_time_sec = GetDurationInSec(s, (const
> char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"suggestedPresentationDelay")) {
> > +                c->suggested_presentation_delay_sec =
> GetDurationInSec(s, (const char *)val);
> > +            } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"mediaPresentationDuration")) {
> > +                c->media_presentation_duration_sec =
> GetDurationInSec(s, (const char *)val);
> > +            }
> > +            attr = attr->next;
> > +            xmlFree(val);
> > +        }
> > +
> > +        mpd_baseurl_node = findChildNodeByName(node, "BaseURL");
> > +
> > +        // at now we can handle only one period, with the longest
> duration
> > +        node = xmlFirstElementChild(node);
> > +        while (node) {
> > +            if (!xmlStrcmp(node->name, (const xmlChar *)"Period")) {
> > +                perdiod_duration_sec = 0;
> > +                perdiod_start_sec = 0;
> > +                attr = node->properties;
> > +                while (attr) {
> > +                    val = xmlGetProp(node, attr->name);
> > +                    if (!xmlStrcmp(attr->name, (const xmlChar
> *)"duration")) {
> > +                        perdiod_duration_sec = GetDurationInSec(s,
> (const char *)val);
> > +                    } else if (!xmlStrcmp(attr->name, (const xmlChar
> *)"start")) {
> > +                        perdiod_start_sec = GetDurationInSec(s, (const
> char *)val);
> > +                    }
> > +                    attr = attr->next;
> > +                    xmlFree(val);
> > +                }
> > +                if ((perdiod_duration_sec) >= (c->period_duration_sec))
> {
> > +                    period_node = node;
> > +                    c->period_duration_sec = perdiod_duration_sec;
> > +                    c->period_start_sec = perdiod_start_sec;
> > +                    if (c->period_start_sec > 0)
> > +                        c->media_presentation_duration_sec =
> c->period_duration_sec;
> > +                }
> > +            }
> > +            node = xmlNextElementSibling(node);
> > +        }
> > +        if (!period_node) {
> > +            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing
> Period node\n", url);
> > +            ret = AVERROR_INVALIDDATA;
> > +            goto cleanup;
> > +        }
> > +        // explore AdaptationSet
> > +        adaptionset_node = xmlFirstElementChild(period_node);
> > +        while (adaptionset_node) {
> > +            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar
> *)"BaseURL")) {
> > +                period_baseurl_node = adaptionset_node;
> > +            } else if (!xmlStrcmp(adaptionset_node->name, (const
> xmlChar *)"AdaptationSet")) {
> > +                parse_manifest_adaptationset(s, url, adaptionset_node,
> mpd_baseurl_node, period_baseurl_node);
> > +            }
> > +            adaptionset_node = xmlNextElementSibling(adaptionset_node);
> > +        }
> > +        if (c->cur_video) {
> > +            c->cur_video->rep_count = video_rep_idx;
> > +            c->cur_video->fix_multiple_stsd_order = 1;
> > +            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n",
> (int)c->cur_video->rep_idx);
> > +            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n",
> (int)video_rep_idx);
> > +        }
> > +        if (c->cur_audio) {
> > +            c->cur_audio->rep_count = audio_rep_idx;
> > +        }
> > +
> > +cleanup:
> > +        /*free the document */
> > +        xmlFreeDoc(doc);
> > +        xmlCleanupParser();
> > +    } else {
> > +        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
> > +        ret = AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    av_free(new_url);
> > +    av_free(buffer);
> > +    if (close_in) {
> > +        avio_close(in);
> > +    }
> > +    return ret;
> > +}
> > +
> > +static int64_t calc_cur_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec) - c->presentation_delay_sec) *
> pls->fragment_timescale) / pls->fragment_duration;
> > +    } else {
> > +        num = pls->first_seq_no;
> > +    }
> > +    return num;
> > +}
> > +
> > +static int64_t calc_min_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) *
> pls->fragment_timescale)  / pls->fragment_duration;
> > +    } else {
> > +        num = pls->first_seq_no;
> > +    }
> > +    return num;
> > +}
> > +
> > +static int64_t calc_max_seg_no(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t num = 0;
> > +    int64_t i = 0;
> > +
> > +    if (c->is_live) {
> > +        if (!pls->fragment_duration) {
> > +            av_log(s, AV_LOG_ERROR, "Get not get fragment duration
> value in live mode\n");
> > +            return pls->first_seq_no;
> > +        }
> > +        num = pls->first_seq_no + (((GetCurrentTimeInSec() -
> c->availability_start_time_sec)) * pls->fragment_timescale)  /
> pls->fragment_duration;
> > +    } else {
> > +        if (pls->n_fragments) {
> > +            num = pls->first_seq_no + pls->n_fragments - 1;
> > +        } else if (pls->n_timelines) {
> > +            num = pls->first_seq_no + pls->n_timelines - 1;
> > +            for (i = 0; i < pls->n_timelines; ++i) {
> > +                num += pls->timelines[i]->r;
> > +            }
> > +        } else {
> > +            num = pls->first_seq_no + (c->media_presentation_duration_sec
> * pls->fragment_timescale) / pls->fragment_duration;
> > +        }
> > +    }
> > +
> > +    return num;
> > +}
> > +
> > +static int64_t get_fragment_start_time(struct representation *pls,
> int64_t cur_seq_no)
> > +{
> > +    int64_t i = 0;
> > +    int64_t j = 0;
> > +    int64_t num = 0;
> > +    int64_t startTime = 0;
> > +
> > +    if (pls->n_timelines) {
> > +        for (i = 0; i < pls->n_timelines; ++i) {
> > +            if (pls->timelines[i]->t > 0) {
> > +                startTime = pls->timelines[i]->t;
> > +            }
> > +            if (num == cur_seq_no)
> > +                goto finish;
> > +            startTime += pls->timelines[i]->d;
> > +            for (j = 0; j < pls->timelines[i]->r; ++j) {
> > +                num++;
> > +                if (num == cur_seq_no)
> > +                    goto finish;
> > +                startTime += pls->timelines[i]->d;
> > +            }
> > +            num++;
> > +        }
> > +    }
> > +
> > +finish:
> > +    return startTime;
> > +}
> > +
> > +static struct fragment *get_current_fragment(struct representation *pls)
> > +{
> > +    int64_t tmp_val = 0;
> > +    int64_t min_seq_no = 0;
> > +    int64_t max_seq_no = 0;
> > +    char buffer[1024];
> > +    char *tmp_str = NULL;
> > +    struct fragment *seg = NULL;
> > +    struct fragment *seg_ptr = NULL;
> > +    DASHContext *c = pls->parent->priv_data;
> > +
> > +    if (pls->n_fragments > 0) {
> > +        if (pls->cur_seq_no < pls->n_fragments) {
> > +            seg_ptr = pls->fragments[pls->cur_seq_no];
> > +            seg = av_mallocz(sizeof(struct fragment));
> > +            if (!seg) {
> > +                return NULL;
> > +            }
> > +            seg->url = av_strdup(seg_ptr->url);
> > +            if (!seg->url) {
> > +                av_free(seg);
> > +                return NULL;
> > +            }
> > +            seg->size = seg_ptr->size;
> > +            seg->url_offset = seg_ptr->url_offset;
> > +            return seg;
> > +        }
> > +    }
> > +    if (c->is_live) {
> > +        while (1) {
> > +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> > +            max_seq_no = calc_max_seg_no(pls->parent, pls);
> > +
> > +            if (pls->cur_seq_no <= min_seq_no) {
> > +                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment:
> cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> > +                       (int64_t)pls->cur_seq_no, min_seq_no,
> max_seq_no, (int)pls->rep_idx);
> > +                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
> > +            } else if (pls->cur_seq_no > max_seq_no) {
> > +                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment:
> min[%"PRId64"] max[%"PRId64"], playlist %d\n",
> > +                       min_seq_no, max_seq_no, (int)pls->rep_idx);
> > +                av_usleep(1000);
> > +                continue;
> > +            }
> > +            break;
> > +        }
> > +        seg = av_mallocz(sizeof(struct fragment));
> > +        if (!seg) {
> > +            return NULL;
> > +        }
> > +    } else if (pls->cur_seq_no <= pls->last_seq_no) {
> > +        seg = av_mallocz(sizeof(struct fragment));
> > +        if (!seg) {
> > +            return NULL;
> > +        }
> > +    }
> > +    if (seg) {
> > +        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
> > +            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ?
> pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
> > +            if (av_get_frame_filename(buffer, sizeof(buffer),
> pls->url_template, (int64_t)tmp_val) < 0) {
>
> It's not a security issue like with printf functions, but you shouldn't
> use av_get_frame_filename() with arbitrary format-string input either, as
> it will fail if the input already contained a '%' character. I also don't
> understand how this is supposed to work, since your source string
> (url_template) shouldn't contain a "%d"?
>

Use av_get_frame_filename() because the filename or timestring format is
have a template looks like -hls_segment_filename or segment output segment
files template:
for example:  "output_file-%05d.ts" , of course user may input
"output_file-%%%%s%%%s" or other, so use av_get_frame_filename() to cover
that format wrong, and give an error message.

>
> > +                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment
> filename template %s\n", pls->url_template);
> > +                return NULL;
> > +            }
> > +
> > +            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
> > +                tmp_str = replace_template_str(buffer, "$Number$");
> > +            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME){
> > +                tmp_str = replace_template_str(buffer, "$Time$");
> > +            } else {
> > +                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type
> [%d]\n", pls->tmp_url_type);
>
> Since this shouldn't be possible, an av_assert0 is applicable; no need to
> log the value.
>
> > +                return NULL;
> > +            }
> > +
> > +            seg->url = av_strdup(tmp_str);
> > +            if (!seg->url) {
> > +                av_free(tmp_str);
> > +                return NULL;
> > +            }
> > +        } else {
> > +            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to
> resolve template url '%s'\n", pls->url_template);
> > +            seg->url = av_strdup(pls->url_template);
> > +            if (!seg->url) {
> > +                return NULL;
> > +            }
> > +        }
> > +        seg->size = -1;
> > +    }
> > +
> > +    av_free(tmp_str);
> > +    return seg;
> > +}
> > +
> > +enum ReadFromURLMode {
> > +    READ_NORMAL,
> > +    READ_COMPLETE,
> > +};
> > +
> > +static int read_from_url(struct representation *pls, struct fragment
> *seg,
> > +                         uint8_t *buf, int buf_size,
> > +                         enum ReadFromURLMode mode)
> > +{
> > +    int ret;
> > +
> > +    /* limit read if the fragment was only a part of a file */
> > +    if (seg->size >= 0)
> > +        buf_size = FFMIN(buf_size, pls->cur_seg_size -
> pls->cur_seg_offset);
> > +
> > +    if (mode == READ_COMPLETE) {
> > +        ret = avio_read(pls->input, buf, buf_size);
> > +        if (ret < buf_size) {
> > +            av_log(pls->parent, AV_LOG_WARNING, "Could not read
> complete fragment.\n");
> > +        }
> > +    } else {
> > +        ret = avio_read(pls->input, buf, buf_size);
> > +    }
> > +    if (ret > 0)
> > +        pls->cur_seg_offset += ret;
> > +
> > +    return ret;
> > +}
> > +
> > +static int open_input(DASHContext *c, struct representation *pls,
> struct fragment *seg)
> > +{
> > +    AVDictionary *opts = NULL;
> > +    char url[MAX_URL_SIZE];
> > +    int ret;
> > +
> > +    // broker prior HTTP options that should be consistent across
> requests
> > +    av_dict_set(&opts, "user-agent", c->user_agent, 0);
> > +    av_dict_set(&opts, "cookies", c->cookies, 0);
> > +    av_dict_set(&opts, "headers", c->headers, 0);
> > +    if (c->is_live) {
> > +        av_dict_set(&opts, "seekable", "0", 0);
> > +    }
> > +
> > +    if (seg->size >= 0) {
> > +        /* try to restrict the HTTP request to the part we want
> > +         * (if this is in fact a HTTP request) */
> > +        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
> > +        av_dict_set_int(&opts, "end_offset", seg->url_offset +
> seg->size, 0);
> > +    }
> > +
> > +    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
> > +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s',
> offset %"PRId64", playlist %d\n",
> > +           url, seg->url_offset, pls->rep_idx);
> > +    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts,
> NULL);
> > +    if (ret < 0) {
> > +        goto cleanup;
> > +    }
> > +
> > +    /* Seek to the requested position. If this was a HTTP request, the
> offset
> > +     * should already be where want it to, but this allows e.g. local
> testing
> > +     * without a HTTP server. */
> > +    if (!ret && seg->url_offset) {
> > +        int64_t seekret = avio_seek(pls->input, seg->url_offset,
> SEEK_SET);
> > +        if (seekret < 0) {
> > +            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset
> %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
> > +            ret = (int) seekret;
> > +            ff_format_io_close(pls->parent, &pls->input);
> > +        }
> > +    }
> > +
> > +cleanup:
> > +    av_dict_free(&opts);
> > +    pls->cur_seg_offset = 0;
> > +    pls->cur_seg_size = seg->size;
> > +    return ret;
> > +}
> > +
> > +static int update_init_section(struct representation *pls)
> > +{
> > +    static const int max_init_section_size = 1024*1024;
> > +    DASHContext *c = pls->parent->priv_data;
> > +    int64_t sec_size = 0;
> > +    int64_t urlsize = 0;
> > +    int ret = 0;
> > +
> > +    /* read init section only once per representation */
> > +    if (!pls->init_section || pls->init_sec_buf) {
> > +        return 0;
> > +    }
> > +
> > +    ret = open_input(c, pls, pls->init_section);
> > +    if (ret < 0) {
> > +        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an
> initialization section in playlist %d\n", pls->rep_idx);
> > +        return ret;
> > +    }
> > +
> > +    if (pls->init_section->size >= 0) {
> > +        sec_size = pls->init_section->size;
> > +    } else if ((urlsize = avio_size(pls->input)) >= 0) {
> > +        sec_size = urlsize;
> > +    } else {
> > +        sec_size = max_init_section_size;
> > +    }
> > +    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization
> section of size %"PRId64"\n", sec_size);
> > +    sec_size = FFMIN(sec_size, max_init_section_size);
> > +    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size,
> sec_size);
> > +    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf,
> pls->init_sec_buf_size, READ_COMPLETE);
> > +    ff_format_io_close(pls->parent, &pls->input);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
> > +        uint8_t **stsd_entries = NULL;
> > +        int *stsd_entries_size = NULL;
> > +        int i = 4;
> > +
> > +        while (i <= (ret - 4)) {
> > +            // find start stsd atom
> > +            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
> > +                /* 1B version
> > +                 * 3B flags
> > +                 * 4B num of entries */
> > +                int stsd_first_offset = i + 8;
> > +                int stsd_offset = 0;
> > +                int j = 0;
> > +                uint32_t stsd_count = AV_RB32(pls->init_sec_buf +
> stsd_first_offset);
> > +                stsd_first_offset += 4;
> > +                if (stsd_count != pls->rep_count) {
> > +                    i++;
> > +                    continue;
> > +                }
> > +                // find all stsd entries
> > +                stsd_entries = av_mallocz_array(stsd_count,
> sizeof(*stsd_entries));
> > +                stsd_entries_size = av_mallocz_array(stsd_count,
> sizeof(*stsd_entries_size));
> > +                for (j = 0; j < stsd_count; ++j) {
> > +                    /* 4B - size
> > +                     * 4B - format */
> > +                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf +
> stsd_first_offset + stsd_offset);
> > +                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
> > +                    memcpy(stsd_entries[j], pls->init_sec_buf +
> stsd_first_offset + stsd_offset, stsd_entries_size[j]);
> > +                    stsd_offset += stsd_entries_size[j];
> > +                }
> > +                // reorder stsd entries
> > +                // as first put stsd entry for current representation
> > +                j = pls->rep_idx;
> > +                stsd_offset = stsd_first_offset;
> > +                memcpy(pls->init_sec_buf + stsd_offset,
> stsd_entries[j], stsd_entries_size[j]);
> > +                stsd_offset += stsd_entries_size[j];
> > +                for (j = 0; j < stsd_count; ++j) {
> > +                    if (j != pls->rep_idx) {
> > +                        memcpy(pls->init_sec_buf + stsd_offset,
> stsd_entries[j], stsd_entries_size[j]);
> > +                        stsd_offset += stsd_entries_size[j];
> > +                    }
> > +                    av_free(stsd_entries[j]);
> > +                }
> > +                av_freep(&stsd_entries);
> > +                av_freep(&stsd_entries_size);
> > +                break;
> > +            }
> > +            i++;
> > +        }
> > +    }
> > +
> > +    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section
> size[%d]\n", pls, (int)ret);
> > +    pls->init_sec_data_len = ret;
> > +    pls->init_sec_buf_read_offset = 0;
> > +
> > +    return 0;
> > +}
> > +
> > +static int64_t seek_data(void *opaque, int64_t offset, int whence)
> > +{
> > +    struct representation *v = opaque;
> > +    if (v->n_fragments && !v->init_sec_data_len) {
> > +        return avio_seek(v->input, offset, whence);
> > +    }
> > +
> > +    return AVERROR(ENOSYS);
> > +}
> > +
> > +static int read_data(void *opaque, uint8_t *buf, int buf_size)
> > +{
> > +    int ret = 0;
> > +    struct representation *v = opaque;
> > +    DASHContext *c = v->parent->priv_data;
> > +
> > +restart:
> > +    if (!v->input) {
> > +        free_fragment(&v->cur_seg);
> > +        v->cur_seg = get_current_fragment(v);
> > +        if (!v->cur_seg) {
> > +            ret = AVERROR_EOF;
> > +            goto end;
> > +        }
> > +
> > +        /* load/update Media Initialization Section, if any */
> > +        ret = update_init_section(v);
> > +        if (ret)
> > +            goto end;
> > +
> > +        ret = open_input(c, v, v->cur_seg);
> > +        if (ret < 0) {
> > +            if (ff_check_interrupt(c->interrupt_callback)) {
> > +                goto end;
> > +                ret = AVERROR_EXIT;
> > +            }
> > +            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment
> of playlist %d\n", v->rep_idx);
> > +            v->cur_seq_no++;
> > +            goto restart;
> > +        }
> > +    }
> > +
> > +    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
> > +        /* Push init section out first before first actual fragment */
> > +        int copy_size = FFMIN(v->init_sec_data_len -
> v->init_sec_buf_read_offset, buf_size);
> > +        memcpy(buf, v->init_sec_buf, copy_size);
> > +        v->init_sec_buf_read_offset += copy_size;
> > +        ret = copy_size;
> > +        goto end;
> > +    }
> > +
> > +    /* check the v->cur_seg, if it is null, get current and double
> check if the new v->cur_seg*/
> > +    if (!v->cur_seg) {
> > +        v->cur_seg = get_current_fragment(v);
> > +    }
> > +    if (!v->cur_seg) {
> > +        ret = AVERROR_EOF;
> > +        goto end;
> > +    }
> > +    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
> > +    if (ret > 0)
> > +        goto end;
> > +    ff_format_io_close(v->parent, &v->input);
> > +    v->cur_seq_no++;
> > +    goto restart;
> > +
> > +end:
> > +    return ret;
> > +}
> > +
> > +static int save_avio_options(AVFormatContext *s)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    const char *opts[] = { "headers", "user_agent", "user-agent",
> "cookies", NULL }, **opt = opts;
> > +    uint8_t *buf = NULL;
> > +    int ret = 0;
> > +
> > +    while (*opt) {
> > +        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0)
> {
> > +            if (buf) {
> > +                ret = av_dict_set(&c->avio_opts, *opt, buf,
> AV_DICT_DONT_STRDUP_VAL);
> > +                if (ret < 0)
> > +                    return ret;
> > +            }
> > +        }
> > +        opt++;
> > +    }
> > +
> > +    return ret;
> > +}
> > +
> > +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const
> char *url,
> > +                          int flags, AVDictionary **opts)
> > +{
> > +    av_log(s, AV_LOG_ERROR,
> > +           "A HDS playlist item '%s' referred to an external file '%s'.
> "
> > +           "Opening this file was forbidden for security reasons\n",
> > +           s->filename, url);
> > +    return AVERROR(EPERM);
> > +}
> > +
> > +static int reopen_demux_for_component(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    AVInputFormat *in_fmt = NULL;
> > +    AVDictionary  *in_fmt_opts = NULL;
> > +    uint8_t *avio_ctx_buffer  = NULL;
> > +    int ret = 0;
> > +
> > +    if (pls->ctx) {
> > +        /* note: the internal buffer could have changed, and be !=
> avio_ctx_buffer */
> > +        av_freep(&pls->pb.buffer);
> > +        memset(&pls->pb, 0x00, sizeof(AVIOContext));
> > +        pls->ctx->pb = NULL;
> > +        avformat_close_input(&pls->ctx);
> > +        pls->ctx = NULL;
> > +    }
> > +    if (!(pls->ctx = avformat_alloc_context())) {
> > +        ret = AVERROR(ENOMEM);
> > +        goto fail;
> > +    }
> > +
> > +    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
> > +    if (!avio_ctx_buffer ){
> > +        ret = AVERROR(ENOMEM);
> > +        avformat_free_context(pls->ctx);
> > +        pls->ctx = NULL;
> > +        goto fail;
> > +    }
> > +    if (c->is_live) {
> > +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
> > +    } else {
> > +        ffio_init_context(&pls->pb, avio_ctx_buffer ,
> INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
> > +    }
> > +    pls->pb.seekable = 0;
> > +
> > +    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
> > +        goto fail;
> > +
> > +    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
> > +    pls->ctx->probesize = 1024 * 4;
> > +    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
> > +    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
> > +    if (ret < 0) {
> > +        av_log(s, AV_LOG_ERROR, "Error when loading first fragment,
> playlist %d\n", (int)pls->rep_idx);
> > +        avformat_free_context(pls->ctx);
> > +        pls->ctx = NULL;
> > +        goto fail;
> > +    }
> > +
> > +    pls->ctx->pb = &pls->pb;
> > +    pls->ctx->io_open  = nested_io_open;
> > +
> > +    // provide additional information from mpd if available
> > +    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts);
> //pls->init_section->url
> > +    av_dict_free(&in_fmt_opts);
> > +    if (ret < 0)
> > +        goto fail;
> > +    if (pls->n_fragments) {
> > +        ret = avformat_find_stream_info(pls->ctx, NULL);
> > +        if (ret < 0)
> > +            goto fail;
> > +    }
> > +
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int open_demux_for_component(AVFormatContext *s, struct
> representation *pls)
> > +{
> > +    int ret = 0;
> > +    int i;
> > +
> > +    pls->parent = s;
> > +    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
> > +    pls->last_seq_no = calc_max_seg_no(s, pls);
> > +
> > +    ret = reopen_demux_for_component(s, pls);
> > +    if (ret < 0) {
> > +        goto fail;
> > +    }
> > +    for (i = 0; i < pls->ctx->nb_streams; i++) {
> > +        AVStream *st = avformat_new_stream(s, NULL);
> > +        AVStream *ist = pls->ctx->streams[i];
> > +        if (!st) {
> > +            ret = AVERROR(ENOMEM);
> > +            goto fail;
> > +        }
> > +        st->id = i;
> > +        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->
> codecpar);
> > +        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num,
> ist->time_base.den);
> > +    }
> > +
> > +    return 0;
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int dash_read_header(AVFormatContext *s)
> > +{
> > +    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    int stream_index = 0;
> > +
> > +    c->interrupt_callback = &s->interrupt_callback;
> > +    // if the URL context is good, read important options we must
> broker later
> > +    if (u) {
> > +        update_options(&c->user_agent, "user-agent", u);
> > +        update_options(&c->cookies, "cookies", u);
> > +        update_options(&c->headers, "headers", u);
> > +    }
> > +
> > +    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
> > +        goto fail;
> > +
> > +    if ((ret = save_avio_options(s)) < 0)
> > +        goto fail;
> > +
> > +    /* If this isn't a live stream, fill the total duration of the
> > +     * stream. */
> > +    if (!c->is_live) {
> > +        s->duration = (int64_t) c->media_presentation_duration_sec *
> AV_TIME_BASE;
> > +    }
> > +
> > +    /* Open the demuxer for curent video and current audio components
> if available */
> > +    if (!ret && c->cur_video) {
> > +        ret = open_demux_for_component(s, c->cur_video);
> > +        if (!ret) {
> > +            c->cur_video->stream_index = stream_index;
> > +            ++stream_index;
> > +        } else {
> > +            free_representation(c->cur_video);
> > +            c->cur_video = NULL;
> > +        }
> > +    }
> > +
> > +    if (!ret && c->cur_audio) {
> > +        ret = open_demux_for_component(s, c->cur_audio);
> > +        if (!ret) {
> > +            c->cur_audio->stream_index = stream_index;
> > +            ++stream_index;
> > +        } else {
> > +            free_representation(c->cur_audio);
> > +            c->cur_audio = NULL;
> > +        }
> > +    }
> > +
> > +    if (!stream_index) {
> > +        ret = AVERROR_INVALIDDATA;
> > +        goto fail;
> > +    }
> > +
> > +    /* Create a program */
> > +    if (!ret) {
> > +        AVProgram *program;
> > +        program = av_new_program(s, 0);
> > +        if (!program) {
> > +            goto fail;
> > +        }
> > +
> > +        if (c->cur_video) {
> > +            av_program_add_stream_index(s, 0,
> c->cur_video->stream_index);
> > +        }
> > +
> > +        if (c->cur_audio) {
> > +            av_program_add_stream_index(s, 0,
> c->cur_audio->stream_index);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +fail:
> > +    return ret;
> > +}
> > +
> > +static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +    int ret = 0;
> > +    struct representation *cur = NULL;
> > +
> > +    if (!c->cur_audio && !c->cur_video) {
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    if (c->cur_audio && !c->cur_video) {
> > +        cur = c->cur_audio;
> > +    } else if (!c->cur_audio && c->cur_video) {
> > +        cur = c->cur_video;
> > +    } else if (c->cur_video->cur_timestamp <
> c->cur_audio->cur_timestamp) {
> > +        cur = c->cur_video;
> > +    } else {
> > +        cur = c->cur_audio;
> > +    }
> > +
> > +    if (cur->ctx) {
> > +        ret = av_read_frame(cur->ctx, &cur->pkt);
> > +        if (ret < 0) {
> > +            av_packet_unref(&cur->pkt);
> > +        } else {
> > +            /* If we got a packet, return it */
> > +            *pkt = cur->pkt;
> > +            cur->cur_timestamp = av_rescale(pkt->pts,
> (int64_t)cur->ctx->streams[0]->time_base.num * 90000,
> cur->ctx->streams[0]->time_base.den);
> > +            pkt->stream_index = cur->stream_index;
> > +            reset_packet(&cur->pkt);
> > +            return 0;
> > +        }
> > +    }
> > +    return AVERROR_EOF;
> > +}
> > +
> > +static int dash_close(AVFormatContext *s)
> > +{
> > +    DASHContext *c = s->priv_data;
> > +
> > +    if (c->cur_audio) {
> > +        free_representation(c->cur_audio);
> > +    }
> > +
> > +    if (c->cur_video) {
> > +        free_representation(c->cur_video);
> > +    }
> > +
> > +    av_freep(&c->cookies);
> > +    av_freep(&c->user_agent);
> > +    av_dict_free(&c->avio_opts);
> > +    av_freep(&c->base_url);
> > +    return 0;
> > +}
> > +
> > +static int dash_seek(AVFormatContext *s, struct representation *pls,
> int64_t seek_pos_msec, int flags)
> > +{
> > +    int ret = 0;
> > +    int i = 0;
> > +    int j = 0;
> > +    int64_t duration = 0;
> > +
> > +    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms],
> playlist %d\n", seek_pos_msec, pls->rep_idx);
> > +
> > +    // single fragment mode
> > +    if (pls->n_fragments) {
> > +        pls->cur_timestamp = 0;
> > +        pls->cur_seg_offset = 0;
> > +        ff_read_frame_flush(pls->ctx);
> > +        return av_seek_frame(pls->ctx, -1, seek_pos_msec*1000, flags);
> > +    }
> > +
> > +    if (pls->input)
> > +        ff_format_io_close(pls->parent, &pls->input);
> > +
> > +    // find the nearest fragment
> > +    if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
> > +        int64_t num = pls->first_seq_no;
> > +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
> SegmentTimeline start n_timelines[%d] last_seq_no[%"PRId64"], playlist
> %d.\n",
> > +               (int)pls->n_timelines, (int64_t)pls->last_seq_no,
> (int)pls->rep_idx);
> > +        for (i = 0; i < pls->n_timelines; i++) {
> > +            if (pls->timelines[i]->t > 0) {
> > +                duration = pls->timelines[i]->t;
> > +            }
> > +            duration += pls->timelines[i]->d;
> > +            if (seek_pos_msec < ((duration * 1000) /
> pls->fragment_timescale)) {
> > +                goto set_seq_num;
> > +            }
> > +            for (j = 0; j < pls->timelines[i]->r; j++) {
> > +                duration += pls->timelines[i]->d;
> > +                num++;
> > +                if (seek_pos_msec < ((duration * 1000) /
> pls->fragment_timescale)) {
> > +                    goto set_seq_num;
> > +                }
> > +            }
> > +            num++;
> > +        }
> > +
> > +set_seq_num:
> > +        pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no :
> num;
> > +        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with
> SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
> > +               (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
> > +    } else if (pls->fragment_duration > 0) {
> > +        pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec *
> pls->fragment_timescale) / pls->fragment_duration) / 1000;
> > +    } else {
> > +        av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing
> fragment_duration\n");
> > +        pls->cur_seq_no = pls->first_seq_no;
> > +    }
> > +    pls->cur_timestamp = 0;
> > +    pls->cur_seg_offset = 0;
> > +    pls->init_sec_buf_read_offset = 0;
> > +    ret = reopen_demux_for_component(s, pls);
> > +
> > +    return ret;
> > +}
> > +
> > +static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t
> timestamp, int flags)
> > +{
> > +    int ret = 0;
> > +    DASHContext *c = s->priv_data;
> > +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
> > +                                         s->streams[stream_index]->
> time_base.den,
> > +                                         flags & AVSEEK_FLAG_BACKWARD ?
> > +                                         AV_ROUND_DOWN : AV_ROUND_UP);
> > +    if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
> > +        return AVERROR(ENOSYS);
> > +    if (c->cur_audio) {
> > +        ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
> > +    }
> > +    if (!ret && c->cur_video) {
> > +        ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
> > +    }
> > +    return ret;
> > +}
> > +
> > +static int dash_probe(AVProbeData *p)
> > +{
> > +    if (!av_stristr(p->buf, "<MPD"))
> > +        return 0;
> > +
> > +    if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
> > +        av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
> > +        return AVPROBE_SCORE_MAX;
> > +    }
> > +    if (av_stristr(p->buf, "dash:profile")) {
> > +        return AVPROBE_SCORE_MAX >> 1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +#define OFFSET(x) offsetof(DASHContext, x)
> > +#define FLAGS AV_OPT_FLAG_DECODING_PARAM
> > +static const AVOption dash_options[] = {
> > +    {NULL}
> > +};
> > +
> > +static const AVClass dash_class = {
> > +    .class_name = "dash",
> > +    .item_name  = av_default_item_name,
> > +    .option     = dash_options,
> > +    .version    = LIBAVUTIL_VERSION_INT,
> > +};
> > +
> > +AVInputFormat ff_dash_demuxer = {
> > +    .name           = "dash",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming
> over HTTP"),
> > +    .priv_class     = &dash_class,
> > +    .priv_data_size = sizeof(DASHContext),
> > +    .read_probe     = dash_probe,
> > +    .read_header    = dash_read_header,
> > +    .read_packet    = dash_read_packet,
> > +    .read_close     = dash_close,
> > +    .read_seek      = dash_read_seek,
> > +    .flags          = AVFMT_NO_BYTE_SEEK,
> > +};
> > --
> > 2.10.1 (Apple Git-78)
> >
> >
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
diff mbox

Patch

diff --git a/configure b/configure
index 9cc7e7d..4a6fe8a 100755
--- a/configure
+++ b/configure
@@ -273,6 +273,7 @@  External library support:
   --enable-libxcb-shape    enable X11 grabbing shape rendering [autodetect]
   --enable-libxvid         enable Xvid encoding via xvidcore,
                            native MPEG-4/Xvid encoder exists [no]
+  --enable-libxml2            enable XML parsing using the C library libxml2 [no]
   --enable-libzimg         enable z.lib, needed for zscale filter [no]
   --enable-libzmq          enable message passing via libzmq [no]
   --enable-libzvbi         enable teletext support via libzvbi [no]
@@ -1574,6 +1575,7 @@  EXTERNAL_LIBRARY_LIST="
     libvpx
     libwavpack
     libwebp
+    libxml2
     libzimg
     libzmq
     libzvbi
@@ -2906,6 +2908,7 @@  avi_muxer_select="riffenc"
 caf_demuxer_select="iso_media riffdec"
 caf_muxer_select="iso_media"
 dash_muxer_select="mp4_muxer"
+dash_demuxer_deps="libxml2"
 dirac_demuxer_select="dirac_parser"
 dts_demuxer_select="dca_parser"
 dtshd_demuxer_select="dca_parser"
@@ -5908,6 +5911,7 @@  enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
                                check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||
                                die "ERROR: openssl not found"; }
 enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; }
+enabled libxml2             && require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion
 
 # libdc1394 check
 if enabled libdc1394; then
diff --git a/libavformat/Makefile b/libavformat/Makefile
index f56ef16..d77ffaa 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -134,6 +134,7 @@  OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
 OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
 OBJS-$(CONFIG_DATA_MUXER)                += rawenc.o
 OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o
+OBJS-$(CONFIG_DASH_DEMUXER)              += dashdec.o
 OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
 OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
 OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 09e62c3..d57314b 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -96,7 +96,7 @@  static void register_all(void)
     REGISTER_DEMUXER (CINE,             cine);
     REGISTER_DEMUXER (CONCAT,           concat);
     REGISTER_MUXER   (CRC,              crc);
-    REGISTER_MUXER   (DASH,             dash);
+    REGISTER_MUXDEMUX(DASH,             dash);
     REGISTER_MUXDEMUX(DATA,             data);
     REGISTER_MUXDEMUX(DAUD,             daud);
     REGISTER_DEMUXER (DCSTR,            dcstr);
diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
new file mode 100644
index 0000000..a4d3ba2
--- /dev/null
+++ b/libavformat/dashdec.c
@@ -0,0 +1,1842 @@ 
+/*
+ * 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"
+
+#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 {
+    /* t: 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@t + @d * (@r + 1)).
+     * */
+    int64_t t;
+    /* r: Element or Attribute Name
+     * specifies the repeat count of the number of following contiguous Segments with
+     * the same duration expressed by the value of @d. This value is zero-based
+     * (e.g. a value of three means four Segments in the contiguous series).
+     * */
+    int32_t r;
+    /* d: Element or Attribute Name
+     * specifies the Segment duration, in units of the value of the @timescale.
+     * */
+    int64_t d;
+};
+
+enum DASHTmplUrlType {
+    TMP_URL_TYPE_UNSPECIFIED,
+    TMP_URL_TYPE_NUMBER,
+    TMP_URL_TYPE_TIME,
+};
+
+/*
+ * 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;
+    enum DASHTmplUrlType tmp_url_type;
+    AVIOContext pb;
+    AVIOContext *input;
+    AVFormatContext *parent;
+    AVFormatContext *ctx;
+    AVPacket pkt;
+    int rep_idx;
+    int rep_count;
+    int stream_index;
+
+    enum AVMediaType type;
+    int64_t target_duration;
+
+    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 fragment_duration;
+    int64_t fragment_timescale;
+
+    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;
+    int fix_multiple_stsd_order;
+    int64_t cur_timestamp;
+};
+
+typedef struct DASHContext {
+    const AVClass *class;
+    char *base_url;
+    struct representation *cur_video;
+    struct representation *cur_audio;
+    uint64_t media_presentation_duration_sec;
+    uint64_t suggested_presentation_delay_sec;
+    uint64_t presentation_delay_sec;
+    uint64_t availability_start_time_sec;
+    uint64_t publish_time_sec;
+    uint64_t minimum_update_period_sec;
+    uint64_t time_shift_buffer_depth_sec;
+    uint64_t min_buffer_time_sec;
+    uint64_t period_duration_sec;
+    uint64_t period_start_sec;
+    int is_live;
+    AVIOInterruptCB *interrupt_callback;
+    char *user_agent;                    ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
+    char *cookies;                       ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
+    char *headers;                       ///< holds HTTP headers set as an AVOption to the HTTP protocol context
+    AVDictionary *avio_opts;
+} DASHContext;
+
+static uint64_t GetCurrentTimeInSec(void)
+{
+    return  av_gettime() / 1000000;
+}
+
+static uint64_t GetUTCDateTimeInSec(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, "GetUTCDateTimeInSec get a wrong time format\n");
+       // return 0; /* parser error */
+    }
+    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 GetDurationInSec(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;
+    uint32_t size = 0;
+    float value = 0;
+    uint8_t 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, "GetDurationInSec 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 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;
+}
+
+/*
+ * Used to reset a statically allocated AVPacket to a clean slate,
+ * containing no data.
+ */
+static void reset_packet(AVPacket *pkt)
+{
+    av_init_packet(pkt);
+    pkt->data = NULL;
+}
+
+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_packet_unref(&pls->pkt);
+    reset_packet(&pls->pkt);
+    av_freep(&pls->pb.buffer);
+    if (pls->input)
+        ff_format_io_close(pls->parent, &pls->input);
+    if (pls->ctx) {
+        pls->ctx->pb = NULL;
+        avformat_close_input(&pls->ctx);
+    }
+
+    av_free(pls->url_template);
+    av_free(pls);
+}
+
+static void update_options(char **dest, const char *name, void *src)
+{
+    av_freep(dest);
+    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
+    if (*dest)
+        av_freep(dest);
+}
+
+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, "http", NULL) && !av_strstart(proto_name, "file", NULL))
+        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;
+
+    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
+    if (ret >= 0) {
+        // update cookies on http response with setcookies.
+        void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
+        update_options(&c->cookies, "cookies", u);
+        av_dict_set(&opts, "cookies", c->cookies, 0);
+    }
+
+    av_dict_free(&tmp);
+
+    if (is_http)
+        *is_http = av_strstart(proto_name, "http", NULL);
+
+    return ret;
+
+}
+
+static char *strreplace(const char *str, const char *from, const char *to)
+{
+    /* Adjust each of the below values to suit your needs. */
+    /* Increment positions cache size initially by this number. */
+    size_t cache_sz_inc = 16;
+    /* Thereafter, each time capacity needs to be increased,
+     * multiply the increment by this factor. */
+    const size_t cache_sz_inc_factor = 3;
+    /* But never increment capacity by more than this number. */
+    const size_t cache_sz_inc_max = 1048576;
+
+    char *pret, *ret = NULL;
+    const char *pstr2, *pstr = str;
+    size_t i, count = 0;
+    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
+    size_t cache_sz = 0;
+    size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
+
+    /* Find all matches and cache their positions. */
+    while ((pstr2 = av_stristr(pstr, from))) {
+        count++;
+        /* Increase the cache size when necessary. */
+        if (cache_sz < count) {
+            cache_sz += cache_sz_inc;
+            pos_cache_tmp = av_realloc(pos_cache, sizeof(*pos_cache) * cache_sz);
+            if (!pos_cache_tmp) {
+                goto end_strreplace;
+            } else pos_cache = pos_cache_tmp;
+            cache_sz_inc *= cache_sz_inc_factor;
+            if (cache_sz_inc > cache_sz_inc_max) {
+                cache_sz_inc = cache_sz_inc_max;
+            }
+        }
+
+        pos_cache[count-1] = pstr2 - str;
+        pstr = pstr2 + fromlen;
+    }
+    orglen = pstr - str + strlen(pstr);
+    /* Allocate memory for the post-replacement string. */
+    if (count > 0) {
+        tolen = strlen(to);
+        retlen = orglen + (tolen - fromlen) * count;
+    } else {
+        retlen = orglen;
+    }
+    ret = av_malloc(retlen + 1);
+    if (!ret) {
+        goto end_strreplace;
+    }
+
+    if (!count) {
+        /* If no matches, then just duplicate the string. */
+        av_strlcpy(ret, str, retlen + 1);
+    } else {
+        /* Otherwise, duplicate the string whilst performing
+         * the replacements using the position cache. */
+        pret = ret;
+        memcpy(pret, str, pos_cache[0]);
+        pret += pos_cache[0];
+        for (i = 0; i < count; i++) {
+            memcpy(pret, to, tolen);
+            pret += tolen;
+            pstr = str + pos_cache[i] + fromlen;
+            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;
+            memcpy(pret, pstr, cpylen);
+            pret += cpylen;
+        }
+        ret[retlen] = '\0';
+    }
+
+end_strreplace:
+    /* Free the cache and return the post-replacement string,
+     * which will be NULL in the event of an error. */
+    av_free(pos_cache);
+    return ret;
+}
+
+static char *replace_template_str(const char *url, const char *marker)
+{
+    char *prefix = NULL;
+    char *start = 0;
+    char *end = NULL;
+    char *tmp_url = NULL;
+    char *new_url = NULL;
+    int marker_len;
+
+    prefix = av_strdup(marker);
+    marker_len = strlen(prefix) - 1;
+    prefix[marker_len] = '\0';
+
+    start = av_stristr(url, prefix);
+    if (!start)
+        goto finish;
+    end = strchr(start + 1, '$');
+    if (!end)
+        goto finish;
+
+    tmp_url = av_mallocz(MAX_URL_SIZE);
+    if (!tmp_url) {
+        return NULL;
+    }
+
+    av_strlcpy(tmp_url, url, start - url+ 1);
+    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end - start - marker_len + 1);
+    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
+
+finish:
+    av_free(prefix);
+    new_url = strreplace(tmp_url ? tmp_url : url, marker, "%"PRId64);
+    av_free(tmp_url);
+    return new_url;
+}
+
+static char *get_content_url(xmlNodePtr *baseurl_nodes, int n_baseurl_nodes, xmlChar *rep_id_val, xmlChar *rep_bandwidth_val, xmlChar *val)
+{
+    int i;
+    xmlChar *text;
+    char *url = NULL;
+    char *tmp_str = av_mallocz(MAX_URL_SIZE);
+    char *tmp_str_2 = NULL;
+
+    if (!tmp_str) {
+        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) {
+                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
+                if (!tmp_str_2) {
+                    av_free(tmp_str);
+                    return NULL;
+                }
+                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str, text);
+                av_free(tmp_str);
+                tmp_str = tmp_str_2;
+                xmlFree(text);
+            }
+        }
+    }
+    if (val)
+        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
+    if (rep_id_val) {
+        url = strreplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
+        av_free(tmp_str);
+        tmp_str = url;
+    }
+    if (rep_bandwidth_val && tmp_str)
+        url = strreplace(tmp_str, "$Bandwidth$", (const char*)rep_bandwidth_val);
+    if (tmp_str != url)
+        av_free(tmp_str);
+    return url;
+}
+
+static xmlChar *get_val_from_nodes_tab(xmlNodePtr *nodes, const int n_nodes, const xmlChar *attrname)
+{
+    int i;
+    xmlChar *val;
+
+    for (i = 0; i < n_nodes; ++i) {
+        if (nodes[i]) {
+            val = xmlGetProp(nodes[i], attrname);
+            if (val)
+                return val;
+        }
+    }
+
+    return NULL;
+}
+
+static xmlNodePtr findChildNodeByName(xmlNodePtr rootnode, const xmlChar *nodename)
+{
+    xmlNodePtr node = rootnode;
+    if (!node) {
+        return NULL;
+    }
+
+    node = xmlFirstElementChild(node);
+    while (node) {
+        if (!xmlStrcmp(node->name, nodename)) {
+            return node;
+        }
+        node = xmlNextElementSibling(node);
+    }
+    return NULL;
+}
+
+static enum AVMediaType get_content_type(xmlNodePtr node)
+{
+    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
+    int i = 0;
+    const char *attr;
+
+    if (node) {
+        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
+            attr = (i) ? "mimeType" : "contentType";
+            xmlChar *val = xmlGetProp(node, attr);
+            if (val) {
+                if (av_stristr((const char *)val, "video")) {
+                    type = AVMEDIA_TYPE_VIDEO;
+                } else if (av_stristr((const char *)val, "audio")) {
+                    type = AVMEDIA_TYPE_AUDIO;
+                }
+                xmlFree(val);
+            }
+            i++;
+        }
+    }
+    return type;
+}
+
+static int parse_manifest_segmenturlnode(AVFormatContext *s, struct representation *rep,
+                                          xmlNodePtr fragmenturl_node,
+                                          xmlNodePtr *baseurl_nodes,
+                                          xmlChar *rep_id_val,
+                                          xmlChar *rep_bandwidth_val)
+{
+    xmlChar *initialization_val = NULL;
+    xmlChar *media_val = NULL;
+
+    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar *)"Initialization")) {
+        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
+        if (initialization_val) {
+            rep->init_section = av_mallocz(sizeof(struct fragment));
+            if (!rep->init_section) {
+                xmlFree(initialization_val);
+                return AVERROR(ENOMEM);
+            }
+            rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
+            rep->init_section->size = -1;
+            xmlFree(initialization_val);
+        }
+    } else if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar *)"SegmentURL")) {
+        media_val = xmlGetProp(fragmenturl_node, "media");
+        if (media_val) {
+            struct fragment *seg = av_mallocz(sizeof(struct fragment));
+            if (!seg) {
+                xmlFree(media_val);
+                return AVERROR(ENOMEM);
+            }
+            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
+            seg->size = -1;
+            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
+            xmlFree(media_val);
+        }
+    }
+
+    return 0;
+}
+
+static int parse_manifest_segmenttimeline(AVFormatContext *s, struct representation *rep,
+                                           xmlNodePtr fragment_timeline_node)
+{
+    xmlAttrPtr attr = NULL;
+    xmlChar *val  = NULL;
+
+    if (!xmlStrcmp(fragment_timeline_node->name, (const xmlChar *)"S")) {
+        struct timeline *tml = av_mallocz(sizeof(struct timeline));
+        if (!tml) {
+            return AVERROR(ENOMEM);
+        }
+        attr = fragment_timeline_node->properties;
+        while (attr) {
+            val = xmlGetProp(fragment_timeline_node, attr->name);
+
+            if (!val) {
+                av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline attr->name = %s val is NULL\n", attr->name);
+                continue;
+            }
+
+            if (!xmlStrcmp(attr->name, (const xmlChar *)"t")) {
+                tml->t = (int64_t)atoll((const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
+                tml->r =(int32_t) atoi((const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
+                tml->d = (int64_t)atoll((const char *)val);
+                rep->fragment_duration = (int64_t) atoll((const char *)val);
+            }
+            attr = attr->next;
+            xmlFree(val);
+        }
+        dynarray_add(&rep->timelines, &rep->n_timelines, tml);
+    }
+
+    return 0;
+}
+
+static int parse_manifest_representation(AVFormatContext *s, const char *url,
+                                          xmlNodePtr node,
+                                          xmlNodePtr adaptionset_node,
+                                          xmlNodePtr mpd_baseurl_node,
+                                          xmlNodePtr period_baseurl_node,
+                                          xmlNodePtr fragment_template_node,
+                                          xmlNodePtr content_component_node,
+                                          xmlNodePtr adaptionset_baseurl_node)
+{
+    int32_t ret = 0;
+    int32_t audio_rep_idx = 0;
+    int32_t video_rep_idx = 0;
+    char *temp_string = NULL;
+    DASHContext *c = s->priv_data;
+    struct representation *rep = NULL;
+    struct fragment *seg = NULL;
+    xmlNodePtr representation_segmenttemplate_node = NULL;
+    xmlNodePtr representation_baseurl_node = NULL;
+    xmlNodePtr representation_segmentlist_node = NULL;
+    xmlNodePtr fragment_timeline_node = NULL;
+    xmlNodePtr fragment_templates_tab[2];
+    xmlChar *duration_val = NULL;
+    xmlChar *startnumber_val = NULL;
+    xmlChar *timescale_val = NULL;
+    xmlChar *initialization_val = NULL;
+    xmlChar *media_val = NULL;
+    xmlNodePtr baseurl_nodes[4];
+    xmlNodePtr representation_node = node;
+    xmlChar *rep_id_val = xmlGetProp(representation_node, "id");
+    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node, "bandwidth");
+    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
+
+    // try get information from representation
+    if (type == AVMEDIA_TYPE_UNKNOWN)
+        type = get_content_type(representation_node);
+    // try get information from contentComponen
+    if (type == AVMEDIA_TYPE_UNKNOWN)
+        type = get_content_type(content_component_node);
+    // try get information from adaption set
+    if (type == AVMEDIA_TYPE_UNKNOWN)
+        type = get_content_type(adaptionset_node);
+    if (type == AVMEDIA_TYPE_UNKNOWN) {
+        av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported representation type\n", url);
+    } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
+        // convert selected representation to our internal struct
+        rep = av_mallocz(sizeof(struct representation));
+        if (!rep) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
+        representation_segmenttemplate_node = findChildNodeByName(representation_node, "SegmentTemplate");
+        representation_baseurl_node = findChildNodeByName(representation_node, "BaseURL");
+        representation_segmentlist_node = findChildNodeByName(representation_node, "SegmentList");
+        reset_packet(&rep->pkt);
+
+        baseurl_nodes[0] = mpd_baseurl_node;
+        baseurl_nodes[1] = period_baseurl_node;
+        baseurl_nodes[2] = adaptionset_baseurl_node;
+        baseurl_nodes[3] = representation_baseurl_node;
+
+        if (representation_segmenttemplate_node || fragment_template_node) {
+            fragment_timeline_node = NULL;
+            fragment_templates_tab[0] = representation_segmenttemplate_node;
+            fragment_templates_tab[1] = fragment_template_node;
+
+            duration_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "duration");
+            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "startNumber");
+            timescale_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "timescale");
+            initialization_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "initialization");
+            media_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "media");
+
+            if (initialization_val) {
+                rep->init_section = av_mallocz(sizeof(struct fragment));
+                if (!rep->init_section) {
+                    av_free(rep);
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+                rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
+                rep->init_section->size = -1;
+                xmlFree(initialization_val);
+            }
+
+            if (media_val) {
+                temp_string = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
+                if (temp_string) {
+                    if (av_stristr(temp_string, "$Number")) {
+                        rep->url_template = av_strdup(temp_string);
+                        /* A URL template is provided from which clients build a chunk list
+                         * where the chunk URLs include chunk numbers (like index numbers).
+                         */
+                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
+                    } else if (av_stristr(temp_string, "$Time")) {
+                        rep->url_template = av_strdup(temp_string);
+                        /* A URL template is provided from which clients build a chunk list
+                         * where the chunk URLs include chunk start times.
+                         */
+                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
+                    } else {
+                        rep->url_template = temp_string;
+                        temp_string = NULL;
+                    }
+                    av_free(temp_string);
+                }
+                xmlFree(media_val);
+            }
+            if (duration_val) {
+                rep->fragment_duration = (int64_t) atoll((const char *)duration_val);
+                xmlFree(duration_val);
+            }
+            if (timescale_val) {
+                rep->fragment_timescale = (int64_t) atoll((const char *)timescale_val);
+                xmlFree(timescale_val);
+            }
+            if (startnumber_val) {
+                rep->first_seq_no = (int64_t) atoll((const char *)startnumber_val);
+                xmlFree(startnumber_val);
+            }
+
+            fragment_timeline_node = findChildNodeByName(representation_segmenttemplate_node, "SegmentTimeline");
+
+            if (!fragment_timeline_node)
+                fragment_timeline_node = findChildNodeByName(fragment_template_node, "SegmentTimeline");
+            if (fragment_timeline_node) {
+                fragment_timeline_node = xmlFirstElementChild(fragment_timeline_node);
+                while (fragment_timeline_node) {
+                    ret = parse_manifest_segmenttimeline(s, rep, fragment_timeline_node);
+                    if (ret < 0) {
+                        return ret;
+                    }
+                    fragment_timeline_node = xmlNextElementSibling(fragment_timeline_node);
+                }
+            }
+        } else if (representation_baseurl_node && !representation_segmentlist_node) {
+            seg = av_mallocz(sizeof(struct fragment));
+            if (!seg) {
+                ret = AVERROR(ENOMEM);
+                goto end;
+            }
+            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);
+            seg->size = -1;
+            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
+        } else if (!representation_baseurl_node && representation_segmentlist_node) {
+            // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
+            // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
+            xmlNodePtr fragmenturl_node = NULL;
+            duration_val = xmlGetProp(representation_segmentlist_node, "duration");
+            timescale_val = xmlGetProp(representation_segmentlist_node, "timescale");
+            if (duration_val) {
+                rep->fragment_duration = (int64_t) atoll((const char *)duration_val);
+                xmlFree(duration_val);
+            }
+            if (timescale_val) {
+                rep->fragment_timescale = (int64_t) atoll((const char *)timescale_val);
+                xmlFree(timescale_val);
+            }
+            fragmenturl_node = xmlFirstElementChild(representation_segmentlist_node);
+            while (fragmenturl_node) {
+                ret = parse_manifest_segmenturlnode(s, rep, fragmenturl_node, baseurl_nodes, rep_id_val, rep_bandwidth_val);
+                if (ret < 0) {
+                    return ret;
+                }
+                fragmenturl_node = xmlNextElementSibling(fragmenturl_node);
+            }
+        } else {
+            free_representation(rep);
+            rep = NULL;
+            av_log(s, AV_LOG_ERROR, "Unknown format of Representation node id[%s] \n", (const char *)rep_id_val);
+        }
+
+        if (rep) {
+            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
+                rep->fragment_timescale = 1;
+            if (type == AVMEDIA_TYPE_VIDEO) {
+                rep->rep_idx = video_rep_idx;
+                c->cur_video = rep;
+            } else {
+                rep->rep_idx = audio_rep_idx;
+                c->cur_audio = rep;
+            }
+        }
+    }
+
+    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
+    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
+
+end:
+    if (rep_id_val)
+        xmlFree(rep_id_val);
+    if (rep_bandwidth_val)
+        xmlFree(rep_bandwidth_val);
+
+    return ret;
+}
+
+static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, xmlNodePtr period_baseurl_node)
+{
+    int ret = 0;
+    xmlNodePtr fragment_template_node = NULL;
+    xmlNodePtr content_component_node = NULL;
+    xmlNodePtr adaptionset_baseurl_node = NULL;
+    xmlNodePtr node = NULL;
+
+    node = xmlFirstElementChild(adaptionset_node);
+    while (node) {
+        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate")) {
+            fragment_template_node = node;
+        } else if (!xmlStrcmp(node->name, (const xmlChar *)"ContentComponent")) {
+            content_component_node = node;
+        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
+            adaptionset_baseurl_node = node;
+        } else if (!xmlStrcmp(node->name, (const xmlChar *)"Representation")) {
+            ret = parse_manifest_representation(s, url, node,
+                                                 adaptionset_node,
+                                                 mpd_baseurl_node,
+                                                 period_baseurl_node,
+                                                 fragment_template_node,
+                                                 content_component_node,
+                                                 adaptionset_baseurl_node);
+            if (ret < 0) {
+                return ret;
+            }
+        }
+        node = xmlNextElementSibling(node);
+    }
+    return 0;
+}
+
+
+static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+{
+    DASHContext *c = s->priv_data;
+    int ret = 0;
+    int close_in = 0;
+    uint8_t *new_url = NULL;
+    int64_t filesize = 0;
+    char *buffer = NULL;
+    AVDictionary *opts = NULL;
+    xmlDoc *doc = NULL;
+    xmlNodePtr root_element = NULL;
+    xmlNodePtr node = NULL;
+    xmlNodePtr period_node = NULL;
+    xmlNodePtr mpd_baseurl_node = NULL;
+    xmlNodePtr period_baseurl_node = NULL;
+    xmlNodePtr adaptionset_node = NULL;
+    xmlAttrPtr attr = NULL;
+    xmlChar *val  = NULL;
+    uint32_t perdiod_duration_sec = 0;
+    uint32_t perdiod_start_sec = 0;
+    int32_t audio_rep_idx = 0;
+    int32_t video_rep_idx = 0;
+
+    if (!in) {
+        close_in = 1;
+        /* This is XML manifest there is no need to set range header */
+        av_dict_set(&opts, "seekable", "0", 0);
+        // broker prior HTTP options that should be consistent across requests
+        av_dict_set(&opts, "user-agent", c->user_agent, 0);
+        av_dict_set(&opts, "cookies", c->cookies, 0);
+        av_dict_set(&opts, "headers", c->headers, 0);
+
+        ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
+        av_dict_free(&opts);
+        if (ret < 0)
+            return ret;
+    }
+
+    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) {
+        c->base_url = av_strdup(new_url);
+    } else {
+        c->base_url = av_strdup(url);
+    }
+
+    filesize = avio_size(in);
+    if (filesize <= 0){
+        filesize = 8 * 1024;
+    }
+
+    buffer = av_mallocz(filesize);
+    if (!buffer) {
+        return AVERROR(ENOMEM);
+    }
+
+    filesize = avio_read(in, buffer, filesize);
+    if (filesize > 0) {
+        LIBXML_TEST_VERSION
+
+        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
+        root_element = xmlDocGetRootElement(doc);
+        node = root_element;
+
+        if (!node) {
+            ret = AVERROR_INVALIDDATA;
+            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing root node\n", url);
+            goto cleanup;
+        }
+
+        if (node->type != XML_ELEMENT_NODE ||
+            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
+            ret = AVERROR_INVALIDDATA;
+            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root node name[%s] type[%d]\n", url, node->name, (int)node->type);
+            goto cleanup;
+        }
+
+        val = xmlGetProp(node, "type");
+        if (!val) {
+            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing type attrib\n", url);
+            ret = AVERROR_INVALIDDATA;
+            goto cleanup;
+        }
+        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
+            c->is_live = 1;
+        xmlFree(val);
+
+        attr = node->properties;
+        while (attr) {
+            val = xmlGetProp(node, attr->name);
+
+            if (!xmlStrcmp(attr->name, (const xmlChar *)"availabilityStartTime")) {
+                c->availability_start_time_sec = GetUTCDateTimeInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
+                c->publish_time_sec = GetUTCDateTimeInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
+                c->minimum_update_period_sec = GetDurationInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
+                c->time_shift_buffer_depth_sec = GetDurationInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
+                c->min_buffer_time_sec = GetDurationInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
+                c->suggested_presentation_delay_sec = GetDurationInSec(s, (const char *)val);
+            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
+                c->media_presentation_duration_sec = GetDurationInSec(s, (const char *)val);
+            }
+            attr = attr->next;
+            xmlFree(val);
+        }
+
+        mpd_baseurl_node = findChildNodeByName(node, "BaseURL");
+
+        // at now we can handle only one period, with the longest duration
+        node = xmlFirstElementChild(node);
+        while (node) {
+            if (!xmlStrcmp(node->name, (const xmlChar *)"Period")) {
+                perdiod_duration_sec = 0;
+                perdiod_start_sec = 0;
+                attr = node->properties;
+                while (attr) {
+                    val = xmlGetProp(node, attr->name);
+                    if (!xmlStrcmp(attr->name, (const xmlChar *)"duration")) {
+                        perdiod_duration_sec = GetDurationInSec(s, (const char *)val);
+                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
+                        perdiod_start_sec = GetDurationInSec(s, (const char *)val);
+                    }
+                    attr = attr->next;
+                    xmlFree(val);
+                }
+                if ((perdiod_duration_sec) >= (c->period_duration_sec)) {
+                    period_node = node;
+                    c->period_duration_sec = perdiod_duration_sec;
+                    c->period_start_sec = perdiod_start_sec;
+                    if (c->period_start_sec > 0)
+                        c->media_presentation_duration_sec = c->period_duration_sec;
+                }
+            }
+            node = xmlNextElementSibling(node);
+        }
+        if (!period_node) {
+            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing Period node\n", url);
+            ret = AVERROR_INVALIDDATA;
+            goto cleanup;
+        }
+        // explore AdaptationSet
+        adaptionset_node = xmlFirstElementChild(period_node);
+        while (adaptionset_node) {
+            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"BaseURL")) {
+                period_baseurl_node = adaptionset_node;
+            } else if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"AdaptationSet")) {
+                parse_manifest_adaptationset(s, url, adaptionset_node, mpd_baseurl_node, period_baseurl_node);
+            }
+            adaptionset_node = xmlNextElementSibling(adaptionset_node);
+        }
+        if (c->cur_video) {
+            c->cur_video->rep_count = video_rep_idx;
+            c->cur_video->fix_multiple_stsd_order = 1;
+            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n", (int)c->cur_video->rep_idx);
+            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n", (int)video_rep_idx);
+        }
+        if (c->cur_audio) {
+            c->cur_audio->rep_count = audio_rep_idx;
+        }
+
+cleanup:
+        /*free the document */
+        xmlFreeDoc(doc);
+        xmlCleanupParser();
+    } else {
+        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
+        ret = AVERROR_INVALIDDATA;
+    }
+
+    av_free(new_url);
+    av_free(buffer);
+    if (close_in) {
+        avio_close(in);
+    }
+    return ret;
+}
+
+static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
+{
+    DASHContext *c = pls->parent->priv_data;
+    int64_t num = 0;
+
+    if (c->is_live) {
+        if (!pls->fragment_duration) {
+            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
+            return pls->first_seq_no;
+        }
+        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec) - c->presentation_delay_sec) * pls->fragment_timescale) / pls->fragment_duration;
+    } else {
+        num = pls->first_seq_no;
+    }
+    return num;
+}
+
+static int64_t calc_min_seg_no(AVFormatContext *s, struct representation *pls)
+{
+    DASHContext *c = pls->parent->priv_data;
+    int64_t num = 0;
+
+    if (c->is_live) {
+        if (!pls->fragment_duration) {
+            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
+            return pls->first_seq_no;
+        }
+        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) * pls->fragment_timescale)  / pls->fragment_duration;
+    } else {
+        num = pls->first_seq_no;
+    }
+    return num;
+}
+
+static int64_t calc_max_seg_no(AVFormatContext *s, struct representation *pls)
+{
+    DASHContext *c = pls->parent->priv_data;
+    int64_t num = 0;
+    int64_t i = 0;
+
+    if (c->is_live) {
+        if (!pls->fragment_duration) {
+            av_log(s, AV_LOG_ERROR, "Get not get fragment duration value in live mode\n");
+            return pls->first_seq_no;
+        }
+        num = pls->first_seq_no + (((GetCurrentTimeInSec() - c->availability_start_time_sec)) * pls->fragment_timescale)  / pls->fragment_duration;
+    } else {
+        if (pls->n_fragments) {
+            num = pls->first_seq_no + pls->n_fragments - 1;
+        } else if (pls->n_timelines) {
+            num = pls->first_seq_no + pls->n_timelines - 1;
+            for (i = 0; i < pls->n_timelines; ++i) {
+                num += pls->timelines[i]->r;
+            }
+        } else {
+            num = pls->first_seq_no + (c->media_presentation_duration_sec * pls->fragment_timescale) / pls->fragment_duration;
+        }
+    }
+
+    return num;
+}
+
+static int64_t get_fragment_start_time(struct representation *pls, int64_t cur_seq_no)
+{
+    int64_t i = 0;
+    int64_t j = 0;
+    int64_t num = 0;
+    int64_t startTime = 0;
+
+    if (pls->n_timelines) {
+        for (i = 0; i < pls->n_timelines; ++i) {
+            if (pls->timelines[i]->t > 0) {
+                startTime = pls->timelines[i]->t;
+            }
+            if (num == cur_seq_no)
+                goto finish;
+            startTime += pls->timelines[i]->d;
+            for (j = 0; j < pls->timelines[i]->r; ++j) {
+                num++;
+                if (num == cur_seq_no)
+                    goto finish;
+                startTime += pls->timelines[i]->d;
+            }
+            num++;
+        }
+    }
+
+finish:
+    return startTime;
+}
+
+static struct fragment *get_current_fragment(struct representation *pls)
+{
+    int64_t tmp_val = 0;
+    int64_t min_seq_no = 0;
+    int64_t max_seq_no = 0;
+    char buffer[1024];
+    char *tmp_str = NULL;
+    struct fragment *seg = NULL;
+    struct fragment *seg_ptr = NULL;
+    DASHContext *c = pls->parent->priv_data;
+
+    if (pls->n_fragments > 0) {
+        if (pls->cur_seq_no < pls->n_fragments) {
+            seg_ptr = pls->fragments[pls->cur_seq_no];
+            seg = av_mallocz(sizeof(struct fragment));
+            if (!seg) {
+                return NULL;
+            }
+            seg->url = av_strdup(seg_ptr->url);
+            if (!seg->url) {
+                av_free(seg);
+                return NULL;
+            }
+            seg->size = seg_ptr->size;
+            seg->url_offset = seg_ptr->url_offset;
+            return seg;
+        }
+    }
+    if (c->is_live) {
+        while (1) {
+            min_seq_no = calc_min_seg_no(pls->parent, pls);
+            max_seq_no = calc_max_seg_no(pls->parent, pls);
+
+            if (pls->cur_seq_no <= min_seq_no) {
+                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment: cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
+                       (int64_t)pls->cur_seq_no, min_seq_no, max_seq_no, (int)pls->rep_idx);
+                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
+            } else if (pls->cur_seq_no > max_seq_no) {
+                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment: min[%"PRId64"] max[%"PRId64"], playlist %d\n",
+                       min_seq_no, max_seq_no, (int)pls->rep_idx);
+                av_usleep(1000);
+                continue;
+            }
+            break;
+        }
+        seg = av_mallocz(sizeof(struct fragment));
+        if (!seg) {
+            return NULL;
+        }
+    } else if (pls->cur_seq_no <= pls->last_seq_no) {
+        seg = av_mallocz(sizeof(struct fragment));
+        if (!seg) {
+            return NULL;
+        }
+    }
+    if (seg) {
+        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
+            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
+            if (av_get_frame_filename(buffer, sizeof(buffer), pls->url_template, (int64_t)tmp_val) < 0) {
+                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment filename template %s\n", pls->url_template);
+                return NULL;
+            }
+
+            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
+                tmp_str = replace_template_str(buffer, "$Number$");
+            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME){
+                tmp_str = replace_template_str(buffer, "$Time$");
+            } else {
+                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type [%d]\n", pls->tmp_url_type);
+                return NULL;
+            }
+
+            seg->url = av_strdup(tmp_str);
+            if (!seg->url) {
+                av_free(tmp_str);
+                return NULL;
+            }
+        } else {
+            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to resolve template url '%s'\n", pls->url_template);
+            seg->url = av_strdup(pls->url_template);
+            if (!seg->url) {
+                return NULL;
+            }
+        }
+        seg->size = -1;
+    }
+
+    av_free(tmp_str);
+    return seg;
+}
+
+enum ReadFromURLMode {
+    READ_NORMAL,
+    READ_COMPLETE,
+};
+
+static int read_from_url(struct representation *pls, struct fragment *seg,
+                         uint8_t *buf, int buf_size,
+                         enum ReadFromURLMode mode)
+{
+    int ret;
+
+    /* limit read if the fragment was only a part of a file */
+    if (seg->size >= 0)
+        buf_size = FFMIN(buf_size, pls->cur_seg_size - pls->cur_seg_offset);
+
+    if (mode == READ_COMPLETE) {
+        ret = avio_read(pls->input, buf, buf_size);
+        if (ret < buf_size) {
+            av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment.\n");
+        }
+    } else {
+        ret = avio_read(pls->input, buf, buf_size);
+    }
+    if (ret > 0)
+        pls->cur_seg_offset += ret;
+
+    return ret;
+}
+
+static int open_input(DASHContext *c, struct representation *pls, struct fragment *seg)
+{
+    AVDictionary *opts = NULL;
+    char url[MAX_URL_SIZE];
+    int ret;
+
+    // broker prior HTTP options that should be consistent across requests
+    av_dict_set(&opts, "user-agent", c->user_agent, 0);
+    av_dict_set(&opts, "cookies", c->cookies, 0);
+    av_dict_set(&opts, "headers", c->headers, 0);
+    if (c->is_live) {
+        av_dict_set(&opts, "seekable", "0", 0);
+    }
+
+    if (seg->size >= 0) {
+        /* try to restrict the HTTP request to the part we want
+         * (if this is in fact a HTTP request) */
+        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
+        av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0);
+    }
+
+    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
+    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s', offset %"PRId64", playlist %d\n",
+           url, seg->url_offset, pls->rep_idx);
+    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts, NULL);
+    if (ret < 0) {
+        goto cleanup;
+    }
+
+    /* Seek to the requested position. If this was a HTTP request, the offset
+     * should already be where want it to, but this allows e.g. local testing
+     * without a HTTP server. */
+    if (!ret && seg->url_offset) {
+        int64_t seekret = avio_seek(pls->input, seg->url_offset, SEEK_SET);
+        if (seekret < 0) {
+            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
+            ret = (int) seekret;
+            ff_format_io_close(pls->parent, &pls->input);
+        }
+    }
+
+cleanup:
+    av_dict_free(&opts);
+    pls->cur_seg_offset = 0;
+    pls->cur_seg_size = seg->size;
+    return ret;
+}
+
+static int update_init_section(struct representation *pls)
+{
+    static const int max_init_section_size = 1024*1024;
+    DASHContext *c = pls->parent->priv_data;
+    int64_t sec_size = 0;
+    int64_t urlsize = 0;
+    int ret = 0;
+
+    /* read init section only once per representation */
+    if (!pls->init_section || pls->init_sec_buf) {
+        return 0;
+    }
+
+    ret = open_input(c, pls, pls->init_section);
+    if (ret < 0) {
+        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an initialization section in playlist %d\n", pls->rep_idx);
+        return ret;
+    }
+
+    if (pls->init_section->size >= 0) {
+        sec_size = pls->init_section->size;
+    } else if ((urlsize = avio_size(pls->input)) >= 0) {
+        sec_size = urlsize;
+    } else {
+        sec_size = max_init_section_size;
+    }
+    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization section of size %"PRId64"\n", sec_size);
+    sec_size = FFMIN(sec_size, max_init_section_size);
+    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
+    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf, pls->init_sec_buf_size, READ_COMPLETE);
+    ff_format_io_close(pls->parent, &pls->input);
+    if (ret < 0)
+        return ret;
+
+    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
+        uint8_t **stsd_entries = NULL;
+        int *stsd_entries_size = NULL;
+        int i = 4;
+
+        while (i <= (ret - 4)) {
+            // find start stsd atom
+            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
+                /* 1B version
+                 * 3B flags
+                 * 4B num of entries */
+                int stsd_first_offset = i + 8;
+                int stsd_offset = 0;
+                int j = 0;
+                uint32_t stsd_count = AV_RB32(pls->init_sec_buf + stsd_first_offset);
+                stsd_first_offset += 4;
+                if (stsd_count != pls->rep_count) {
+                    i++;
+                    continue;
+                }
+                // find all stsd entries
+                stsd_entries = av_mallocz_array(stsd_count, sizeof(*stsd_entries));
+                stsd_entries_size = av_mallocz_array(stsd_count, sizeof(*stsd_entries_size));
+                for (j = 0; j < stsd_count; ++j) {
+                    /* 4B - size
+                     * 4B - format */
+                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf + stsd_first_offset + stsd_offset);
+                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
+                    memcpy(stsd_entries[j], pls->init_sec_buf + stsd_first_offset + stsd_offset, stsd_entries_size[j]);
+                    stsd_offset += stsd_entries_size[j];
+                }
+                // reorder stsd entries
+                // as first put stsd entry for current representation
+                j = pls->rep_idx;
+                stsd_offset = stsd_first_offset;
+                memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
+                stsd_offset += stsd_entries_size[j];
+                for (j = 0; j < stsd_count; ++j) {
+                    if (j != pls->rep_idx) {
+                        memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
+                        stsd_offset += stsd_entries_size[j];
+                    }
+                    av_free(stsd_entries[j]);
+                }
+                av_freep(&stsd_entries);
+                av_freep(&stsd_entries_size);
+                break;
+            }
+            i++;
+        }
+    }
+
+    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section size[%d]\n", pls, (int)ret);
+    pls->init_sec_data_len = ret;
+    pls->init_sec_buf_read_offset = 0;
+
+    return 0;
+}
+
+static int64_t seek_data(void *opaque, int64_t offset, int whence)
+{
+    struct representation *v = opaque;
+    if (v->n_fragments && !v->init_sec_data_len) {
+        return avio_seek(v->input, offset, whence);
+    }
+
+    return AVERROR(ENOSYS);
+}
+
+static int read_data(void *opaque, uint8_t *buf, int buf_size)
+{
+    int ret = 0;
+    struct representation *v = opaque;
+    DASHContext *c = v->parent->priv_data;
+
+restart:
+    if (!v->input) {
+        free_fragment(&v->cur_seg);
+        v->cur_seg = get_current_fragment(v);
+        if (!v->cur_seg) {
+            ret = AVERROR_EOF;
+            goto end;
+        }
+
+        /* load/update Media Initialization Section, if any */
+        ret = update_init_section(v);
+        if (ret)
+            goto end;
+
+        ret = open_input(c, v, v->cur_seg);
+        if (ret < 0) {
+            if (ff_check_interrupt(c->interrupt_callback)) {
+                goto end;
+                ret = AVERROR_EXIT;
+            }
+            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment of playlist %d\n", v->rep_idx);
+            v->cur_seq_no++;
+            goto restart;
+        }
+    }
+
+    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
+        /* Push init section out first before first actual fragment */
+        int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
+        memcpy(buf, v->init_sec_buf, copy_size);
+        v->init_sec_buf_read_offset += copy_size;
+        ret = copy_size;
+        goto end;
+    }
+
+    /* check the v->cur_seg, if it is null, get current and double check if the new v->cur_seg*/
+    if (!v->cur_seg) {
+        v->cur_seg = get_current_fragment(v);
+    }
+    if (!v->cur_seg) {
+        ret = AVERROR_EOF;
+        goto end;
+    }
+    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
+    if (ret > 0)
+        goto end;
+    ff_format_io_close(v->parent, &v->input);
+    v->cur_seq_no++;
+    goto restart;
+
+end:
+    return ret;
+}
+
+static int save_avio_options(AVFormatContext *s)
+{
+    DASHContext *c = s->priv_data;
+    const char *opts[] = { "headers", "user_agent", "user-agent", "cookies", NULL }, **opt = opts;
+    uint8_t *buf = NULL;
+    int ret = 0;
+
+    while (*opt) {
+        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
+            if (buf) {
+                ret = av_dict_set(&c->avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL);
+                if (ret < 0)
+                    return ret;
+            }
+        }
+        opt++;
+    }
+
+    return ret;
+}
+
+static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
+                          int flags, AVDictionary **opts)
+{
+    av_log(s, AV_LOG_ERROR,
+           "A HDS playlist item '%s' referred to an external file '%s'. "
+           "Opening this file was forbidden for security reasons\n",
+           s->filename, url);
+    return AVERROR(EPERM);
+}
+
+static int reopen_demux_for_component(AVFormatContext *s, struct representation *pls)
+{
+    DASHContext *c = s->priv_data;
+    AVInputFormat *in_fmt = NULL;
+    AVDictionary  *in_fmt_opts = NULL;
+    uint8_t *avio_ctx_buffer  = NULL;
+    int ret = 0;
+
+    if (pls->ctx) {
+        /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
+        av_freep(&pls->pb.buffer);
+        memset(&pls->pb, 0x00, sizeof(AVIOContext));
+        pls->ctx->pb = NULL;
+        avformat_close_input(&pls->ctx);
+        pls->ctx = NULL;
+    }
+    if (!(pls->ctx = avformat_alloc_context())) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
+    if (!avio_ctx_buffer ){
+        ret = AVERROR(ENOMEM);
+        avformat_free_context(pls->ctx);
+        pls->ctx = NULL;
+        goto fail;
+    }
+    if (c->is_live) {
+        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
+    } else {
+        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
+    }
+    pls->pb.seekable = 0;
+
+    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+        goto fail;
+
+    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
+    pls->ctx->probesize = 1024 * 4;
+    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
+    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR, "Error when loading first fragment, playlist %d\n", (int)pls->rep_idx);
+        avformat_free_context(pls->ctx);
+        pls->ctx = NULL;
+        goto fail;
+    }
+
+    pls->ctx->pb = &pls->pb;
+    pls->ctx->io_open  = nested_io_open;
+
+    // provide additional information from mpd if available
+    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
+    av_dict_free(&in_fmt_opts);
+    if (ret < 0)
+        goto fail;
+    if (pls->n_fragments) {
+        ret = avformat_find_stream_info(pls->ctx, NULL);
+        if (ret < 0)
+            goto fail;
+    }
+
+fail:
+    return ret;
+}
+
+static int open_demux_for_component(AVFormatContext *s, struct representation *pls)
+{
+    int ret = 0;
+    int i;
+
+    pls->parent = s;
+    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
+    pls->last_seq_no = calc_max_seg_no(s, pls);
+
+    ret = reopen_demux_for_component(s, pls);
+    if (ret < 0) {
+        goto fail;
+    }
+    for (i = 0; i < pls->ctx->nb_streams; i++) {
+        AVStream *st = avformat_new_stream(s, NULL);
+        AVStream *ist = pls->ctx->streams[i];
+        if (!st) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        st->id = i;
+        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->codecpar);
+        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
+    }
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int dash_read_header(AVFormatContext *s)
+{
+    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
+    DASHContext *c = s->priv_data;
+    int ret = 0;
+    int stream_index = 0;
+
+    c->interrupt_callback = &s->interrupt_callback;
+    // if the URL context is good, read important options we must broker later
+    if (u) {
+        update_options(&c->user_agent, "user-agent", u);
+        update_options(&c->cookies, "cookies", u);
+        update_options(&c->headers, "headers", u);
+    }
+
+    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
+        goto fail;
+
+    if ((ret = save_avio_options(s)) < 0)
+        goto fail;
+
+    /* If this isn't a live stream, fill the total duration of the
+     * stream. */
+    if (!c->is_live) {
+        s->duration = (int64_t) c->media_presentation_duration_sec * AV_TIME_BASE;
+    }
+
+    /* Open the demuxer for curent video and current audio components if available */
+    if (!ret && c->cur_video) {
+        ret = open_demux_for_component(s, c->cur_video);
+        if (!ret) {
+            c->cur_video->stream_index = stream_index;
+            ++stream_index;
+        } else {
+            free_representation(c->cur_video);
+            c->cur_video = NULL;
+        }
+    }
+
+    if (!ret && c->cur_audio) {
+        ret = open_demux_for_component(s, c->cur_audio);
+        if (!ret) {
+            c->cur_audio->stream_index = stream_index;
+            ++stream_index;
+        } else {
+            free_representation(c->cur_audio);
+            c->cur_audio = NULL;
+        }
+    }
+
+    if (!stream_index) {
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
+    /* Create a program */
+    if (!ret) {
+        AVProgram *program;
+        program = av_new_program(s, 0);
+        if (!program) {
+            goto fail;
+        }
+
+        if (c->cur_video) {
+            av_program_add_stream_index(s, 0, c->cur_video->stream_index);
+        }
+
+        if (c->cur_audio) {
+            av_program_add_stream_index(s, 0, c->cur_audio->stream_index);
+        }
+    }
+
+    return 0;
+fail:
+    return ret;
+}
+
+static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    DASHContext *c = s->priv_data;
+    int ret = 0;
+    struct representation *cur = NULL;
+
+    if (!c->cur_audio && !c->cur_video) {
+        return AVERROR_INVALIDDATA;
+    }
+    if (c->cur_audio && !c->cur_video) {
+        cur = c->cur_audio;
+    } else if (!c->cur_audio && c->cur_video) {
+        cur = c->cur_video;
+    } else if (c->cur_video->cur_timestamp < c->cur_audio->cur_timestamp) {
+        cur = c->cur_video;
+    } else {
+        cur = c->cur_audio;
+    }
+
+    if (cur->ctx) {
+        ret = av_read_frame(cur->ctx, &cur->pkt);
+        if (ret < 0) {
+            av_packet_unref(&cur->pkt);
+        } else {
+            /* If we got a packet, return it */
+            *pkt = cur->pkt;
+            cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
+            pkt->stream_index = cur->stream_index;
+            reset_packet(&cur->pkt);
+            return 0;
+        }
+    }
+    return AVERROR_EOF;
+}
+
+static int dash_close(AVFormatContext *s)
+{
+    DASHContext *c = s->priv_data;
+
+    if (c->cur_audio) {
+        free_representation(c->cur_audio);
+    }
+
+    if (c->cur_video) {
+        free_representation(c->cur_video);
+    }
+
+    av_freep(&c->cookies);
+    av_freep(&c->user_agent);
+    av_dict_free(&c->avio_opts);
+    av_freep(&c->base_url);
+    return 0;
+}
+
+static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t seek_pos_msec, int flags)
+{
+    int ret = 0;
+    int i = 0;
+    int j = 0;
+    int64_t duration = 0;
+
+    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms], playlist %d\n", seek_pos_msec, pls->rep_idx);
+
+    // single fragment mode
+    if (pls->n_fragments) {
+        pls->cur_timestamp = 0;
+        pls->cur_seg_offset = 0;
+        ff_read_frame_flush(pls->ctx);
+        return av_seek_frame(pls->ctx, -1, seek_pos_msec*1000, flags);
+    }
+
+    if (pls->input)
+        ff_format_io_close(pls->parent, &pls->input);
+
+    // find the nearest fragment
+    if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
+        int64_t num = pls->first_seq_no;
+        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline start n_timelines[%d] last_seq_no[%"PRId64"], playlist %d.\n",
+               (int)pls->n_timelines, (int64_t)pls->last_seq_no, (int)pls->rep_idx);
+        for (i = 0; i < pls->n_timelines; i++) {
+            if (pls->timelines[i]->t > 0) {
+                duration = pls->timelines[i]->t;
+            }
+            duration += pls->timelines[i]->d;
+            if (seek_pos_msec < ((duration * 1000) /  pls->fragment_timescale)) {
+                goto set_seq_num;
+            }
+            for (j = 0; j < pls->timelines[i]->r; j++) {
+                duration += pls->timelines[i]->d;
+                num++;
+                if (seek_pos_msec < ((duration * 1000) /  pls->fragment_timescale)) {
+                    goto set_seq_num;
+                }
+            }
+            num++;
+        }
+
+set_seq_num:
+        pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no : num;
+        av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
+               (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
+    } else if (pls->fragment_duration > 0) {
+        pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec * pls->fragment_timescale) / pls->fragment_duration) / 1000;
+    } else {
+        av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing fragment_duration\n");
+        pls->cur_seq_no = pls->first_seq_no;
+    }
+    pls->cur_timestamp = 0;
+    pls->cur_seg_offset = 0;
+    pls->init_sec_buf_read_offset = 0;
+    ret = reopen_demux_for_component(s, pls);
+
+    return ret;
+}
+
+static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+{
+    int ret = 0;
+    DASHContext *c = s->priv_data;
+    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
+                                         s->streams[stream_index]->time_base.den,
+                                         flags & AVSEEK_FLAG_BACKWARD ?
+                                         AV_ROUND_DOWN : AV_ROUND_UP);
+    if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
+        return AVERROR(ENOSYS);
+    if (c->cur_audio) {
+        ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
+    }
+    if (!ret && c->cur_video) {
+        ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
+    }
+    return ret;
+}
+
+static int dash_probe(AVProbeData *p)
+{
+    if (!av_stristr(p->buf, "<MPD"))
+        return 0;
+
+    if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
+        av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
+        av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
+        av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
+        return AVPROBE_SCORE_MAX;
+    }
+    if (av_stristr(p->buf, "dash:profile")) {
+        return AVPROBE_SCORE_MAX >> 1;
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(DASHContext, x)
+#define FLAGS AV_OPT_FLAG_DECODING_PARAM
+static const AVOption dash_options[] = {
+    {NULL}
+};
+
+static const AVClass dash_class = {
+    .class_name = "dash",
+    .item_name  = av_default_item_name,
+    .option     = dash_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_dash_demuxer = {
+    .name           = "dash",
+    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
+    .priv_class     = &dash_class,
+    .priv_data_size = sizeof(DASHContext),
+    .read_probe     = dash_probe,
+    .read_header    = dash_read_header,
+    .read_packet    = dash_read_packet,
+    .read_close     = dash_close,
+    .read_seek      = dash_read_seek,
+    .flags          = AVFMT_NO_BYTE_SEEK,
+};