diff mbox

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

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

Commit Message

Steven Liu Aug. 27, 2017, 2:19 p.m. UTC
ffmpeg need a dash demuxer for demux the dash formats base on
https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch

TODO:
1. support multi bitrate dash

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

v10 fixed:
1. fix function API name code style
2. remove redundant strreplace call
3. remove redundant memory operation and check return value from get_content_url()
4. add space between ) and {
5. remove no need to log the value for print

v11 fixed:
1. from atoll to strtoll

v12 fixed:
1. remove strreplace and instead by av_strreplace

v13 fixed:
1. fix bug: cannot play:
http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd

v14 fixed:
1. fix bug: TLS connection was non-properly terminated
2. fix bug: No trailing CRLF found in HTTP header

v15 fixed:
1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" | jq -r ".requested_formats[0].manifest_url")
2. code refine for timeline living stream

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>
Reviewed-by: wm4 <nfxjfg@googlemail.com>
Tested-by: Andy Furniss <adf.lists@gmail.com>
Reported-by: Andy Furniss <adf.lists@gmail.com>
Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
Signed-off-by: samsamsam <samsamsam@o2.pl>
---
 configure                |    4 +
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    2 +-
 libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1987 insertions(+), 1 deletion(-)
 create mode 100644 libavformat/dashdec.c

Comments

Rodger Combs Aug. 27, 2017, 2:34 p.m. UTC | #1
You're still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`, and specify an additional "precision" argument you parse from the XML yourself. I can't reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_.

Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That's what it does internally anyway.

> On Aug 27, 2017, at 09:19, Steven Liu <lq@chinaffmpeg.org> wrote:
> 
> ffmpeg need a dash demuxer for demux the dash formats base on
> https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch
> 
> TODO:
> 1. support multi bitrate dash
> 
> 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
> 
> v10 fixed:
> 1. fix function API name code style
> 2. remove redundant strreplace call
> 3. remove redundant memory operation and check return value from get_content_url()
> 4. add space between ) and {
> 5. remove no need to log the value for print
> 
> v11 fixed:
> 1. from atoll to strtoll
> 
> v12 fixed:
> 1. remove strreplace and instead by av_strreplace
> 
> v13 fixed:
> 1. fix bug: cannot play:
> http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd
> 
> v14 fixed:
> 1. fix bug: TLS connection was non-properly terminated
> 2. fix bug: No trailing CRLF found in HTTP header
> 
> v15 fixed:
> 1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" | jq -r ".requested_formats[0].manifest_url")
> 2. code refine for timeline living stream
> 
> 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>
> Reviewed-by: wm4 <nfxjfg@googlemail.com>
> Tested-by: Andy Furniss <adf.lists@gmail.com>
> Reported-by: Andy Furniss <adf.lists@gmail.com>
> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> Signed-off-by: samsamsam <samsamsam@o2.pl>
> ---
> configure                |    4 +
> libavformat/Makefile     |    1 +
> libavformat/allformats.c |    2 +-
> libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1987 insertions(+), 1 deletion(-)
> create mode 100644 libavformat/dashdec.c
> 
> diff --git a/configure b/configure
> index 05f6dcc99a..7a7d61fa13 100755
> --- a/configure
> +++ b/configure
> @@ -272,6 +272,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]
> @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST="
>     libvpx
>     libwavpack
>     libwebp
> +    libxml2
>     libzimg
>     libzmq
>     libzvbi
> @@ -2937,6 +2939,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"
> @@ -5996,6 +5999,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>                                check_lib openssl 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
> 
> if enabled gcrypt; then
>     GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index f2b465cfa2..3d478749d0 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab
> --- /dev/null
> +++ b/libavformat/dashdec.c
> @@ -0,0 +1,1981 @@
> +/*
> + * 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).
> +     * */
> +    int64_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;
> +    char *url_template_pattern;
> +    char *url_template_format;
> +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
> +
> +    int64_t fragment_duration;
> +    int64_t fragment_timescale;
> +
> +    int64_t presentation_timeoffset;
> +
> +    int64_t cur_seq_no;
> +    int64_t cur_seg_offset;
> +    int64_t cur_seg_size;
> +    struct fragment *cur_seg;
> +
> +    /* Currently active Media Initialization Section */
> +    struct fragment *init_section;
> +    uint8_t *init_sec_buf;
> +    uint32_t init_sec_buf_size;
> +    uint32_t init_sec_data_len;
> +    uint32_t init_sec_buf_read_offset;
> +    int fix_multiple_stsd_order;
> +    int64_t cur_timestamp;
> +    int is_restart_needed;
> +};
> +
> +typedef struct DASHContext {
> +    const AVClass *class;
> +    char *base_url;
> +    struct representation *cur_video;
> +    struct representation *cur_audio;
> +
> +    /* MediaPresentationDescription Attribute */
> +    uint64_t media_presentation_duration;
> +    uint64_t suggested_presentation_delay;
> +    uint64_t availability_start_time;
> +    uint64_t publish_time;
> +    uint64_t minimum_update_period;
> +    uint64_t time_shift_buffer_depth;
> +    uint64_t min_buffer_time;
> +
> +    /* Period Attribute */
> +    uint64_t period_duration;
> +    uint64_t period_start;
> +
> +    int is_live;
> +    AVIOInterruptCB *interrupt_callback;
> +    char *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 get_current_time_in_sec(void)
> +{
> +    return  av_gettime() / 1000000;
> +}
> +
> +static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
> +{
> +    struct tm timeinfo;
> +    int year = 0;
> +    int month = 0;
> +    int day = 0;
> +    int hour = 0;
> +    int minute = 0;
> +    int ret = 0;
> +    float second = 0.0;
> +
> +    /* ISO-8601 date parser */
> +    if (!datetime)
> +        return 0;
> +
> +    ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
> +    /* year, month, day, hour, minute, second  6 arguments */
> +    if (ret != 6) {
> +        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
> +    }
> +    timeinfo.tm_year = year - 1900;
> +    timeinfo.tm_mon  = month - 1;
> +    timeinfo.tm_mday = day;
> +    timeinfo.tm_hour = hour;
> +    timeinfo.tm_min  = minute;
> +    timeinfo.tm_sec  = (int)second;
> +
> +    return av_timegm(&timeinfo);
> +}
> +
> +static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
> +{
> +    /* ISO-8601 duration parser */
> +    uint32_t days = 0;
> +    uint32_t hours = 0;
> +    uint32_t mins = 0;
> +    uint32_t secs = 0;
> +    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, "get_duration_insec get a wrong time format\n");
> +            return 0; /* parser error */
> +        }
> +        switch (type) {
> +            case 'D':
> +                days = (uint32_t)value;
> +                break;
> +            case 'H':
> +                hours = (uint32_t)value;
> +                break;
> +            case 'M':
> +                mins = (uint32_t)value;
> +                break;
> +            case 'S':
> +                secs = (uint32_t)value;
> +                break;
> +            default:
> +                // handle invalid type
> +                break;
> +        }
> +        ptr += size;
> +    }
> +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;
> +}
> +
> +static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
> +{
> +    int64_t start_time = 0;
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +
> +    if (pls->n_timelines) {
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            if (pls->timelines[i]->t > 0) {
> +                start_time = pls->timelines[i]->t;
> +            }
> +            if (num == cur_seq_no)
> +                goto finish;
> +
> +            start_time += pls->timelines[i]->d;
> +            for (j = 0; j < pls->timelines[i]->r; j++) {
> +                num++;
> +                if (num == cur_seq_no)
> +                    goto finish;
> +                start_time += pls->timelines[i]->d;
> +            }
> +            num++;
> +        }
> +    }
> +finish:
> +    return start_time;
> +}
> +
> +static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
> +{
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +    int64_t start_time = 0;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        if (pls->timelines[i]->t > 0) {
> +            start_time = pls->timelines[i]->t;
> +        }
> +        if (start_time > cur_time)
> +            goto finish;
> +
> +        start_time += pls->timelines[i]->d;
> +        for (j = 0; j < pls->timelines[i]->r; j++) {
> +            num++;
> +            if (start_time > cur_time)
> +                goto finish;
> +            start_time += pls->timelines[i]->d;
> +        }
> +        num++;
> +    }
> +
> +    return -1;
> +
> +finish:
> +    return num;
> +}
> +
> +static void free_fragment(struct fragment **seg)
> +{
> +    if (!(*seg)) {
> +        return;
> +    }
> +    av_freep(&(*seg)->url);
> +    av_freep(seg);
> +}
> +
> +static void free_fragment_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_fragments; i++) {
> +        free_fragment(&pls->fragments[i]);
> +    }
> +    av_freep(&pls->fragments);
> +    pls->n_fragments = 0;
> +}
> +
> +static void free_timelines_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        av_freep(&pls->timelines[i]);
> +    }
> +    av_freep(&pls->timelines);
> +    pls->n_timelines = 0;
> +}
> +
> +static void free_representation(struct representation *pls)
> +{
> +    free_fragment_list(pls);
> +    free_timelines_list(pls);
> +    free_fragment(&pls->cur_seg);
> +    free_fragment(&pls->init_section);
> +    av_freep(&pls->init_sec_buf);
> +    av_freep(&pls->pb.buffer);
> +    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_pattern);
> +    av_free(pls->url_template_format);
> +    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;
> +    void *p = NULL;
> +
> +    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.
> +        p = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> +        update_options(&c->cookies, "cookies", p);
> +        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 *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 = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
> +        av_free(tmp_str);
> +        tmp_str = url;
> +    }
> +    if (rep_bandwidth_val && tmp_str)
> +        url = av_strireplace(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 find_child_node_by_name(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 int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format)
> +{
> +    int ret = -1;
> +    int marker_len = 0;
> +    int format_len = 0;
> +    char *prefix = NULL;
> +    char *start = NULL;
> +    char *end = NULL;
> +
> +
> +    if (av_stristr(i_url, i_marker)) {
> +        *o_pattern = av_strdup(i_marker);
> +        *o_format = av_strdup("%"PRId64);
> +        ret = 0;
> +    } else {
> +        prefix = av_strdup(i_marker);
> +        marker_len = strlen(prefix)-1;
> +        prefix[marker_len] = '\0';
> +        start = av_stristr(i_url, prefix);
> +        if (!start)
> +            goto finish;
> +
> +        end = strchr(start + 1, '$');
> +        if (!end)
> +            goto finish;
> +
> +        if (start[marker_len] != '%')
> +            goto finish;
> +
> +        if (end[-1] != 'd')
> +            goto finish;
> +
> +        format_len = end - start - marker_len - 1 + strlen(PRId64);
> +        *o_format = av_mallocz(format_len+1);
> +        av_strlcpy(*o_format, start + marker_len, end - start - marker_len -1);
> +        av_strlcat(*o_format, PRId64, strlen(*o_format) + strlen(PRId64));
> +        *o_pattern = av_mallocz(end - start + 2);
> +        if (*o_pattern) {
> +            ret = AVERROR(EINVAL);
> +            goto finish;
> +        }
> +        av_strlcpy(*o_pattern, start, end - start + 1);
> +        ret = 0;
> +
> +finish:
> +        av_free(prefix);
> +    }
> +
> +    return ret;
> +}
> +
> +static enum AVMediaType get_content_type(xmlNodePtr node)
> +{
> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> +    int i = 0;
> +    const char *attr;
> +    xmlChar *val = NULL;
> +
> +    if (node) {
> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> +            attr = (i) ? "mimeType" : "contentType";
> +            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);
> +            if (!rep->init_section->url) {
> +                av_free(rep->init_section);
> +                xmlFree(initialization_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                xmlFree(media_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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)strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> +                tml->r =(int64_t) strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> +                tml->d = (int64_t)strtoll(val, NULL, 10);
> +//                rep->fragment_duration = (int64_t) strtoll(val, NULL, 10);
> +            }
> +            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 *presentation_timeoffset_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 = find_child_node_by_name(representation_node, "SegmentTemplate");
> +        representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
> +        representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
> +
> +        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;
> +
> +            presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
> +            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);
> +                if (!rep->init_section->url) {
> +                    av_free(rep->init_section);
> +                    av_free(rep);
> +                    ret = AVERROR(ENOMEM);
> +                    goto end;
> +                }
> +                rep->init_section->size = -1;
> +                xmlFree(initialization_val);
> +            }
> +
> +            if (media_val) {
> +                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
> +                temp_string = rep->url_template;
> +                if (temp_string) {
> +                    if (av_stristr(temp_string, "$Number")) {
> +                        get_repl_pattern_and_format(temp_string, "$Number$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
> +                    } else if (av_stristr(temp_string, "$Time")) {
> +                        get_repl_pattern_and_format(temp_string, "$Time$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
> +                    } else {
> +                        temp_string = NULL;
> +                    }
> +                }
> +                xmlFree(media_val);
> +            }
> +
> +            if (presentation_timeoffset_val) {
> +                rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
> +                xmlFree(presentation_timeoffset_val);
> +            }
> +            if (duration_val) {
> +                rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                xmlFree(timescale_val);
> +            }
> +            if (startnumber_val) {
> +                rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
> +                xmlFree(startnumber_val);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                ret = AVERROR(ENOMEM);
> +                goto end;
> +            }
> +            seg->size = -1;
> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> +        } else if (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) strtoll(duration_val, NULL, 10);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                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);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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 {
> +            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 = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
> +                c->publish_time = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
> +                c->minimum_update_period = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
> +                c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
> +                c->min_buffer_time = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
> +                c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
> +                c->media_presentation_duration = get_duration_insec(s, (const char *)val);
> +            }
> +            attr = attr->next;
> +            xmlFree(val);
> +        }
> +
> +        mpd_baseurl_node = find_child_node_by_name(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 = get_duration_insec(s, (const char *)val);
> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
> +                        perdiod_start_sec = get_duration_insec(s, (const char *)val);
> +                    }
> +                    attr = attr->next;
> +                    xmlFree(val);
> +                }
> +                if ((perdiod_duration_sec) >= (c->period_duration)) {
> +                    period_node = node;
> +                    c->period_duration = perdiod_duration_sec;
> +                    c->period_start = perdiod_start_sec;
> +                    if (c->period_start > 0)
> +                        c->media_presentation_duration = c->period_duration;
> +                }
> +            }
> +            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;
> +        }
> +
> +        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 = s->priv_data;
> +    int64_t num = 0;
> +    int64_t start_time_offset = 0;
> +
> +    if (c->is_live) {
> +        if (pls->n_fragments) {
> +            num = pls->first_seq_no;
> +        } else if (pls->n_timelines) {
> +            start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->t; // total duration of playlist
> +            if (start_time_offset < 60 * pls->fragment_timescale)
> +                start_time_offset = 0;
> +            else
> +                start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
> +
> +            num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->t + start_time_offset);
> +            if (num == -1)
> +                num = pls->first_seq_no;
> +        } else {
> +            if (pls->presentation_timeoffset) {
> +                num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
> +            } else if (c->publish_time > 0) {
> +                num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
> +            } else {
> +                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * 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 = s->priv_data;
> +    int64_t num = 0;
> +
> +    if (c->is_live && pls->fragment_duration) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no;
> +    }
> +    return num;
> +}
> +
> +static int64_t calc_max_seg_no(struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +
> +    if (pls->n_fragments) {
> +        num = pls->first_seq_no + pls->n_fragments - 1;
> +    } else if (pls->n_timelines) {
> +        int i = 0;
> +        num = pls->first_seq_no + pls->n_timelines - 1;
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            num += pls->timelines[i]->r;
> +        }
> +    } else if (c->is_live) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
> +    }
> +
> +    return num;
> +}
> +
> +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_timelines_list(rep_dest);
> +        rep_dest->timelines    = rep_src->timelines;
> +        rep_dest->n_timelines  = rep_src->n_timelines;
> +        rep_dest->first_seq_no = rep_src->first_seq_no;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->timelines = NULL;
> +        rep_src->n_timelines = 0;
> +        rep_dest->cur_seq_no = rep_src->cur_seq_no;
> +    }
> +}
> +
> +static void move_segments(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_fragment_list(rep_dest);
> +        if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
> +            rep_dest->cur_seq_no = 0;
> +        else
> +            rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
> +        rep_dest->fragments    = rep_src->fragments;
> +        rep_dest->n_fragments  = rep_src->n_fragments;
> +        rep_dest->parent  = rep_src->parent;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->fragments = NULL;
> +        rep_src->n_fragments = 0;
> +    }
> +}
> +
> +
> +static int refresh_manifest(AVFormatContext *s)
> +{
> +
> +    int ret = 0;
> +    DASHContext *c = s->priv_data;
> +
> +    // save current context
> +    struct representation *cur_video =  c->cur_video;
> +    struct representation *cur_audio =  c->cur_audio;
> +    char *base_url = c->base_url;
> +
> +    c->base_url = NULL;
> +    c->cur_video = NULL;
> +    c->cur_audio = NULL;
> +    ret = parse_manifest(s, s->filename, NULL);
> +    if (ret)
> +        goto finish;
> +
> +    if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
> +        // calc current time
> +        int64_t currentVideoTime = 0;
> +        int64_t currentAudioTime = 0;
> +        if (cur_video && cur_video->timelines)
> +            currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
> +        if (cur_audio && cur_audio->timelines)
> +            currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
> +        // update segments
> +        if (cur_video && cur_video->timelines) {
> +            c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
> +            if (c->cur_video->cur_seq_no >= 0) {
> +                move_timelines(c->cur_video, cur_video);
> +            }
> +        }
> +        if (cur_audio && cur_audio->timelines) {
> +            c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
> +            if (c->cur_audio->cur_seq_no >= 0) {
> +               move_timelines(c->cur_audio, cur_audio);
> +            }
> +        }
> +    }
> +    if (cur_video && cur_video->fragments) {
> +        move_segments(c->cur_video, cur_video);
> +    }
> +    if (cur_audio && cur_audio->fragments) {
> +        move_segments(c->cur_audio, cur_audio);
> +    }
> +
> +finish:
> +    // restore context
> +    if (c->base_url)
> +        av_free(base_url);
> +    else
> +        c->base_url  = base_url;
> +    if (c->cur_audio)
> +        free_representation(c->cur_audio);
> +    if (c->cur_video)
> +        free_representation(c->cur_video);
> +    c->cur_audio = cur_audio;
> +    c->cur_video = cur_video;
> +    return ret;
> +}
> +
> +static struct fragment *get_current_fragment(struct representation *pls)
> +{
> +    int64_t min_seq_no = 0;
> +    int64_t max_seq_no = 0;
> +    struct fragment *seg = NULL;
> +    struct fragment *seg_ptr = NULL;
> +    DASHContext *c = pls->parent->priv_data;
> +
> +    while (( !ff_check_interrupt(c->interrupt_callback)&& 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;
> +        } else if (c->is_live) {
> +            av_usleep(1000*1000);
> +            refresh_manifest(pls->parent);
> +        } else {
> +            break;
> +        }
> +    }
> +    if (c->is_live) {
> +        while (!ff_check_interrupt(c->interrupt_callback)) {
> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> +            max_seq_no = calc_max_seg_no(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);
> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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*1000);
> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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) {
> +            int64_t val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no);
> +            int size = snprintf(NULL, 0, pls->url_template_format, val); // calc needed buffer size
> +
> +            if (size > 0) {
> +                char *tmp_val = av_mallocz(size + 1);
> +                snprintf(tmp_val, size+1, pls->url_template_format, val);
> +                seg->url = av_strireplace(pls->url_template, pls->url_template_pattern, tmp_val);
> +                av_free(tmp_val);
> +            }
> +        }
> +
> +        if (!seg->url) {
> +            av_log(pls->parent, AV_LOG_ERROR, "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;
> +    }
> +
> +    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;
> +
> +    if (!v->is_restart_needed)
> +        v->cur_seq_no++;
> +    v->is_restart_needed = 1;
> +
> +/*
> +    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[0] != '\0') {
> +                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 DASH 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(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 * 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) {
> +        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
> +            ret = av_read_frame(cur->ctx, pkt);
> +            if (ret >= 0) {
> +                /* If we got a packet, return it */
> +                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;
> +                return 0;
> +            }
> +            if (cur->is_restart_needed) {
> +                while (!ff_check_interrupt(c->interrupt_callback)) {
> +                    cur->cur_seg_offset = 0;
> +                    cur->init_sec_buf_read_offset = 0;
> +                    if (cur->input)
> +                        ff_format_io_close(cur->parent, &cur->input);
> +                    ret = reopen_demux_for_component(s, cur);
> +                    if (c->is_live && ret) {
> +                        av_usleep(1000*1000);
> +                        continue;
> +                    }
> +                    break;
> +                }
> +                cur->is_restart_needed = 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 == 1) {
> +        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 / 2;
> +    }
> +
> +    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.11.0 (Apple Git-81)
> 
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
samsamsam Aug. 27, 2017, 7:04 p.m. UTC | #2
get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64`   If you afraid about safety then the only thing which need to be added to  get_repl_pattern_and_format is validation of format.  Simple loop to validate format will be enough. Do you agree?    Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file.   I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety.   Regards,  SSS 
                
              
               
                 
                  Dnia 27 sierpnia 2017 16:34 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a):
                 
                 
                   You&#39;re still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64`, and specify an additional &#34;precision&#34; argument you parse from the XML yourself. I can&#39;t reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_. 
  
 Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That&#39;s what it does internally anyway. 
  
 
 On Aug 27, 2017, at 09:19, Steven Liu &lt;lq@chinaffmpeg.org&gt; wrote: 
  
 ffmpeg need a dash demuxer for demux the dash formats base on 
 github.com github.com 
  
 TODO: 
 1. support multi bitrate dash 
  
 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 &#39;time(NULL)`, except it is not 2038-safe.&#39; 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-&gt;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 
  
 v10 fixed: 
 1. fix function API name code style 
 2. remove redundant strreplace call 
 3. remove redundant memory operation and check return value from get_content_url() 
 4. add space between ) and { 
 5. remove no need to log the value for print 
  
 v11 fixed: 
 1. from atoll to strtoll 
  
 v12 fixed: 
 1. remove strreplace and instead by av_strreplace 
  
 v13 fixed: 
 1. fix bug: cannot play: 
 dash.edgesuite.net dash.edgesuite.net 
  
 v14 fixed: 
 1. fix bug: TLS connection was non-properly terminated 
 2. fix bug: No trailing CRLF found in HTTP header 
  
 v15 fixed: 
 1. play youtube link: ffmpeg -i $(youtube-dl -J &#34; www.youtube.com www.youtube.com  | jq -r &#34;.requested_formats[0].manifes 
 2. code refine for timeline living stream 
  
 Reviewed-by: Clément Bœsch &lt;u@pkh.me&gt; 
 Reviewed-by: Michael Niedermayer &lt;michael@niedermayer.cc&gt; 
 Reviewed-by: Carl Eugen Hoyos &lt;cehoyos@ag.or.at&gt; 
 Reviewed-by: Rodger Combs &lt;rodger.combs@gmail.com&gt; 
 Reviewed-by: Moritz Barsnick &lt;barsnick@gmx.net&gt; 
 Reviewed-by: Nicolas George &lt;george@nsup.org&gt; 
 Reviewed-by: Ricardo Constantino &lt;wiiaboo@gmail.com&gt; 
 Reviewed-by: wm4 &lt;nfxjfg@googlemail.com&gt; 
 Tested-by: Andy Furniss &lt;adf.lists@gmail.com&gt; 
 Reported-by: Andy Furniss &lt;adf.lists@gmail.com&gt; 
 Signed-off-by: Steven Liu &lt;lq@chinaffmpeg.org&gt; 
 Signed-off-by: samsamsam &lt;samsamsam@o2.pl&gt; 
 --- 
 configure                    4 + 
 libavformat/Makefile     |    1 + 
 libavformat/allformats.c |    2 +- 
 libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++ 
 4 files changed, 1987 insertions(+), 1 deletion(-) 
 create mode 100644 libavformat/dashdec.c 
  
 diff --git a/configure b/configure 
 index 05f6dcc99a..7a7d61fa13 100755 
 --- a/configure 
 +++ b/configure 
 @@ -272,6 +272,7 @@ External library support: 
   --enable-libxcb-shape    enable X11 grabbing shape rendering [autodetect] 
   --enable-libxvid         enable Xvid encoding via xvidcore, 
                  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] 
 @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST=&#34; 
     libvpx 
     libwavpack 
     libwebp 
 +    libxml2 
     libzimg 
     libzmq 
     libzvbi 
 @@ -2937,6 +2939,7 @@ avi_muxer_select=&#34;riffenc&#34; 
 caf_demuxer_select=&#34;iso_media riffdec&#34; 
 caf_muxer_select=&#34;iso_media&#34; 
 dash_muxer_select=&#34;mp4_muxer&#34; 
 +dash_demuxer_deps=&#34;libxml2&#34; 
 dirac_demuxer_select=&#34;dirac_pa 
 dts_demuxer_select=&#34;dca_parser 
 dtshd_demuxer_select=&#34;dca_pars 
 @@ -5996,6 +5999,7 @@ enabled openssl           &amp;&amp; { use_pkg_config openssl openssl/ssl.h OPENSSL_init 
                  openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 || 
                  &#34;ERROR: openssl not found&#34;; } 
 enabled qtkit_indev      &amp;&amp; { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; } 
 +enabled libxml2          &amp;&amp; require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion 
  
 if enabled gcrypt; then 
     GCRYPT_CONFIG=&#34;${cross_p 
 diff --git a/libavformat/Makefile b/libavformat/Makefile 
 index f2b465cfa2..3d478749d0 100644 
 --- a/libavformat/Makefile 
 +++ b/libavformat/Makefile 
 @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab 
 --- /dev/null 
 +++ b/libavformat/dashdec.c 
 @@ -0,0 +1,1981 @@ 
 +/* 
 + * 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 &lt;libxml/parser.h&gt; 
 +#include &#34;libavutil/intreadwrite.h&#34; 
 +#include &#34;libavutil/opt.h&#34; 
 +#include &#34;libavutil/time.h&#34; 
 +#include &#34;libavutil/parseutils.h&#34; 
 +#include &#34;internal.h&#34; 
 +#include &#34;avio_internal.h&#34; 
 + 
 +#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&#39;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). 
 +     * */ 
 +    int64_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; 
 +    char *url_template_pattern; 
 +    char *url_template_format; 
 +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/ 
 + 
 +    int64_t fragment_duration; 
 +    int64_t fragment_timescale; 
 + 
 +    int64_t presentation_timeoffset; 
 + 
 +    int64_t cur_seq_no; 
 +    int64_t cur_seg_offset; 
 +    int64_t cur_seg_size; 
 +    struct fragment *cur_seg; 
 + 
 +    /* Currently active Media Initialization Section */ 
 +    struct fragment *init_section; 
 +    uint8_t *init_sec_buf; 
 +    uint32_t init_sec_buf_size; 
 +    uint32_t init_sec_data_len; 
 +    uint32_t init_sec_buf_read_offset; 
 +    int fix_multiple_stsd_order; 
 +    int64_t cur_timestamp; 
 +    int is_restart_needed; 
 +}; 
 + 
 +typedef struct DASHContext { 
 +    const AVClass *class; 
 +    char *base_url; 
 +    struct representation *cur_video; 
 +    struct representation *cur_audio; 
 + 
 +    /* MediaPresentationDescription Attribute */ 
 +    uint64_t media_presentation_duration; 
 +    uint64_t suggested_presentation_delay; 
 +    uint64_t availability_start_time; 
 +    uint64_t publish_time; 
 +    uint64_t minimum_update_period; 
 +    uint64_t time_shift_buffer_depth; 
 +    uint64_t min_buffer_time; 
 + 
 +    /* Period Attribute */ 
 +    uint64_t period_duration; 
 +    uint64_t period_start; 
 + 
 +    int is_live; 
 +    AVIOInterruptCB *interrupt_callback; 
 +    char *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 get_current_time_in_sec(void) 
 +{ 
 +    return  av_gettime() / 1000000; 
 +} 
 + 
 +static uint64_t get_utc_date_time_insec(AVForm *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, &#34;%d-%d-%dT%d:%d:%fZ&#34;, &amp;year, &amp;month, &amp;day, &amp;hour, &amp;minute, &amp;second); 
 +    /* year, month, day, hour, minute, second  6 arguments */ 
 +    if (ret != 6) { 
 +        av_log(s, AV_LOG_WARNING, &#34;get_utc_date_time_insec get a wrong time format\n&#34;); 
 +    } 
 +    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(&amp;timeinfo); 
 +} 
 + 
 +static uint32_t get_duration_insec(AVFormatCon *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 == &#39;P&#39; || *ptr == &#39;T&#39;) { 
 +            ptr++; 
 +            continue 
 +        } 
 + 
 +        if (sscanf(ptr, &#34;%f%c%n&#34;, &amp;value, &amp;type, &amp;size) != 2) { 
 +            av_log(s AV_LOG_WARNING, &#34;get_duration_insec get a wrong time format\n&#34;); 
 +            return 0; /* parser error */ 
 +        } 
 +        switch (type) { 
 +            case &#39;D&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;H&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;M&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;S&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            default: 
 +                 handle invalid type 
 +                 
 +        } 
 +        ptr += size; 
 +    } 
 +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs; 
 +} 
 + 
 +static int64_t get_segment_start_time_based_o representation *pls, int64_t cur_seq_no) 
 +{ 
 +    int64_t start_time = 0; 
 +    int64_t i = 0; 
 +    int64_t j = 0; 
 +    int64_t num = 0; 
 + 
 +    if (pls-&gt;n_timelines) { 
 +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +            if (pls-&gt;timelines[i]-&gt;t &gt; 0) { 
 +                 = pls-&gt;timelines[i]-&gt;t; 
 +            } 
 +            if (num == cur_seq_no) 
 +                 finish; 
 + 
 +            start_ti += pls-&gt;timelines[i]-&gt;d; 
 +            for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) { 
 +                 
 +                 (num == cur_seq_no) 
 +                 finish; 
 +                 += pls-&gt;timelines[i]-&gt;d; 
 +            } 
 +            num++; 
 +        } 
 +    } 
 +finish: 
 +    return start_time; 
 +} 
 + 
 +static int64_t calc_next_seg_no_from_timeline representation *pls, int64_t cur_time) 
 +{ 
 +    int64_t i = 0; 
 +    int64_t j = 0; 
 +    int64_t num = 0; 
 +    int64_t start_time = 0; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +        if (pls-&gt;timelines[i]-&gt;t &gt; 0) { 
 +            start_ti = pls-&gt;timelines[i]-&gt;t; 
 +        } 
 +        if (start_time &gt; cur_time) 
 +            goto finish; 
 + 
 +        start_time += pls-&gt;timelines[i]-&gt;d; 
 +        for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) { 
 +            num++; 
 +            if (start_time &gt; cur_time) 
 +                 finish; 
 +            start_ti += pls-&gt;timelines[i]-&gt;d; 
 +        } 
 +        num++; 
 +    } 
 + 
 +    return -1; 
 + 
 +finish: 
 +    return num; 
 +} 
 + 
 +static void free_fragment(struct fragment **seg) 
 +{ 
 +    if (!(*seg)) { 
 +        return; 
 +    } 
 +    av_freep(&amp;(*seg)-&gt;url); 
 +    av_freep(seg); 
 +} 
 + 
 +static void free_fragment_list(struct representation *pls) 
 +{ 
 +    int i; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_fragments; i++) { 
 +        free_fragment(&amp;p 
 +    } 
 +    av_freep(&amp;pls-&gt;fragments 
 +    pls-&gt;n_fragments = 0; 
 +} 
 + 
 +static void free_timelines_list(struct representation *pls) 
 +{ 
 +    int i; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +        av_freep(&amp;pls-&gt;t 
 +    } 
 +    av_freep(&amp;pls-&gt;timelines 
 +    pls-&gt;n_timelines = 0; 
 +} 
 + 
 +static void free_representation(struct representation *pls) 
 +{ 
 +    free_fragment_list(pls); 
 +    free_timelines_list(pls) 
 +    free_fragment(&amp;pls-&gt;cur_ 
 +    free_fragment(&amp;pls-&gt;init 
 +    av_freep(&amp;pls-&gt;init_sec_ 
 +    av_freep(&amp;pls-&gt;pb.buffer 
 +    if (pls-&gt;input) 
 +        ff_format_io_clo &amp;pls-&gt;input); 
 +    if (pls-&gt;ctx) { 
 +        pls-&gt;ctx-&gt;pb = NULL; 
 +        avformat_close_i 
 +    } 
 + 
 +    av_free(pls-&gt;url_templat 
 +    av_free(pls-&gt;url_templat 
 +    av_free(pls-&gt;url_templat 
 +    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, 
 +                 *opts, AVDictionary *opts2, int *is_http) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    AVDictionary *tmp = NULL; 
 +    const char *proto_name = NULL; 
 +    int ret; 
 +    void *p = NULL; 
 + 
 +    av_dict_copy(&amp;tmp, opts, 0); 
 +    av_dict_copy(&amp;tmp, opts2, 0); 
 + 
 +    if (av_strstart(url, &#34;crypto&#34;, NULL)) { 
 +        if (url[6] == &#39;+&#39; || url[6] == &#39;:&#39;) 
 +            proto_na = 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) &amp; file are allowed 
 +    if (!av_strstart(proto_name, &#34;http&#34;, NULL) &amp;&amp; !av_strstart(proto_name, &#34;file&#34;, NULL)) { 
 +        return AVERROR_INVALIDDATA; 
 +    } 
 +    if (!strncmp(proto_name, url, strlen(proto_name)) &amp;&amp; url[strlen(proto_name)] == &#39;:&#39;) { 
 +        ; 
 +    } else if (av_strstart(url, &#34;crypto&#34;, NULL) &amp;&amp; !strncmp(proto_name, url + 7, strlen(proto_name)) &amp;&amp; url[7 + strlen(proto_name)] == &#39;:&#39;) { 
 +        ; 
 +    } else if (strcmp(proto_name, &#34;file&#34;) || !strncmp(url, &#34;file,&#34;, 5)) { 
 +        return AVERROR_INVALIDDATA; 
 +    } 
 +    ret = s-&gt;io_open(s, pb, url, AVIO_FLAG_READ, &amp;tmp); 
 +    if (ret &gt;= 0) { 
 +        // update cookies on http response with setcookies. 
 +        p = (s-&gt;flags &amp; AVFMT_FLAG_CUSTOM_IO) ? NULL : s-&gt;pb; 
 +        update_options(&amp; &#34;cookies&#34;, p); 
 +        av_dict_set(&amp;opt &#34;cookies&#34;, c-&gt;cookies, 0); 
 +    } 
 + 
 +    av_dict_free(&amp;tmp); 
 + 
 +    if (is_http) 
 +        *is_http = av_strstart(proto_name, &#34;http&#34;, NULL); 
 + 
 +    return ret; 
 + 
 +} 
 + 
 +static char *get_content_url(xmlNodePtr *baseurl_nodes, 
 +                 n_baseurl_nodes, 
 +                 *rep_id_val, 
 +                 *rep_bandwidth_val, 
 +                 *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 &lt; n_baseurl_nodes; ++i) { 
 +        if (baseurl_nodes[i] &amp;&amp; 
 +            baseurl_ &amp;&amp; 
 +            baseurl_ == XML_TEXT_NODE) { 
 +            text = xmlNodeGetContent(baseurl_node 
 +            if (text) { 
 +                 = av_mallocz(MAX_URL_SIZE); 
 +                 (!tmp_str_2) { 
 +                 
 +                 NULL; 
 +                 
 +                 MAX_URL_SIZE, tmp_str, text); 
 +                 
 +                 = tmp_str_2; 
 +                 
 +            } 
 +        } 
 +    } 
 +    if (val) 
 +        av_strlcat(tmp_s (const char*)val, MAX_URL_SIZE); 
 + 
 +    if (rep_id_val) { 
 +        url = av_strireplace(tmp_str, &#34;$RepresentationID$&#34;, (const char*)rep_id_val); 
 +        av_free(tmp_str) 
 +        tmp_str = url; 
 +    } 
 +    if (rep_bandwidth_val &amp;&amp; tmp_str) 
 +        url = av_strireplace(tmp_str, &#34;$Bandwidth$&#34;, (const char*)rep_bandwidth_val); 
 +    if (tmp_str != url) 
 +        av_free(tmp_str) 
 +    return url; 
 +} 
 + 
 +static xmlChar *get_val_from_nodes_tab(xmlNod *nodes, const int n_nodes, const xmlChar *attrname) 
 +{ 
 +    int i; 
 +    xmlChar *val; 
 + 
 +    for (i = 0; i &lt; n_nodes; ++i) { 
 +        if (nodes[i]) { 
 +            val = xmlGetProp(nodes[i], attrname); 
 +            if (val) 
 +                 val; 
 +        } 
 +    } 
 + 
 +    return NULL; 
 +} 
 + 
 +static xmlNodePtr find_child_node_by_name(xmlNod rootnode, const xmlChar *nodename) 
 +{ 
 +    xmlNodePtr node = rootnode; 
 +    if (!node) { 
 +        return NULL; 
 +    } 
 + 
 +    node = xmlFirstElementChild(node); 
 +    while (node) { 
 +        if (!xmlStrcmp(node-&gt;name, nodename)) { 
 +            return node; 
 +        } 
 +        node = xmlNextElementSibling(node); 
 +    } 
 +    return NULL; 
 +} 
 + 
 +static int get_repl_pattern_and_format(co char *i_url, const char *i_marker, char **o_pattern, char **o_format) 
 +{ 
 +    int ret = -1; 
 +    int marker_len = 0; 
 +    int format_len = 0; 
 +    char *prefix = NULL; 
 +    char *start = NULL; 
 +    char *end = NULL; 
 + 
 + 
 +    if (av_stristr(i_url, i_marker)) { 
 +        *o_pattern = av_strdup(i_marker); 
 +        *o_format = av_strdup(&#34;%&#34;PRId64); 
 +        ret = 0; 
 +    } else { 
 +        prefix = av_strdup(i_marker); 
 +        marker_len = strlen(prefix)-1; 
 +        prefix[marker_le = &#39;\0&#39;; 
 +        start = av_stristr(i_url, prefix); 
 +        if (!start) 
 +            goto finish; 
 + 
 +        end = strchr(start + 1, &#39;$&#39;); 
 +        if (!end) 
 +            goto finish; 
 + 
 +        if (start[marker_len] != &#39;%&#39;) 
 +            goto finish; 
 + 
 +        if (end[-1] != &#39;d&#39;) 
 +            goto finish; 
 + 
 +        format_len = end - start - marker_len - 1 + strlen(PRId64); 
 +        *o_format = av_mallocz(format_len+1); 
 +        av_strlcpy(*o_fo start + marker_len, end - start - marker_len -1); 
 +        av_strlcat(*o_fo PRId64, strlen(*o_format) + strlen(PRId64)); 
 +        *o_pattern = av_mallocz(end - start + 2); 
 +        if (*o_pattern) { 
 +            ret = AVERROR(EINVAL); 
 +            goto finish; 
 +        } 
 +        av_strlcpy(*o_pa start, end - start + 1); 
 +        ret = 0; 
 + 
 +finish: 
 +        av_free(prefix); 
 +    } 
 + 
 +    return ret; 
 +} 
 + 
 +static enum AVMediaType get_content_type(xmlNodePtr node) 
 +{ 
 +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN; 
 +    int i = 0; 
 +    const char *attr; 
 +    xmlChar *val = NULL; 
 + 
 +    if (node) { 
 +        while (type == AVMEDIA_TYPE_UNKNOWN &amp;&amp; i &lt; 2) { 
 +            attr = (i) ? &#34;mimeType&#34; : &#34;contentType&#34;; 
 +            val = xmlGetProp(node, attr); 
 +            if (val) { 
 +                 (av_stristr((const char *)val, &#34;video&#34;)) { 
 +                 = AVMEDIA_TYPE_VIDEO; 
 +                 else if (av_stristr((const char *)val, &#34;audio&#34;)) { 
 +                 = AVMEDIA_TYPE_AUDIO; 
 +                 
 +                 
 +            } 
 +            i++; 
 +        } 
 +    } 
 +    return type; 
 +} 
 + 
 +static int parse_manifest_segmenturlnode( *s, struct representation *rep, 
 +                 fragmenturl_node, 
 +                 *baseurl_nodes, 
 +                 *rep_id_val, 
 +                 *rep_bandwidth_val) 
 +{ 
 +    xmlChar *initialization_val = NULL; 
 +    xmlChar *media_val = NULL; 
 + 
 +    if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;Initialization&#34;)) { 
 +        initialization_v = xmlGetProp(fragmenturl_node, &#34;sourceURL&#34;); 
 +        if (initialization_val) { 
 +            rep-&gt;ini = av_mallocz(sizeof(struct fragment)); 
 +            if (!rep-&gt;init_section) { 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            rep-&gt;ini = get_content_url(baseurl_nodes, 4, 
 +                 
 +                 
 +                 
 +            if (!rep-&gt;init_section-&gt;url) { 
 +                 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            rep-&gt;ini = -1; 
 +            xmlFree( 
 +        } 
 +    } else if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;SegmentURL&#34;)) { 
 +        media_val = xmlGetProp(fragmenturl_node, &#34;media&#34;); 
 +        if (media_val) { 
 +            struct fragment *seg = av_mallocz(sizeof(struct fragment)); 
 +            if (!seg) { 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            seg-&gt;url = get_content_url(baseurl_nodes, 4, 
 +                 
 +                 
 +                 
 +            if (!seg-&gt;url) { 
 +                 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            seg-&gt;siz = -1; 
 +            dynarray &amp;rep-&gt;n_fragments, seg); 
 +            xmlFree( 
 +        } 
 +    } 
 + 
 +    return 0; 
 +} 
 + 
 +static int parse_manifest_segmenttimeline *s, struct representation *rep, 
 +                 fragment_timeline_node) 
 +{ 
 +    xmlAttrPtr attr = NULL; 
 +    xmlChar *val  = NULL; 
 + 
 +    if (!xmlStrcmp(fragment_timeline_ (const xmlChar *)&#34;S&#34;)) { 
 +        struct timeline *tml = av_mallocz(sizeof(struct timeline)); 
 +        if (!tml) { 
 +            return AVERROR(ENOMEM); 
 +        } 
 +        attr = fragment_timeline_node-&gt;proper 
 +        while (attr) { 
 +            val = xmlGetProp(fragment_timeline_n attr-&gt;name); 
 + 
 +            if (!val) { 
 +                 AV_LOG_WARNING, &#34;parse_manifest_segmenttimelin attr-&gt;name = %s val is NULL\n&#34;, attr-&gt;name); 
 +                 
 +            } 
 + 
 +            if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;t&#34;)) { 
 +                 = (int64_t)strtoll(val, NULL, 10); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;r&#34;)) { 
 +                 =(int64_t) strtoll(val, NULL, 10); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;d&#34;)) { 
 +                 = (int64_t)strtoll(val, NULL, 10); 
 +//                 = (int64_t) strtoll(val, NULL, 10); 
 +            } 
 +            attr = attr-&gt;next; 
 +            xmlFree( 
 +        } 
 +        dynarray_add(&amp;re &amp;rep-&gt;n_timelines, tml); 
 +    } 
 + 
 +    return 0; 
 +} 
 + 
 +static int parse_manifest_representation( *s, const char *url, 
 +                 node, 
 +                 adaptionset_node, 
 +                 mpd_baseurl_node, 
 +                 period_baseurl_node, 
 +                 fragment_template_node, 
 +                 content_component_node, 
 +                 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-&gt;priv_data; 
 +    struct representation *rep = NULL; 
 +    struct fragment *seg = NULL; 
 +    xmlNodePtr representation_segmenttemplate = NULL; 
 +    xmlNodePtr representation_baseurl_node = NULL; 
 +    xmlNodePtr representation_segmentlist_nod = NULL; 
 +    xmlNodePtr fragment_timeline_node = NULL; 
 +    xmlNodePtr fragment_templates_tab[2]; 
 +    xmlChar *duration_val = NULL; 
 +    xmlChar *presentation_timeoffset_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 &#34;id&#34;); 
 +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node &#34;bandwidth&#34;); 
 +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN; 
 + 
 +    // try get information from representation 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(representatio 
 +    // try get information from contentComponen 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(content_compo 
 +    // try get information from adaption set 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(adaptionset_n 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) { 
 +        av_log(s, AV_LOG_VERBOSE, &#34;Parsing &#39;%s&#39; - skipp not supported representation type\n&#34;, url); 
 +    } else if ((type == AVMEDIA_TYPE_VIDEO &amp;&amp; !c-&gt;cur_video) || (type == AVMEDIA_TYPE_AUDIO &amp;&amp; !c-&gt;cur_audio)) { 
 +        // convert selected representation to our internal struct 
 +        rep = av_mallocz(sizeof(struct representation)); 
 +        if (!rep) { 
 +            ret = AVERROR(ENOMEM); 
 +            goto end; 
 +        } 
 +        representation_s = find_child_node_by_name(repres &#34;SegmentTemplate&#34;); 
 +        representation_b = find_child_node_by_name(repres &#34;BaseURL&#34;); 
 +        representation_s = find_child_node_by_name(repres &#34;SegmentList&#34;); 
 + 
 +        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_segmenttemplat || fragment_template_node) { 
 +            fragment = NULL; 
 +            fragment = representation_segmenttemplate 
 +            fragment = fragment_template_node; 
 + 
 +            presenta = get_val_from_nodes_tab(fragmen 2, &#34;presentationTimeOffset&#34;); 
 +            duration = get_val_from_nodes_tab(fragmen 2, &#34;duration&#34;); 
 +            startnum = get_val_from_nodes_tab(fragmen 2, &#34;startNumber&#34;); 
 +            timescal = get_val_from_nodes_tab(fragmen 2, &#34;timescale&#34;); 
 +            initiali = get_val_from_nodes_tab(fragmen 2, &#34;initialization&#34;); 
 +            media_va = get_val_from_nodes_tab(fragmen 2, &#34;media&#34;); 
 + 
 +            if (initialization_val) { 
 +                 = av_mallocz(sizeof(struct fragment)); 
 +                 (!rep-&gt;init_section) { 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +                 
 +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val); 
 +                 (!rep-&gt;init_section-&gt;url) { 
 +                 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +                 
 +                 = -1; 
 +                 
 +            } 
 + 
 +            if (media_val) { 
 +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val); 
 +                 = rep-&gt;url_template; 
 +                 (temp_string) { 
 +                 (av_stristr(temp_string, &#34;$Number&#34;)) { 
 +                 &#34;$Number$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format)); 
 +                 = TMP_URL_TYPE_NUMBER;  /* Number-Based. */ 
 +                 else if (av_stristr(temp_string, &#34;$Time&#34;)) { 
 +                 &#34;$Time$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format)); 
 +                 = TMP_URL_TYPE_TIME; /* Time-Based. */ 
 +                 else { 
 +                 = NULL; 
 +                 
 +                 
 +                 
 +            } 
 + 
 +            if (presentation_timeoffset_val) { 
 +                 = (int64_t) strtoll(presentation_timeoffse NULL, 10); 
 +                 
 +            } 
 +            if (duration_val) { 
 +                 = (int64_t) strtoll(duration_val, NULL, 10); 
 +                 
 +            } 
 +            if (timescale_val) { 
 +                 = (int64_t) strtoll(timescale_val, NULL, 10); 
 +                 
 +            } 
 +            if (startnumber_val) { 
 +                 = (int64_t) strtoll(startnumber_val, NULL, 10); 
 +                 
 +            } 
 + 
 +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;); 
 + 
 +            if (!fragment_timeline_node) 
 +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;); 
 +            if (fragment_timeline_node) { 
 +                 = xmlFirstElementChild(fragment_ 
 +                 (fragment_timeline_node) { 
 +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node); 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +                 = xmlNextElementSibling(fragment 
 +                 
 +            } 
 +        } else if (representation_baseurl_node &amp;&amp; !representation_segmentlist_no { 
 +            seg = av_mallocz(sizeof(struct fragment)); 
 +            if (!seg) { 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +            } 
 +            seg-&gt;url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL); 
 +            if (!seg-&gt;url) { 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +            } 
 +            seg-&gt;siz = -1; 
 +            dynarray &amp;rep-&gt;n_fragments, seg); 
 +        } else if (representation_segmentlist_no { 
 +            // TODO:  www.brendanlong.com www.brendanlong.com 
 +            //  www-itec.uni-klu.ac.at www-itec.uni-klu.ac.at 
 +            xmlNodeP fragmenturl_node = NULL; 
 +            duration = xmlGetProp(representation_segm &#34;duration&#34;); 
 +            timescal = xmlGetProp(representation_segm &#34;timescale&#34;); 
 +            if (duration_val) { 
 +                 = (int64_t) strtoll(duration_val, NULL, 10); 
 +                 
 +            } 
 +            if (timescale_val) { 
 +                 = (int64_t) strtoll(timescale_val, NULL, 10); 
 +                 
 +            } 
 +            fragment = xmlFirstElementChild(represent 
 +            while (fragmenturl_node) { 
 +                 = parse_manifest_segmenturlnode( rep, fragmenturl_node, 
 +                 
 +                 
 +                 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +                 = xmlNextElementSibling(fragment 
 +            } 
 + 
 +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;); 
 + 
 +            if (!fragment_timeline_node) 
 +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;); 
 +            if (fragment_timeline_node) { 
 +                 = xmlFirstElementChild(fragment_ 
 +                 (fragment_timeline_node) { 
 +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node); 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +                 = xmlNextElementSibling(fragment 
 +                 
 +            } 
 +        } else { 
 +            free_rep 
 +            rep = NULL; 
 +            av_log(s AV_LOG_ERROR, &#34;Unknown format of Representation node id[%s] \n&#34;, (const char *)rep_id_val); 
 +        } 
 + 
 +        if (rep) { 
 +            if (rep-&gt;fragment_duration &gt; 0 &amp;&amp; !rep-&gt;fragment_timescale) 
 +                 = 1; 
 +            if (type == AVMEDIA_TYPE_VIDEO) { 
 +                 = video_rep_idx; 
 +                 = rep; 
 +            } else { 
 +                 = audio_rep_idx; 
 +                 = rep; 
 +            } 
 +        } 
 +    } 
 + 
 +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO; 
 +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO; 
 + 
 +end: 
 +    if (rep_id_val) 
 +        xmlFree(rep_id_v 
 +    if (rep_bandwidth_val) 
 +        xmlFree(rep_band 
 + 
 +    return ret; 
 +} 
 + 
 +static int parse_manifest_adaptationset(A *s, const char *url, 
 +                 adaptionset_node, 
 +                 mpd_baseurl_node, 
 +                 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(adaptions 
 +    while (node) { 
 +        if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;SegmentTemplate&#34;)) { 
 +            fragment = node; 
 +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;ContentComponent&#34;)) { 
 +            content_ = node; 
 +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;BaseURL&#34;)) { 
 +            adaption = node; 
 +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;Representation&#34;)) { 
 +            ret = parse_manifest_representation( url, node, 
 +                 
 +                 
 +                 
 +                 
 +                 
 +                 
 +            if (ret &lt; 0) { 
 +                 ret; 
 +            } 
 +        } 
 +        node = xmlNextElementSibling(node); 
 +    } 
 +    return 0; 
 +} 
 + 
 + 
 +static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) 
 +{ 
 +    DASHContext *c = s-&gt;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(&amp;opt &#34;seekable&#34;, &#34;0&#34;, 0); 
 +        // broker prior HTTP options that should be consistent across requests 
 +        av_dict_set(&amp;opt &#34;user-agent&#34;, c-&gt;user_agent, 0); 
 +        av_dict_set(&amp;opt &#34;cookies&#34;, c-&gt;cookies, 0); 
 +        av_dict_set(&amp;opt &#34;headers&#34;, c-&gt;headers, 0); 
 + 
 +        ret = avio_open2(&amp;in, url, AVIO_FLAG_READ, c-&gt;interrupt_callback, &amp;opts); 
 +        av_dict_free(&amp;op 
 +        if (ret &lt; 0) 
 +            return ret; 
 +    } 
 + 
 +    if (av_opt_get(in, &#34;location&#34;, AV_OPT_SEARCH_CHILDREN, &amp;new_url) &gt;= 0) { 
 +        c-&gt;base_url = av_strdup(new_url); 
 +    } else { 
 +        c-&gt;base_url = av_strdup(url); 
 +    } 
 + 
 +    filesize = avio_size(in); 
 +    if (filesize &lt;= 0) { 
 +        filesize = 8 * 1024; 
 +    } 
 + 
 +    buffer = av_mallocz(filesize); 
 +    if (!buffer) { 
 +        return AVERROR(ENOMEM); 
 +    } 
 + 
 +    filesize = avio_read(in, buffer, filesize); 
 +    if (filesize &gt; 0) { 
 +        LIBXML_TEST_VERS 
 + 
 +        doc = xmlReadMemory(buffer, filesize, c-&gt;base_url, NULL, 0); 
 +        root_element = xmlDocGetRootElement(doc); 
 +        node = root_element; 
 + 
 +        if (!node) { 
 +            ret = AVERROR_INVALIDDATA; 
 +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing root node\n&#34;, url); 
 +            goto cleanup; 
 +        } 
 + 
 +        if (node-&gt;type != XML_ELEMENT_NODE || 
 +            xmlStrcm (const xmlChar *)&#34;MPD&#34;)) { 
 +            ret = AVERROR_INVALIDDATA; 
 +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - wrong root node name[%s] type[%d]\n&#34;, url, node-&gt;name, (int)node-&gt;type); 
 +            goto cleanup; 
 +        } 
 + 
 +        val = xmlGetProp(node, &#34;type&#34;); 
 +        if (!val) { 
 +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing type attrib\n&#34;, url); 
 +            ret = AVERROR_INVALIDDATA; 
 +            goto cleanup; 
 +        } 
 +        if (!xmlStrcmp(val, (const xmlChar *)&#34;dynamic&#34;)) 
 +            c-&gt;is_li = 1; 
 +        xmlFree(val); 
 + 
 +        attr = node-&gt;properties; 
 +        while (attr) { 
 +            val = xmlGetProp(node, attr-&gt;name); 
 + 
 +            if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;availabilityStartTime&#34;)) { 
 +                 = get_utc_date_time_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;publishTime&#34;)) { 
 +                 = get_utc_date_time_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;minimumUpdatePeriod&#34;)) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;timeShiftBufferDepth&#34;)) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;minBufferTime&#34;)) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;suggestedPresentationDelay&#34; { 
 +                 = get_duration_insec(s, (const char *)val); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;mediaPresentationDuration&#34;) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +            } 
 +            attr = attr-&gt;next; 
 +            xmlFree( 
 +        } 
 + 
 +        mpd_baseurl_node = find_child_node_by_name(node, &#34;BaseURL&#34;); 
 + 
 +        // at now we can handle only one period, with the longest duration 
 +        node = xmlFirstElementChild(node); 
 +        while (node) { 
 +            if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;Period&#34;)) { 
 +                 = 0; 
 +                 = 0; 
 +                 = node-&gt;properties; 
 +                 (attr) { 
 +                 = xmlGetProp(node, attr-&gt;name); 
 +                 (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;duration&#34;)) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +                 else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;start&#34;)) { 
 +                 = get_duration_insec(s, (const char *)val); 
 +                 
 +                 = attr-&gt;next; 
 +                 
 +                 
 +                 ((perdiod_duration_sec) &gt;= (c-&gt;period_duration)) { 
 +                 = node; 
 +                 = perdiod_duration_sec; 
 +                 = perdiod_start_sec; 
 +                 (c-&gt;period_start &gt; 0) 
 +                 = c-&gt;period_duration; 
 +                 
 +            } 
 +            node = xmlNextElementSibling(node); 
 +        } 
 +        if (!period_node) { 
 +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing Period node\n&#34;, url); 
 +            ret = AVERROR_INVALIDDATA; 
 +            goto cleanup; 
 +        } 
 + 
 +        adaptionset_node = xmlFirstElementChild(period_no 
 +        while (adaptionset_node) { 
 +            if (!xmlStrcmp(adaptionset_node-&gt; (const xmlChar *)&#34;BaseURL&#34;)) { 
 +                 = adaptionset_node; 
 +            } else if (!xmlStrcmp(adaptionset_node-&gt; (const xmlChar *)&#34;AdaptationSet&#34;)) { 
 +                 url, adaptionset_node, mpd_baseurl_node, period_baseurl_node); 
 +            } 
 +            adaption = xmlNextElementSibling(adaption 
 +        } 
 +        if (c-&gt;cur_video) { 
 +            c-&gt;cur_v = video_rep_idx; 
 +            c-&gt;cur_v = 1; 
 +            av_log(s AV_LOG_VERBOSE, &#34;rep_idx[%d]\n&#34;, (int)c-&gt;cur_video-&gt;rep_idx); 
 +            av_log(s AV_LOG_VERBOSE, &#34;rep_count[%d]\n&#34;, (int)video_rep_idx); 
 +        } 
 +        if (c-&gt;cur_audio) { 
 +            c-&gt;cur_a = audio_rep_idx; 
 +        } 
 +cleanup: 
 +        /*free the document */ 
 +        xmlFreeDoc(doc); 
 +        xmlCleanupParser 
 +    } else { 
 +        av_log(s, AV_LOG_ERROR, &#34;Unable to read to offset &#39;%s&#39;\n&#34;, 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(AVFormatContex *s, struct representation *pls) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    int64_t num = 0; 
 +    int64_t start_time_offset = 0; 
 + 
 +    if (c-&gt;is_live) { 
 +        if (pls-&gt;n_fragments) { 
 +            num = pls-&gt;first_seq_no; 
 +        } else if (pls-&gt;n_timelines) { 
 +            start_ti = get_segment_start_time_based_o 0xFFFFFFFF) - pls-&gt;timelines[pls-&gt;first_seq_ // total duration of playlist 
 +            if (start_time_offset &lt; 60 * pls-&gt;fragment_timescale) 
 +                 = 0; 
 +            else 
 +                 = start_time_offset - 60 * pls-&gt;fragment_timescale; 
 + 
 +            num = calc_next_seg_no_from_timeline pls-&gt;timelines[pls-&gt;first_seq_ + start_time_offset); 
 +            if (num == -1) 
 +                 = pls-&gt;first_seq_no; 
 +        } else { 
 +            if (pls-&gt;presentation_timeoffset) { 
 +                 = pls-&gt;presentation_timeoffset * pls-&gt;fragment_timescale / pls-&gt;fragment_duration; 
 +            } else if (c-&gt;publish_time &gt; 0) { 
 +                 = pls-&gt;first_seq_no + (((c-&gt;publish_time - c-&gt;availability_start_time) - c-&gt;suggested_presentation_dela * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration; 
 +            } else { 
 +                 = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time) - c-&gt;suggested_presentation_dela * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration; 
 +            } 
 +        } 
 +    } else { 
 +        num = pls-&gt;first_seq_no; 
 +    } 
 +    return num; 
 +} 
 + 
 +static int64_t calc_min_seg_no(AVFormatContex *s, struct representation *pls) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    int64_t num = 0; 
 + 
 +    if (c-&gt;is_live &amp;&amp; pls-&gt;fragment_duration) { 
 +        num = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time) - c-&gt;time_shift_buffer_depth) * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration; 
 +    } else { 
 +        num = pls-&gt;first_seq_no; 
 +    } 
 +    return num; 
 +} 
 + 
 +static int64_t calc_max_seg_no(struct representation *pls) 
 +{ 
 +    DASHContext *c = pls-&gt;parent-&gt;priv_data; 
 +    int64_t num = 0; 
 + 
 +    if (pls-&gt;n_fragments) { 
 +        num = pls-&gt;first_seq_no + pls-&gt;n_fragments - 1; 
 +    } else if (pls-&gt;n_timelines) { 
 +        int i = 0; 
 +        num = pls-&gt;first_seq_no + pls-&gt;n_timelines - 1; 
 +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +            num += pls-&gt;timelines[i]-&gt;r; 
 +        } 
 +    } else if (c-&gt;is_live) { 
 +        num = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time)) * pls-&gt;fragment_timescale)  / pls-&gt;fragment_duration; 
 +    } else { 
 +        num = pls-&gt;first_seq_no + (c-&gt;media_presentation_duratio * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration; 
 +    } 
 + 
 +    return num; 
 +} 
 + 
 +static void move_timelines(struct representation *rep_src, struct representation *rep_dest) 
 +{ 
 +    if (rep_dest &amp;&amp; rep_src ) { 
 +        free_timelines_l 
 +        rep_dest-&gt;timeli    = rep_src-&gt;timelines; 
 +        rep_dest-&gt;n_time  = rep_src-&gt;n_timelines; 
 +        rep_dest-&gt;first_ = rep_src-&gt;first_seq_no; 
 +        rep_dest-&gt;last_s = calc_max_seg_no(rep_dest); 
 +        rep_src-&gt;timelin = NULL; 
 +        rep_src-&gt;n_timel = 0; 
 +        rep_dest-&gt;cur_se = rep_src-&gt;cur_seq_no; 
 +    } 
 +} 
 + 
 +static void move_segments(struct representation *rep_src, struct representation *rep_dest) 
 +{ 
 +    if (rep_dest &amp;&amp; rep_src ) { 
 +        free_fragment_li 
 +        if (rep_src-&gt;start_number &gt; (rep_dest-&gt;start_number + rep_dest-&gt;n_fragments)) 
 +            rep_dest = 0; 
 +        else 
 +            rep_dest += rep_src-&gt;start_number - rep_dest-&gt;start_number; 
 +        rep_dest-&gt;fragme    = rep_src-&gt;fragments; 
 +        rep_dest-&gt;n_frag  = rep_src-&gt;n_fragments; 
 +        rep_dest-&gt;parent  = rep_src-&gt;parent; 
 +        rep_dest-&gt;last_s = calc_max_seg_no(rep_dest); 
 +        rep_src-&gt;fragmen = NULL; 
 +        rep_src-&gt;n_fragm = 0; 
 +    } 
 +} 
 + 
 + 
 +static int refresh_manifest(AVFormatConte *s) 
 +{ 
 + 
 +    int ret = 0; 
 +    DASHContext *c = s-&gt;priv_data; 
 + 
 +    // save current context 
 +    struct representation *cur_video =  c-&gt;cur_video; 
 +    struct representation *cur_audio =  c-&gt;cur_audio; 
 +    char *base_url = c-&gt;base_url; 
 + 
 +    c-&gt;base_url = NULL; 
 +    c-&gt;cur_video = NULL; 
 +    c-&gt;cur_audio = NULL; 
 +    ret = parse_manifest(s, s-&gt;filename, NULL); 
 +    if (ret) 
 +        goto finish; 
 + 
 +    if (cur_video &amp;&amp; cur_video-&gt;timelines || cur_audio &amp;&amp; cur_audio-&gt;timelines) { 
 +        // calc current time 
 +        int64_t currentVideoTime = 0; 
 +        int64_t currentAudioTime = 0; 
 +        if (cur_video &amp;&amp; cur_video-&gt;timelines) 
 +            currentV = get_segment_start_time_based_o cur_video-&gt;cur_seq_no) / cur_video-&gt;fragment_timescale; 
 +        if (cur_audio &amp;&amp; cur_audio-&gt;timelines) 
 +            currentA = get_segment_start_time_based_o cur_audio-&gt;cur_seq_no) / cur_audio-&gt;fragment_timescale; 
 +        // update segments 
 +        if (cur_video &amp;&amp; cur_video-&gt;timelines) { 
 +            c-&gt;cur_v = calc_next_seg_no_from_timeline currentVideoTime * cur_video-&gt;fragment_timescale - 1); 
 +            if (c-&gt;cur_video-&gt;cur_seq_no &gt;= 0) { 
 +                 cur_video); 
 +            } 
 +        } 
 +        if (cur_audio &amp;&amp; cur_audio-&gt;timelines) { 
 +            c-&gt;cur_a = calc_next_seg_no_from_timeline currentAudioTime * cur_audio-&gt;fragment_timescale - 1); 
 +            if (c-&gt;cur_audio-&gt;cur_seq_no &gt;= 0) { 
 +               mo cur_audio); 
 +            } 
 +        } 
 +    } 
 +    if (cur_video &amp;&amp; cur_video-&gt;fragments) { 
 +        move_segments(c- cur_video); 
 +    } 
 +    if (cur_audio &amp;&amp; cur_audio-&gt;fragments) { 
 +        move_segments(c- cur_audio); 
 +    } 
 + 
 +finish: 
 +    // restore context 
 +    if (c-&gt;base_url) 
 +        av_free(base_url 
 +    else 
 +        c-&gt;base_url  = base_url; 
 +    if (c-&gt;cur_audio) 
 +        free_representat 
 +    if (c-&gt;cur_video) 
 +        free_representat 
 +    c-&gt;cur_audio = cur_audio; 
 +    c-&gt;cur_video = cur_video; 
 +    return ret; 
 +} 
 + 
 +static struct fragment *get_current_fragment(struct representation *pls) 
 +{ 
 +    int64_t min_seq_no = 0; 
 +    int64_t max_seq_no = 0; 
 +    struct fragment *seg = NULL; 
 +    struct fragment *seg_ptr = NULL; 
 +    DASHContext *c = pls-&gt;parent-&gt;priv_data; 
 + 
 +    while (( !ff_check_interrupt(c-&gt;interru pls-&gt;n_fragments &gt; 0)) { 
 +        if (pls-&gt;cur_seq_no &lt; pls-&gt;n_fragments) { 
 +            seg_ptr = pls-&gt;fragments[pls-&gt;cur_seq_no 
 +            seg = av_mallocz(sizeof(struct fragment)); 
 +            if (!seg) { 
 +                 NULL; 
 +            } 
 +            seg-&gt;url = av_strdup(seg_ptr-&gt;url); 
 +            if (!seg-&gt;url) { 
 +                 
 +                 NULL; 
 +            } 
 +            seg-&gt;siz = seg_ptr-&gt;size; 
 +            seg-&gt;url = seg_ptr-&gt;url_offset; 
 +            return seg; 
 +        } else if (c-&gt;is_live) { 
 +            av_uslee 
 +            refresh_ 
 +        } else { 
 +            break; 
 +        } 
 +    } 
 +    if (c-&gt;is_live) { 
 +        while (!ff_check_interrupt(c-&gt;interr { 
 +            min_seq_ = calc_min_seg_no(pls-&gt;parent, pls); 
 +            max_seq_ = calc_max_seg_no(pls); 
 + 
 +            if (pls-&gt;cur_seq_no &lt;= min_seq_no) { 
 +                 AV_LOG_VERBOSE, &#34;old fragment: cur[%&#34;PRId64&#34;] min[%&#34;PRId64&#34;] max[%&#34;PRId64&#34;], playlist %d\n&#34;, (int64_t)pls-&gt;cur_seq_no, min_seq_no, max_seq_no, (int)pls-&gt;rep_idx); 
 +                 (c-&gt;is_live &amp;&amp; (pls-&gt;timelines || pls-&gt;fragments)) { 
 +                 
 +                 
 +                 = calc_cur_seg_no(pls-&gt;parent, pls); 
 +            } else if (pls-&gt;cur_seq_no &gt; max_seq_no) { 
 +                 AV_LOG_VERBOSE, &#34;new fragment: min[%&#34;PRId64&#34;] max[%&#34;PRId64&#34;], playlist %d\n&#34;, min_seq_no, max_seq_no, (int)pls-&gt;rep_idx); 
 +                 
 +                 (c-&gt;is_live &amp;&amp; (pls-&gt;timelines || pls-&gt;fragments)) { 
 +                 
 +                 
 +                 
 +            } 
 +            break; 
 +        } 
 +        seg = av_mallocz(sizeof(struct fragment)); 
 +        if (!seg) { 
 +            return NULL; 
 +        } 
 +    } else if (pls-&gt;cur_seq_no &lt;= pls-&gt;last_seq_no) { 
 +        seg = av_mallocz(sizeof(struct fragment)); 
 +        if (!seg) { 
 +            return NULL; 
 +        } 
 +    } 
 +    if (seg) { 
 +        if (pls-&gt;tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) { 
 +            int64_t val = pls-&gt;tmp_url_type == TMP_URL_TYPE_NUMBER ? pls-&gt;cur_seq_no : get_segment_start_time_based_o pls-&gt;cur_seq_no); 
 +            int size = snprintf(NULL, 0, pls-&gt;url_template_format, val); // calc needed buffer size 
 + 
 +            if (size &gt; 0) { 
 +                 *tmp_val = av_mallocz(size + 1); 
 +                 size+1, pls-&gt;url_template_format, val); 
 +                 = av_strireplace(pls-&gt;url_templa pls-&gt;url_template_pattern, tmp_val); 
 +                 
 +            } 
 +        } 
 + 
 +        if (!seg-&gt;url) { 
 +            av_log(p AV_LOG_ERROR, &#34;Unable to resolve template url &#39;%s&#39;\n&#34;, pls-&gt;url_template); 
 +            seg-&gt;url = av_strdup(pls-&gt;url_template); 
 +            if (!seg-&gt;url) { 
 +                 NULL; 
 +            } 
 +        } 
 + 
 +        seg-&gt;size = -1; 
 +    } 
 + 
 +    return seg; 
 +} 
 + 
 +enum ReadFromURLMode { 
 +    READ_NORMAL, 
 +    READ_COMPLETE, 
 +}; 
 + 
 +static int read_from_url(struct representation *pls, struct fragment *seg, 
 +                 *buf, int buf_size, 
 +                 ReadFromURLMode mode) 
 +{ 
 +    int ret; 
 + 
 +    /* limit read if the fragment was only a part of a file */ 
 +    if (seg-&gt;size &gt;= 0) 
 +        buf_size = FFMIN(buf_size, pls-&gt;cur_seg_size - pls-&gt;cur_seg_offset); 
 + 
 +    if (mode == READ_COMPLETE) { 
 +        ret = avio_read(pls-&gt;input, buf, buf_size); 
 +        if (ret &lt; buf_size) { 
 +            av_log(p AV_LOG_WARNING, &#34;Could not read complete fragment.\n&#34;); 
 +        } 
 +    } else { 
 +        ret = avio_read(pls-&gt;input, buf, buf_size); 
 +    } 
 +    if (ret &gt; 0) 
 +        pls-&gt;cur_seg_off += 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(&amp;opts, &#34;user-agent&#34;, c-&gt;user_agent, 0); 
 +    av_dict_set(&amp;opts, &#34;cookies&#34;, c-&gt;cookies, 0); 
 +    av_dict_set(&amp;opts, &#34;headers&#34;, c-&gt;headers, 0); 
 +    if (c-&gt;is_live) { 
 +        av_dict_set(&amp;opt &#34;seekable&#34;, &#34;0&#34;, 0); 
 +    } 
 + 
 +    if (seg-&gt;size &gt;= 0) { 
 +        /* try to restrict the HTTP request to the part we want 
 +         * (if this is in fact a HTTP request) */ 
 +        av_dict_set_int( &#34;offset&#34;, seg-&gt;url_offset, 0); 
 +        av_dict_set_int( &#34;end_offset&#34;, seg-&gt;url_offset + seg-&gt;size, 0); 
 +    } 
 + 
 +    ff_make_absolute_url(url MAX_URL_SIZE, c-&gt;base_url, seg-&gt;url); 
 +    av_log(pls-&gt;parent, AV_LOG_VERBOSE, &#34;DASH request for url &#39;%s&#39;, offset %&#34;PRId64&#34;, playlist %d\n&#34;, 
 +           url, seg-&gt;url_offset, pls-&gt;rep_idx); 
 +    ret = open_url(pls-&gt;parent, &amp;pls-&gt;input, url, c-&gt;avio_opts, opts, NULL); 
 +    if (ret &lt; 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 &amp;&amp; seg-&gt;url_offset) { 
 +        int64_t seekret = avio_seek(pls-&gt;input, seg-&gt;url_offset, SEEK_SET); 
 +        if (seekret &lt; 0) { 
 +            av_log(p AV_LOG_ERROR, &#34;Unable to seek to offset %&#34;PRId64&#34; of DASH fragment &#39;%s&#39;\n&#34;, seg-&gt;url_offset, seg-&gt;url); 
 +            ret = (int) seekret; 
 +            ff_forma &amp;pls-&gt;input); 
 +        } 
 +    } 
 + 
 +cleanup: 
 +    av_dict_free(&amp;opts); 
 +    pls-&gt;cur_seg_offset = 0; 
 +    pls-&gt;cur_seg_size = seg-&gt;size; 
 +    return ret; 
 +} 
 + 
 +static int update_init_section(struct representation *pls) 
 +{ 
 +    static const int max_init_section_size = 1024*1024; 
 +    DASHContext *c = pls-&gt;parent-&gt;priv_data; 
 +    int64_t sec_size = 0; 
 +    int64_t urlsize = 0; 
 +    int ret = 0; 
 + 
 +    /* read init section only once per representation */ 
 +    if (!pls-&gt;init_section || pls-&gt;init_sec_buf) { 
 +        return 0; 
 +    } 
 + 
 +    ret = open_input(c, pls, pls-&gt;init_section); 
 +    if (ret &lt; 0) { 
 +        av_log(pls-&gt;pare AV_LOG_WARNING, &#34;Failed to open an initialization section in playlist %d\n&#34;, pls-&gt;rep_idx); 
 +        return ret; 
 +    } 
 + 
 +    if (pls-&gt;init_section-&gt;size &gt;= 0) { 
 +        sec_size = pls-&gt;init_section-&gt;size; 
 +    } else if ((urlsize = avio_size(pls-&gt;input)) &gt;= 0) { 
 +        sec_size = urlsize; 
 +    } else { 
 +        sec_size = max_init_section_size; 
 +    } 
 +    av_log(pls-&gt;parent, AV_LOG_DEBUG, &#34;Downloading an initialization section of size %&#34;PRId64&#34;\n&#34;, sec_size); 
 +    sec_size = FFMIN(sec_size, max_init_section_size); 
 +    av_fast_malloc(&amp;pls-&gt;ini &amp;pls-&gt;init_sec_buf_size, sec_size); 
 +    ret = read_from_url(pls, pls-&gt;init_section, pls-&gt;init_sec_buf, pls-&gt;init_sec_buf_size, READ_COMPLETE); 
 +    ff_format_io_close(pls-&gt; &amp;pls-&gt;input); 
 +    if (ret &lt; 0) 
 +        return ret; 
 + 
 +    if (pls-&gt;fix_multiple_stsd_order &amp;&amp; pls-&gt;rep_idx &gt; 0) { 
 +        uint8_t **stsd_entries = NULL; 
 +        int *stsd_entries_size = NULL; 
 +        int i = 4; 
 + 
 +        while (i &lt;= (ret - 4)) { 
 +            // find start stsd atom 
 +            if (!memcmp(pls-&gt;init_sec_buf + i, &#34;stsd&#34;, 4)) { 
 +                 1B version 
 +                 3B flags 
 +                 4B num of entries */ 
 +                 stsd_first_offset = i + 8; 
 +                 stsd_offset = 0; 
 +                 j = 0; 
 +                 stsd_count = AV_RB32(pls-&gt;init_sec_buf + stsd_first_offset); 
 +                 += 4; 
 +                 (stsd_count != pls-&gt;rep_count) { 
 +                 
 +                 
 +                 
 +                 find all stsd entries 
 +                 = av_mallocz_array(stsd_count, sizeof(*stsd_entries)); 
 +                 = av_mallocz_array(stsd_count, sizeof(*stsd_entries_size)); 
 +                 (j = 0; j &lt; stsd_count; ++j) { 
 +                 4B - size 
 +                 4B - format */ 
 +                 = AV_RB32(pls-&gt;init_sec_buf + stsd_first_offset + stsd_offset); 
 +                 = av_malloc(stsd_entries_size[j] 
 +                 pls-&gt;init_sec_buf + stsd_first_offset + stsd_offset, stsd_entries_size[j]); 
 +                 += stsd_entries_size[j]; 
 +                 
 +                 reorder stsd entries 
 +                 as first put stsd entry for current representation 
 +                 = pls-&gt;rep_idx; 
 +                 = stsd_first_offset; 
 +                 + stsd_offset, stsd_entries[j], stsd_entries_size[j]); 
 +                 += stsd_entries_size[j]; 
 +                 (j = 0; j &lt; stsd_count; ++j) { 
 +                 (j != pls-&gt;rep_idx) { 
 +                 + stsd_offset, stsd_entries[j], stsd_entries_size[j]); 
 +                 += stsd_entries_size[j]; 
 +                 
 +                 
 +                 
 +                 
 +                 
 +                 
 +            } 
 +            i++; 
 +        } 
 +    } 
 + 
 +    av_log(pls-&gt;parent, AV_LOG_TRACE, &#34;pls[%p] init section size[%d]\n&#34;, pls, (int)ret); 
 +    pls-&gt;init_sec_data_len = ret; 
 +    pls-&gt;init_sec_buf_read_o = 0; 
 + 
 +    return 0; 
 +} 
 + 
 +static int64_t seek_data(void *opaque, int64_t offset, int whence) 
 +{ 
 +    struct representation *v = opaque; 
 +    if (v-&gt;n_fragments &amp;&amp; !v-&gt;init_sec_data_len) { 
 +        return avio_seek(v-&gt;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-&gt;parent-&gt;priv_data; 
 + 
 +restart: 
 +    if (!v-&gt;input) { 
 +        free_fragment(&amp;v 
 +        v-&gt;cur_seg = get_current_fragment(v); 
 +        if (!v-&gt;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-&gt;cur_seg); 
 +        if (ret &lt; 0) { 
 +            if (ff_check_interrupt(c-&gt;interru { 
 +                 end; 
 +                 = AVERROR_EXIT; 
 +            } 
 +            av_log(v AV_LOG_WARNING, &#34;Failed to open fragment of playlist %d\n&#34;, v-&gt;rep_idx); 
 +            v-&gt;cur_s 
 +            goto restart; 
 +        } 
 +    } 
 + 
 +    if (v-&gt;init_sec_buf_read_offset &lt; v-&gt;init_sec_data_len) { 
 +        /* Push init section out first before first actual fragment */ 
 +        int copy_size = FFMIN(v-&gt;init_sec_data_len - v-&gt;init_sec_buf_read_offset, buf_size); 
 +        memcpy(buf, v-&gt;init_sec_buf, copy_size); 
 +        v-&gt;init_sec_buf_ += copy_size; 
 +        ret = copy_size; 
 +        goto end; 
 +    } 
 + 
 +    /* check the v-&gt;cur_seg, if it is null, get current and double check if the new v-&gt;cur_seg*/ 
 +    if (!v-&gt;cur_seg) { 
 +        v-&gt;cur_seg = get_current_fragment(v); 
 +    } 
 +    if (!v-&gt;cur_seg) { 
 +        ret = AVERROR_EOF; 
 +        goto end; 
 +    } 
 +    ret = read_from_url(v, v-&gt;cur_seg, buf, buf_size, READ_NORMAL); 
 +    if (ret &gt; 0) 
 +        goto end; 
 + 
 +    if (!v-&gt;is_restart_needed) 
 +        v-&gt;cur_seq_no++; 
 +    v-&gt;is_restart_needed = 1; 
 + 
 +/* 
 +    ff_format_io_close(v-&gt;pa &amp;v-&gt;input); 
 +    v-&gt;cur_seq_no++; 
 +    goto restart; 
 +*/ 
 +end: 
 +    return ret; 
 +} 
 + 
 +static int save_avio_options(AVFormatCont *s) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    const char *opts[] = { &#34;headers&#34;, &#34;user_agent&#34;, &#34;user-agent&#34;, &#34;cookies&#34;, NULL }, **opt = opts; 
 +    uint8_t *buf = NULL; 
 +    int ret = 0; 
 + 
 +    while (*opt) { 
 +        if (av_opt_get(s-&gt;pb, *opt, AV_OPT_SEARCH_CHILDREN, &amp;buf) &gt;= 0) { 
 +            if (buf[0] != &#39;\0&#39;) { 
 +                 = av_dict_set(&amp;c-&gt;avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL); 
 +                 (ret &lt; 0) 
 +                 ret; 
 +            } 
 +        } 
 +        opt++; 
 +    } 
 + 
 +    return ret; 
 +} 
 + 
 +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url, 
 +                 flags, AVDictionary **opts) 
 +{ 
 +    av_log(s, AV_LOG_ERROR, 
 +           &#34;A DASH playlist item &#39;%s&#39; referred to an external file &#39;%s&#39;. &#34; 
 +           &#34;Opening this file was forbidden for security reasons\n&#34;, 
 +           s-&gt;filenam url); 
 +    return AVERROR(EPERM); 
 +} 
 + 
 +static int reopen_demux_for_component(AVF *s, struct representation *pls) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    AVInputFormat *in_fmt = NULL; 
 +    AVDictionary  *in_fmt_opts = NULL; 
 +    uint8_t *avio_ctx_buffer  = NULL; 
 +    int ret = 0; 
 + 
 +    if (pls-&gt;ctx) { 
 +        /* note: the internal buffer could have changed, and be != avio_ctx_buffer */ 
 +        av_freep(&amp;pls-&gt;p 
 +        memset(&amp;pls-&gt;pb, 0x00, sizeof(AVIOContext)); 
 +        pls-&gt;ctx-&gt;pb = NULL; 
 +        avformat_close_i 
 +        pls-&gt;ctx = NULL; 
 +    } 
 +    if (!(pls-&gt;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_co 
 +        pls-&gt;ctx = NULL; 
 +        goto fail; 
 +    } 
 +    if (c-&gt;is_live) { 
 +        ffio_init_contex avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL); 
 +    } else { 
 +        ffio_init_contex avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data); 
 +    } 
 +    pls-&gt;pb.seekable = 0; 
 + 
 +    if ((ret = ff_copy_whiteblacklists(pls-&gt;c s)) &lt; 0) 
 +        goto fail; 
 + 
 +    pls-&gt;ctx-&gt;flags = AVFMT_FLAG_CUSTOM_IO; 
 +    pls-&gt;ctx-&gt;probesize = 1024 * 4; 
 +    pls-&gt;ctx-&gt;max_analyze_du = 4 * AV_TIME_BASE; 
 +    ret = av_probe_input_buffer(&amp;pls-&gt;pb &amp;in_fmt, &#34;&#34;, NULL, 0, 0); 
 +    if (ret &lt; 0) { 
 +        av_log(s, AV_LOG_ERROR, &#34;Error when loading first fragment, playlist %d\n&#34;, (int)pls-&gt;rep_idx); 
 +        avformat_free_co 
 +        pls-&gt;ctx = NULL; 
 +        goto fail; 
 +    } 
 + 
 +    pls-&gt;ctx-&gt;pb = &amp;pls-&gt;pb; 
 +    pls-&gt;ctx-&gt;io_open  = nested_io_open; 
 + 
 +    // provide additional information from mpd if available 
 +    ret = avformat_open_input(&amp;pls-&gt;ctx, &#34;&#34;, in_fmt, &amp;in_fmt_opts); //pls-&gt;init_section-&gt;url 
 +    av_dict_free(&amp;in_fmt_opt 
 +    if (ret &lt; 0) 
 +        goto fail; 
 +    if (pls-&gt;n_fragments) { 
 +        ret = avformat_find_stream_info(pls- NULL); 
 +        if (ret &lt; 0) 
 +            goto fail; 
 +    } 
 + 
 +fail: 
 +    return ret; 
 +} 
 + 
 +static int open_demux_for_component(AVFor *s, struct representation *pls) 
 +{ 
 +    int ret = 0; 
 +    int i; 
 + 
 +    pls-&gt;parent = s; 
 +    pls-&gt;cur_seq_no  = calc_cur_seg_no(s, pls); 
 +    pls-&gt;last_seq_no = calc_max_seg_no(pls); 
 + 
 +    ret = reopen_demux_for_component(s, pls); 
 +    if (ret &lt; 0) { 
 +        goto fail; 
 +    } 
 +    for (i = 0; i &lt; pls-&gt;ctx-&gt;nb_streams; i++) { 
 +        AVStream *st = avformat_new_stream(s, NULL); 
 +        AVStream *ist = pls-&gt;ctx-&gt;streams[i]; 
 +        if (!st) { 
 +            ret = AVERROR(ENOMEM); 
 +            goto fail; 
 +        } 
 +        st-&gt;id = i; 
 +        avcodec_paramete pls-&gt;ctx-&gt;streams[i]-&gt;codecpar 
 +        avpriv_set_pts_i ist-&gt;pts_wrap_bits, ist-&gt;time_base.num, ist-&gt;time_base.den); 
 +    } 
 + 
 +    return 0; 
 +fail: 
 +    return ret; 
 +} 
 + 
 +static int dash_read_header(AVFormatConte *s) 
 +{ 
 +    void *u = (s-&gt;flags &amp; AVFMT_FLAG_CUSTOM_IO) ? NULL : s-&gt;pb; 
 +    DASHContext *c = s-&gt;priv_data; 
 +    int ret = 0; 
 +    int stream_index = 0; 
 + 
 +    c-&gt;interrupt_callback = &amp;s-&gt;interrupt_callback; 
 +    // if the URL context is good, read important options we must broker later 
 +    if (u) { 
 +        update_options(&amp; &#34;user-agent&#34;, u); 
 +        update_options(&amp; &#34;cookies&#34;, u); 
 +        update_options(&amp; &#34;headers&#34;, u); 
 +    } 
 + 
 +    if ((ret = parse_manifest(s, s-&gt;filename, s-&gt;pb)) &lt; 0) 
 +        goto fail; 
 + 
 +    if ((ret = save_avio_options(s)) &lt; 0) 
 +        goto fail; 
 + 
 +    /* If this isn&#39;t a live stream, fill the total duration of the 
 +     * stream. */ 
 +    if (!c-&gt;is_live) { 
 +        s-&gt;duration = (int64_t) c-&gt;media_presentation_duration * AV_TIME_BASE; 
 +    } 
 + 
 +    /* Open the demuxer for curent video and current audio components if available */ 
 +    if (!ret &amp;&amp; c-&gt;cur_video) { 
 +        ret = open_demux_for_component(s, c-&gt;cur_video); 
 +        if (!ret) { 
 +            c-&gt;cur_v = stream_index; 
 +            ++stream 
 +        } else { 
 +            free_rep 
 +            c-&gt;cur_v = NULL; 
 +        } 
 +    } 
 + 
 +    if (!ret &amp;&amp; c-&gt;cur_audio) { 
 +        ret = open_demux_for_component(s, c-&gt;cur_audio); 
 +        if (!ret) { 
 +            c-&gt;cur_a = stream_index; 
 +            ++stream 
 +        } else { 
 +            free_rep 
 +            c-&gt;cur_a = 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-&gt;cur_video) { 
 +            av_progr 0, c-&gt;cur_video-&gt;stream_index); 
 +        } 
 +        if (c-&gt;cur_audio) { 
 +            av_progr 0, c-&gt;cur_audio-&gt;stream_index); 
 +        } 
 +    } 
 + 
 +    return 0; 
 +fail: 
 +    return ret; 
 +} 
 + 
 +static int dash_read_packet(AVFormatConte *s, AVPacket *pkt) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    int ret = 0; 
 +    struct representation *cur = NULL; 
 + 
 +    if (!c-&gt;cur_audio &amp;&amp; !c-&gt;cur_video ) { 
 +        return AVERROR_INVALIDDATA; 
 +    } 
 +    if (c-&gt;cur_audio &amp;&amp; !c-&gt;cur_video) { 
 +        cur = c-&gt;cur_audio; 
 +    } else if (!c-&gt;cur_audio &amp;&amp; c-&gt;cur_video) { 
 +        cur = c-&gt;cur_video; 
 +    } else if (c-&gt;cur_video-&gt;cur_timestamp &lt; c-&gt;cur_audio-&gt;cur_timestamp) { 
 +        cur = c-&gt;cur_video; 
 +    } else { 
 +        cur = c-&gt;cur_audio; 
 +    } 
 + 
 +    if (cur-&gt;ctx) { 
 +        while (!ff_check_interrupt(c-&gt;interr &amp;&amp; !ret) { 
 +            ret = av_read_frame(cur-&gt;ctx, pkt); 
 +            if (ret &gt;= 0) { 
 +                 If we got a packet, return it */ 
 +                 = av_rescale(pkt-&gt;pts, (int64_t)cur-&gt;ctx-&gt;streams[0]- * 90000, cur-&gt;ctx-&gt;streams[0]-&gt;time_bas 
 +                 = cur-&gt;stream_index; 
 +                 0; 
 +            } 
 +            if (cur-&gt;is_restart_needed) { 
 +                 (!ff_check_interrupt(c-&gt;interr { 
 +                 = 0; 
 +                 = 0; 
 +                 (cur-&gt;input) 
 +                 &amp;cur-&gt;input); 
 +                 = reopen_demux_for_component(s, cur); 
 +                 (c-&gt;is_live &amp;&amp; ret) { 
 +                 
 +                 
 +                 
 +                 
 +                 
 +                 = 0; 
 +            } 
 + 
 +        } 
 +    } 
 +    return AVERROR_EOF; 
 +} 
 + 
 +static int dash_close(AVFormatContext *s) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    if (c-&gt;cur_audio) { 
 +        free_representat 
 +    } 
 +    if (c-&gt;cur_video) { 
 +        free_representat 
 +    } 
 + 
 +    av_freep(&amp;c-&gt;cookies); 
 +    av_freep(&amp;c-&gt;user_agent) 
 +    av_dict_free(&amp;c-&gt;avio_op 
 +    av_freep(&amp;c-&gt;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-&gt;parent, AV_LOG_VERBOSE, &#34;DASH seek pos[%&#34;PRId64&#34;ms], playlist %d\n&#34;, seek_pos_msec, pls-&gt;rep_idx); 
 + 
 +    // single fragment mode 
 +    if (pls-&gt;n_fragments == 1) { 
 +        pls-&gt;cur_timesta = 0; 
 +        pls-&gt;cur_seg_off = 0; 
 +        ff_read_frame_fl 
 +        return av_seek_frame(pls-&gt;ctx, -1, seek_pos_msec * 1000, flags); 
 +    } 
 + 
 +    if (pls-&gt;input) 
 +        ff_format_io_clo &amp;pls-&gt;input); 
 + 
 +    // find the nearest fragment 
 +    if (pls-&gt;n_timelines &gt; 0 &amp;&amp; pls-&gt;fragment_timescale &gt; 0) { 
 +        int64_t num = pls-&gt;first_seq_no; 
 +        av_log(pls-&gt;pare AV_LOG_VERBOSE, &#34;dash_seek with SegmentTimeline start n_timelines[%d] &#34; 
 +               &#34;l playlist %d.\n&#34;, 
 +               (i (int64_t)pls-&gt;last_seq_no, (int)pls-&gt;rep_idx); 
 +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +            if (pls-&gt;timelines[i]-&gt;t &gt; 0) { 
 +                 = pls-&gt;timelines[i]-&gt;t; 
 +            } 
 +            duration += pls-&gt;timelines[i]-&gt;d; 
 +            if (seek_pos_msec &lt; ((duration * 1000) /  pls-&gt;fragment_timescale)) { 
 +                 set_seq_num; 
 +            } 
 +            for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) { 
 +                 += pls-&gt;timelines[i]-&gt;d; 
 +                 
 +                 (seek_pos_msec &lt; ((duration * 1000) /  pls-&gt;fragment_timescale)) { 
 +                 set_seq_num; 
 +                 
 +            } 
 +            num++; 
 +        } 
 + 
 +set_seq_num: 
 +        pls-&gt;cur_seq_no = num &gt; pls-&gt;last_seq_no ? pls-&gt;last_seq_no : num; 
 +        av_log(pls-&gt;pare AV_LOG_VERBOSE, &#34;dash_seek with SegmentTimeline end cur_seq_no[%&#34;PRId64&#34;], playlist %d.\n&#34;, 
 +               (i (int)pls-&gt;rep_idx); 
 +    } else if (pls-&gt;fragment_duration &gt; 0) { 
 +        pls-&gt;cur_seq_no = pls-&gt;first_seq_no + ((seek_pos_msec * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration) / 1000; 
 +    } else { 
 +        av_log(pls-&gt;pare AV_LOG_ERROR, &#34;dash_seek missing fragment_duration\n&#34;); 
 +        pls-&gt;cur_seq_no = pls-&gt;first_seq_no; 
 +    } 
 +    pls-&gt;cur_timestamp = 0; 
 +    pls-&gt;cur_seg_offset = 0; 
 +    pls-&gt;init_sec_buf_read_o = 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-&gt;priv_data; 
 +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000, 
 +                 
 +                 &amp; AVSEEK_FLAG_BACKWARD ? 
 +                 : AV_ROUND_UP); 
 +    if ((flags &amp; AVSEEK_FLAG_BYTE) || c-&gt;is_live) 
 +        return AVERROR(ENOSYS); 
 +    if (c-&gt;cur_audio) { 
 +        ret = dash_seek(s, c-&gt;cur_audio, seek_pos_msec, flags); 
 +    } 
 +    if (!ret &amp;&amp; c-&gt;cur_video) { 
 +        ret = dash_seek(s, c-&gt;cur_video, seek_pos_msec, flags); 
 +    } 
 +    return ret; 
 +} 
 + 
 +static int dash_probe(AVProbeData *p) 
 +{ 
 +    if (!av_stristr(p-&gt;buf, &#34;&lt;MPD&#34;)) 
 +        return 0; 
 + 
 +    if (av_stristr(p-&gt;buf, &#34;dash:profile:isoff-on-demand: || 
 +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-live:2011&#34; || 
 +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-live:2012&#34; || 
 +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-main:2011&#34; { 
 +        return AVPROBE_SCORE_MAX; 
 +    } 
 +    if (av_stristr(p-&gt;buf, &#34;dash:profile&#34;)) { 
 +        return AVPROBE_SCORE_MAX / 2; 
 +    } 
 + 
 +    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 = &#34;dash&#34;, 
 +    .item_name  = av_default_item_name, 
 +    .option     = dash_options, 
 +    .version    = LIBAVUTIL_VERSION_INT, 
 +}; 
 + 
 +AVInputFormat ff_dash_demuxer = { 
 +    .name           = &#34;dash&#34;, 
 +    .long_name      = NULL_IF_CONFIG_SMALL(&#34;Dynamic Adaptive Streaming over HTTP&#34;), 
 +    .priv_class     = &amp;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.11.0 (Apple Git-81) 
  
  
  
 ______________________________ 
 ffmpeg-devel mailing list 
  ffmpeg-devel@ffmpeg.org 
 ffmpeg.org ffmpeg.org
Rodger Combs Aug. 28, 2017, 1:30 a.m. UTC | #3
If you know of such a vulnerability, report it to ffmpeg-security@ffmpeg.org. New code with known vulnerabilities will not be accepted.

Sent from my iPhone

> On Aug 27, 2017, at 14:04, samsamsam <samsamsam@o2.pl> wrote:
> 
> get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`
> 
> If you afraid about safety then the only thing which need to be added to get_repl_pattern_and_format is validation of format.
> Simple loop to validate format will be enough. Do you agree? 
> 
> Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file.
> 
> I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety.
> 
> Regards,
> SSS
> 
> Dnia 27 sierpnia 2017 16:34 Rodger Combs <rodger.combs@gmail.com> napisał(a):
> 
> You're still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`, and specify an additional "precision" argument you parse from the XML yourself. I can't reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_.
> 
> Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That's what it does internally anyway.
> 
> On Aug 27, 2017, at 09:19, Steven Liu <lq@chinaffmpeg.org> wrote:
> 
> ffmpeg need a dash demuxer for demux the dash formats base on
> https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch
> 
> TODO:
> 1. support multi bitrate dash
> 
> 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
> 
> v10 fixed:
> 1. fix function API name code style
> 2. remove redundant strreplace call
> 3. remove redundant memory operation and check return value from get_content_url()
> 4. add space between ) and {
> 5. remove no need to log the value for print
> 
> v11 fixed:
> 1. from atoll to strtoll
> 
> v12 fixed:
> 1. remove strreplace and instead by av_strreplace
> 
> v13 fixed:
> 1. fix bug: cannot play:
> http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd
> 
> v14 fixed:
> 1. fix bug: TLS connection was non-properly terminated
> 2. fix bug: No trailing CRLF found in HTTP header
> 
> v15 fixed:
> 1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" | jq -r ".requested_formats[0].manifest_url")
> 2. code refine for timeline living stream
> 
> 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>
> Reviewed-by: wm4 <nfxjfg@googlemail.com>
> Tested-by: Andy Furniss <adf.lists@gmail.com>
> Reported-by: Andy Furniss <adf.lists@gmail.com>
> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> Signed-off-by: samsamsam <samsamsam@o2.pl>
> ---
> configure                |    4 +
> libavformat/Makefile     |    1 +
> libavformat/allformats.c |    2 +-
> libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1987 insertions(+), 1 deletion(-)
> create mode 100644 libavformat/dashdec.c
> 
> diff --git a/configure b/configure
> index 05f6dcc99a..7a7d61fa13 100755
> --- a/configure
> +++ b/configure
> @@ -272,6 +272,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]
> @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST="
>    libvpx
>    libwavpack
>    libwebp
> +    libxml2
>    libzimg
>    libzmq
>    libzvbi
> @@ -2937,6 +2939,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"
> @@ -5996,6 +5999,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>                               check_lib openssl 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
> 
> if enabled gcrypt; then
>    GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index f2b465cfa2..3d478749d0 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab
> --- /dev/null
> +++ b/libavformat/dashdec.c
> @@ -0,0 +1,1981 @@
> +/*
> + * 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).
> +     * */
> +    int64_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;
> +    char *url_template_pattern;
> +    char *url_template_format;
> +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
> +
> +    int64_t fragment_duration;
> +    int64_t fragment_timescale;
> +
> +    int64_t presentation_timeoffset;
> +
> +    int64_t cur_seq_no;
> +    int64_t cur_seg_offset;
> +    int64_t cur_seg_size;
> +    struct fragment *cur_seg;
> +
> +    /* Currently active Media Initialization Section */
> +    struct fragment *init_section;
> +    uint8_t *init_sec_buf;
> +    uint32_t init_sec_buf_size;
> +    uint32_t init_sec_data_len;
> +    uint32_t init_sec_buf_read_offset;
> +    int fix_multiple_stsd_order;
> +    int64_t cur_timestamp;
> +    int is_restart_needed;
> +};
> +
> +typedef struct DASHContext {
> +    const AVClass *class;
> +    char *base_url;
> +    struct representation *cur_video;
> +    struct representation *cur_audio;
> +
> +    /* MediaPresentationDescription Attribute */
> +    uint64_t media_presentation_duration;
> +    uint64_t suggested_presentation_delay;
> +    uint64_t availability_start_time;
> +    uint64_t publish_time;
> +    uint64_t minimum_update_period;
> +    uint64_t time_shift_buffer_depth;
> +    uint64_t min_buffer_time;
> +
> +    /* Period Attribute */
> +    uint64_t period_duration;
> +    uint64_t period_start;
> +
> +    int is_live;
> +    AVIOInterruptCB *interrupt_callback;
> +    char *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 get_current_time_in_sec(void)
> +{
> +    return  av_gettime() / 1000000;
> +}
> +
> +static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
> +{
> +    struct tm timeinfo;
> +    int year = 0;
> +    int month = 0;
> +    int day = 0;
> +    int hour = 0;
> +    int minute = 0;
> +    int ret = 0;
> +    float second = 0.0;
> +
> +    /* ISO-8601 date parser */
> +    if (!datetime)
> +        return 0;
> +
> +    ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
> +    /* year, month, day, hour, minute, second  6 arguments */
> +    if (ret != 6) {
> +        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
> +    }
> +    timeinfo.tm_year = year - 1900;
> +    timeinfo.tm_mon  = month - 1;
> +    timeinfo.tm_mday = day;
> +    timeinfo.tm_hour = hour;
> +    timeinfo.tm_min  = minute;
> +    timeinfo.tm_sec  = (int)second;
> +
> +    return av_timegm(&timeinfo);
> +}
> +
> +static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
> +{
> +    /* ISO-8601 duration parser */
> +    uint32_t days = 0;
> +    uint32_t hours = 0;
> +    uint32_t mins = 0;
> +    uint32_t secs = 0;
> +    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, "get_duration_insec get a wrong time format\n");
> +            return 0; /* parser error */
> +        }
> +        switch (type) {
> +            case 'D':
> +                days = (uint32_t)value;
> +                break;
> +            case 'H':
> +                hours = (uint32_t)value;
> +                break;
> +            case 'M':
> +                mins = (uint32_t)value;
> +                break;
> +            case 'S':
> +                secs = (uint32_t)value;
> +                break;
> +            default:
> +                // handle invalid type
> +                break;
> +        }
> +        ptr += size;
> +    }
> +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;
> +}
> +
> +static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
> +{
> +    int64_t start_time = 0;
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +
> +    if (pls->n_timelines) {
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            if (pls->timelines[i]->t > 0) {
> +                start_time = pls->timelines[i]->t;
> +            }
> +            if (num == cur_seq_no)
> +                goto finish;
> +
> +            start_time += pls->timelines[i]->d;
> +            for (j = 0; j < pls->timelines[i]->r; j++) {
> +                num++;
> +                if (num == cur_seq_no)
> +                    goto finish;
> +                start_time += pls->timelines[i]->d;
> +            }
> +            num++;
> +        }
> +    }
> +finish:
> +    return start_time;
> +}
> +
> +static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
> +{
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +    int64_t start_time = 0;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        if (pls->timelines[i]->t > 0) {
> +            start_time = pls->timelines[i]->t;
> +        }
> +        if (start_time > cur_time)
> +            goto finish;
> +
> +        start_time += pls->timelines[i]->d;
> +        for (j = 0; j < pls->timelines[i]->r; j++) {
> +            num++;
> +            if (start_time > cur_time)
> +                goto finish;
> +            start_time += pls->timelines[i]->d;
> +        }
> +        num++;
> +    }
> +
> +    return -1;
> +
> +finish:
> +    return num;
> +}
> +
> +static void free_fragment(struct fragment **seg)
> +{
> +    if (!(*seg)) {
> +        return;
> +    }
> +    av_freep(&(*seg)->url);
> +    av_freep(seg);
> +}
> +
> +static void free_fragment_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_fragments; i++) {
> +        free_fragment(&pls->fragments[i]);
> +    }
> +    av_freep(&pls->fragments);
> +    pls->n_fragments = 0;
> +}
> +
> +static void free_timelines_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        av_freep(&pls->timelines[i]);
> +    }
> +    av_freep(&pls->timelines);
> +    pls->n_timelines = 0;
> +}
> +
> +static void free_representation(struct representation *pls)
> +{
> +    free_fragment_list(pls);
> +    free_timelines_list(pls);
> +    free_fragment(&pls->cur_seg);
> +    free_fragment(&pls->init_section);
> +    av_freep(&pls->init_sec_buf);
> +    av_freep(&pls->pb.buffer);
> +    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_pattern);
> +    av_free(pls->url_template_format);
> +    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;
> +    void *p = NULL;
> +
> +    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.
> +        p = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> +        update_options(&c->cookies, "cookies", p);
> +        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 *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 = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
> +        av_free(tmp_str);
> +        tmp_str = url;
> +    }
> +    if (rep_bandwidth_val && tmp_str)
> +        url = av_strireplace(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 find_child_node_by_name(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 int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format)
> +{
> +    int ret = -1;
> +    int marker_len = 0;
> +    int format_len = 0;
> +    char *prefix = NULL;
> +    char *start = NULL;
> +    char *end = NULL;
> +
> +
> +    if (av_stristr(i_url, i_marker)) {
> +        *o_pattern = av_strdup(i_marker);
> +        *o_format = av_strdup("%"PRId64);
> +        ret = 0;
> +    } else {
> +        prefix = av_strdup(i_marker);
> +        marker_len = strlen(prefix)-1;
> +        prefix[marker_len] = '\0';
> +        start = av_stristr(i_url, prefix);
> +        if (!start)
> +            goto finish;
> +
> +        end = strchr(start + 1, '$');
> +        if (!end)
> +            goto finish;
> +
> +        if (start[marker_len] != '%')
> +            goto finish;
> +
> +        if (end[-1] != 'd')
> +            goto finish;
> +
> +        format_len = end - start - marker_len - 1 + strlen(PRId64);
> +        *o_format = av_mallocz(format_len+1);
> +        av_strlcpy(*o_format, start + marker_len, end - start - marker_len -1);
> +        av_strlcat(*o_format, PRId64, strlen(*o_format) + strlen(PRId64));
> +        *o_pattern = av_mallocz(end - start + 2);
> +        if (*o_pattern) {
> +            ret = AVERROR(EINVAL);
> +            goto finish;
> +        }
> +        av_strlcpy(*o_pattern, start, end - start + 1);
> +        ret = 0;
> +
> +finish:
> +        av_free(prefix);
> +    }
> +
> +    return ret;
> +}
> +
> +static enum AVMediaType get_content_type(xmlNodePtr node)
> +{
> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> +    int i = 0;
> +    const char *attr;
> +    xmlChar *val = NULL;
> +
> +    if (node) {
> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> +            attr = (i) ? "mimeType" : "contentType";
> +            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);
> +            if (!rep->init_section->url) {
> +                av_free(rep->init_section);
> +                xmlFree(initialization_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                xmlFree(media_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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)strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> +                tml->r =(int64_t) strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> +                tml->d = (int64_t)strtoll(val, NULL, 10);
> +//                rep->fragment_duration = (int64_t) strtoll(val, NULL, 10);
> +            }
> +            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 *presentation_timeoffset_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 = find_child_node_by_name(representation_node, "SegmentTemplate");
> +        representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
> +        representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
> +
> +        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;
> +
> +            presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
> +            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);
> +                if (!rep->init_section->url) {
> +                    av_free(rep->init_section);
> +                    av_free(rep);
> +                    ret = AVERROR(ENOMEM);
> +                    goto end;
> +                }
> +                rep->init_section->size = -1;
> +                xmlFree(initialization_val);
> +            }
> +
> +            if (media_val) {
> +                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
> +                temp_string = rep->url_template;
> +                if (temp_string) {
> +                    if (av_stristr(temp_string, "$Number")) {
> +                        get_repl_pattern_and_format(temp_string, "$Number$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
> +                    } else if (av_stristr(temp_string, "$Time")) {
> +                        get_repl_pattern_and_format(temp_string, "$Time$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
> +                    } else {
> +                        temp_string = NULL;
> +                    }
> +                }
> +                xmlFree(media_val);
> +            }
> +
> +            if (presentation_timeoffset_val) {
> +                rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
> +                xmlFree(presentation_timeoffset_val);
> +            }
> +            if (duration_val) {
> +                rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                xmlFree(timescale_val);
> +            }
> +            if (startnumber_val) {
> +                rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
> +                xmlFree(startnumber_val);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                ret = AVERROR(ENOMEM);
> +                goto end;
> +            }
> +            seg->size = -1;
> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> +        } else if (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) strtoll(duration_val, NULL, 10);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                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);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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 {
> +            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 = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
> +                c->publish_time = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
> +                c->minimum_update_period = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
> +                c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
> +                c->min_buffer_time = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
> +                c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
> +                c->media_presentation_duration = get_duration_insec(s, (const char *)val);
> +            }
> +            attr = attr->next;
> +            xmlFree(val);
> +        }
> +
> +        mpd_baseurl_node = find_child_node_by_name(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 = get_duration_insec(s, (const char *)val);
> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
> +                        perdiod_start_sec = get_duration_insec(s, (const char *)val);
> +                    }
> +                    attr = attr->next;
> +                    xmlFree(val);
> +                }
> +                if ((perdiod_duration_sec) >= (c->period_duration)) {
> +                    period_node = node;
> +                    c->period_duration = perdiod_duration_sec;
> +                    c->period_start = perdiod_start_sec;
> +                    if (c->period_start > 0)
> +                        c->media_presentation_duration = c->period_duration;
> +                }
> +            }
> +            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;
> +        }
> +
> +        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 = s->priv_data;
> +    int64_t num = 0;
> +    int64_t start_time_offset = 0;
> +
> +    if (c->is_live) {
> +        if (pls->n_fragments) {
> +            num = pls->first_seq_no;
> +        } else if (pls->n_timelines) {
> +            start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->t; // total duration of playlist
> +            if (start_time_offset < 60 * pls->fragment_timescale)
> +                start_time_offset = 0;
> +            else
> +                start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
> +
> +            num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->t + start_time_offset);
> +            if (num == -1)
> +                num = pls->first_seq_no;
> +        } else {
> +            if (pls->presentation_timeoffset) {
> +                num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
> +            } else if (c->publish_time > 0) {
> +                num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
> +            } else {
> +                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * 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 = s->priv_data;
> +    int64_t num = 0;
> +
> +    if (c->is_live && pls->fragment_duration) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no;
> +    }
> +    return num;
> +}
> +
> +static int64_t calc_max_seg_no(struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +
> +    if (pls->n_fragments) {
> +        num = pls->first_seq_no + pls->n_fragments - 1;
> +    } else if (pls->n_timelines) {
> +        int i = 0;
> +        num = pls->first_seq_no + pls->n_timelines - 1;
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            num += pls->timelines[i]->r;
> +        }
> +    } else if (c->is_live) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
> +    }
> +
> +    return num;
> +}
> +
> +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_timelines_list(rep_dest);
> +        rep_dest->timelines    = rep_src->timelines;
> +        rep_dest->n_timelines  = rep_src->n_timelines;
> +        rep_dest->first_seq_no = rep_src->first_seq_no;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->timelines = NULL;
> +        rep_src->n_timelines = 0;
> +        rep_dest->cur_seq_no = rep_src->cur_seq_no;
> +    }
> +}
> +
> +static void move_segments(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_fragment_list(rep_dest);
> +        if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
> +            rep_dest->cur_seq_no = 0;
> +        else
> +            rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
> +        rep_dest->fragments    = rep_src->fragments;
> +        rep_dest->n_fragments  = rep_src->n_fragments;
> +        rep_dest->parent  = rep_src->parent;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->fragments = NULL;
> +        rep_src->n_fragments = 0;
> +    }
> +}
> +
> +
> +static int refresh_manifest(AVFormatContext *s)
> +{
> +
> +    int ret = 0;
> +    DASHContext *c = s->priv_data;
> +
> +    // save current context
> +    struct representation *cur_video =  c->cur_video;
> +    struct representation *cur_audio =  c->cur_audio;
> +    char *base_url = c->base_url;
> +
> +    c->base_url = NULL;
> +    c->cur_video = NULL;
> +    c->cur_audio = NULL;
> +    ret = parse_manifest(s, s->filename, NULL);
> +    if (ret)
> +        goto finish;
> +
> +    if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
> +        // calc current time
> +        int64_t currentVideoTime = 0;
> +        int64_t currentAudioTime = 0;
> +        if (cur_video && cur_video->timelines)
> +            currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
> +        if (cur_audio && cur_audio->timelines)
> +            currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
> +        // update segments
> +        if (cur_video && cur_video->timelines) {
> +            c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
> +            if (c->cur_video->cur_seq_no >= 0) {
> +                move_timelines(c->cur_video, cur_video);
> +            }
> +        }
> +        if (cur_audio && cur_audio->timelines) {
> +            c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
> +            if (c->cur_audio->cur_seq_no >= 0) {
> +               move_timelines(c->cur_audio, cur_audio);
> +            }
> +        }
> +    }
> +    if (cur_video && cur_video->fragments) {
> +        move_segments(c->cur_video, cur_video);
> +    }
> +    if (cur_audio && cur_audio->fragments) {
> +        move_segments(c->cur_audio, cur_audio);
> +    }
> +
> +finish:
> +    // restore context
> +    if (c->base_url)
> +        av_free(base_url);
> +    else
> +        c->base_url  = base_url;
> +    if (c->cur_audio)
> +        free_representation(c->cur_audio);
> +    if (c->cur_video)
> +        free_representation(c->cur_video);
> +    c->cur_audio = cur_audio;
> +    c->cur_video = cur_video;
> +    return ret;
> +}
> +
> +static struct fragment *get_current_fragment(struct representation *pls)
> +{
> +    int64_t min_seq_no = 0;
> +    int64_t max_seq_no = 0;
> +    struct fragment *seg = NULL;
> +    struct fragment *seg_ptr = NULL;
> +    DASHContext *c = pls->parent->priv_data;
> +
> +    while (( !ff_check_interrupt(c->interrupt_callback)&& 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;
> +        } else if (c->is_live) {
> +            av_usleep(1000*1000);
> +            refresh_manifest(pls->parent);
> +        } else {
> +            break;
> +        }
> +    }
> +    if (c->is_live) {
> +        while (!ff_check_interrupt(c->interrupt_callback)) {
> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> +            max_seq_no = calc_max_seg_no(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);
> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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*1000);
> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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) {
> +            int64_t val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no);
> +            int size = snprintf(NULL, 0, pls->url_template_format, val); // calc needed buffer size
> +
> +            if (size > 0) {
> +                char *tmp_val = av_mallocz(size + 1);
> +                snprintf(tmp_val, size+1, pls->url_template_format, val);
> +                seg->url = av_strireplace(pls->url_template, pls->url_template_pattern, tmp_val);
> +                av_free(tmp_val);
> +            }
> +        }
> +
> +        if (!seg->url) {
> +            av_log(pls->parent, AV_LOG_ERROR, "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;
> +    }
> +
> +    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;
> +
> +    if (!v->is_restart_needed)
> +        v->cur_seq_no++;
> +    v->is_restart_needed = 1;
> +
> +/*
> +    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[0] != '\0') {
> +                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 DASH 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(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 * 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) {
> +        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
> +            ret = av_read_frame(cur->ctx, pkt);
> +            if (ret >= 0) {
> +                /* If we got a packet, return it */
> +                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;
> +                return 0;
> +            }
> +            if (cur->is_restart_needed) {
> +                while (!ff_check_interrupt(c->interrupt_callback)) {
> +                    cur->cur_seg_offset = 0;
> +                    cur->init_sec_buf_read_offset = 0;
> +                    if (cur->input)
> +                        ff_format_io_close(cur->parent, &cur->input);
> +                    ret = reopen_demux_for_component(s, cur);
> +                    if (c->is_live && ret) {
> +                        av_usleep(1000*1000);
> +                        continue;
> +                    }
> +                    break;
> +                }
> +                cur->is_restart_needed = 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 == 1) {
> +        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 / 2;
> +    }
> +
> +    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.11.0 (Apple Git-81)
> 
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
samsamsam Aug. 28, 2017, 9:28 a.m. UTC | #4
OK. I will. What about adding validation of format instead of adding &#34; something like `&#34;%0*&#34;PRId64`&#34;?   Dnia 28 sierpnia 2017 03:30 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a):  If you know of such a vulnerability, report it to    ffmpeg-security@ffmpeg.org . New code with known vulnerabilities will not be accepted.   Sent from my iPhone   On Aug 27, 2017, at 14:04, samsamsam &lt;   samsamsam@o2.pl &gt; wrote:  get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64`   If you afraid about safety then the only thing which need to be added to  get_repl_pattern_and_format is validation of format.  Simple loop to validate format will be enough. Do you agree?    Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file.   I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety.   Regards,  SSS   Dnia 27 sierpnia 2017 16:34 Rodger Combs &lt;   rodger.combs@gmail.com &gt; napisał(a):  You&#39;re still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64`, and specify an additional &#34;precision&#34; argument you parse from the XML yourself. I can&#39;t reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_.   Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That&#39;s what it does internally anyway.   On Aug 27, 2017, at 09:19, Steven Liu &lt;   lq@chinaffmpeg.org &gt; wrote:   ffmpeg need a dash demuxer for demux the dash formats base on  github.com github.com   TODO:  1. support multi bitrate dash   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 &#39;time(NULL)`, except it is not 2038-safe.&#39; 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-&gt;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   v10 fixed:  1. fix function API name code style  2. remove redundant strreplace call  3. remove redundant memory operation and check return value from get_content_url()  4. add space between ) and {  5. remove no need to log the value for print   v11 fixed:  1. from atoll to strtoll   v12 fixed:  1. remove strreplace and instead by av_strreplace   v13 fixed:  1. fix bug: cannot play:  dash.edgesuite.net dash.edgesuite.net   v14 fixed:  1. fix bug: TLS connection was non-properly terminated  2. fix bug: No trailing CRLF found in HTTP header   v15 fixed:  1. play youtube link: ffmpeg -i $(youtube-dl -J &#34; www.youtube.com www.youtube.com  | jq -r &#34;.requested_formats[0].manifes  2. code refine for timeline living stream   Reviewed-by: Clément Bœsch &lt;   u@pkh.me &gt;  Reviewed-by: Michael Niedermayer &lt;   michael@niedermayer.cc &gt;  Reviewed-by: Carl Eugen Hoyos &lt;   cehoyos@ag.or.at &gt;  Reviewed-by: Rodger Combs &lt;   rodger.combs@gmail.com &gt;  Reviewed-by: Moritz Barsnick &lt;   barsnick@gmx.net &gt;  Reviewed-by: Nicolas George &lt;   george@nsup.org &gt;  Reviewed-by: Ricardo Constantino &lt;   wiiaboo@gmail.com &gt;  Reviewed-by: wm4 &lt;   nfxjfg@googlemail.com &gt;  Tested-by: Andy Furniss &lt;   adf.lists@gmail.com &gt;  Reported-by: Andy Furniss &lt;   adf.lists@gmail.com &gt;  Signed-off-by: Steven Liu &lt;   lq@chinaffmpeg.org &gt;  Signed-off-by: samsamsam &lt;   samsamsam@o2.pl &gt;  ---  configure                    4 +  libavformat/Makefile     |    1 +  libavformat/allformats.c |    2 +-  libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++  4 files changed, 1987 insertions(+), 1 deletion(-)  create mode 100644 libavformat/dashdec.c   diff --git a/configure b/configure  index 05f6dcc99a..7a7d61fa13 100755  --- a/configure  +++ b/configure  @@ -272,6 +272,7 @@ External library support:   --enable-libxcb-shape    enable X11 grabbing shape rendering [autodetect]   --enable-libxvid         enable Xvid encoding via xvidcore,                  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]  @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST=&#34;     libvpx     libwavpack     libwebp  +    libxml2     libzimg     libzmq     libzvbi  @@ -2937,6 +2939,7 @@ avi_muxer_select=&#34;riffenc&#34;  caf_demuxer_select=&#34;iso_media riffdec&#34;  caf_muxer_select=&#34;iso_media&#34;  dash_muxer_select=&#34;mp4_muxer&#34;  +dash_demuxer_deps=&#34;libxml2&#34;  dirac_demuxer_select=&#34;dirac_pa  dts_demuxer_select=&#34;dca_parser  dtshd_demuxer_select=&#34;dca_pars  @@ -5996,6 +5999,7 @@ enabled openssl           &amp;&amp; { use_pkg_config openssl openssl/ssl.h OPENSSL_init                  openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||                  &#34;ERROR: openssl not found&#34;; }  enabled qtkit_indev      &amp;&amp; { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; }  +enabled libxml2          &amp;&amp; require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion   if enabled gcrypt; then     GCRYPT_CONFIG=&#34;${cross_p  diff --git a/libavformat/Makefile b/libavformat/Makefile  index f2b465cfa2..3d478749d0 100644  --- a/libavformat/Makefile  +++ b/libavformat/Makefile  @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab  --- /dev/null  +++ b/libavformat/dashdec.c  @@ -0,0 +1,1981 @@  +/*  + * 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 &lt;libxml/parser.h&gt;  +#include &#34;libavutil/intreadwrite.h&#34;  +#include &#34;libavutil/opt.h&#34;  +#include &#34;libavutil/time.h&#34;  +#include &#34;libavutil/parseutils.h&#34;  +#include &#34;internal.h&#34;  +#include &#34;avio_internal.h&#34;  +  +#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&#39;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).  +     * */  +    int64_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;  +    char *url_template_pattern;  +    char *url_template_format;  +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/  +  +    int64_t fragment_duration;  +    int64_t fragment_timescale;  +  +    int64_t presentation_timeoffset;  +  +    int64_t cur_seq_no;  +    int64_t cur_seg_offset;  +    int64_t cur_seg_size;  +    struct fragment *cur_seg;  +  +    /* Currently active Media Initialization Section */  +    struct fragment *init_section;  +    uint8_t *init_sec_buf;  +    uint32_t init_sec_buf_size;  +    uint32_t init_sec_data_len;  +    uint32_t init_sec_buf_read_offset;  +    int fix_multiple_stsd_order;  +    int64_t cur_timestamp;  +    int is_restart_needed;  +};  +  +typedef struct DASHContext {  +    const AVClass *class;  +    char *base_url;  +    struct representation *cur_video;  +    struct representation *cur_audio;  +  +    /* MediaPresentationDescription Attribute */  +    uint64_t media_presentation_duration;  +    uint64_t suggested_presentation_delay;  +    uint64_t availability_start_time;  +    uint64_t publish_time;  +    uint64_t minimum_update_period;  +    uint64_t time_shift_buffer_depth;  +    uint64_t min_buffer_time;  +  +    /* Period Attribute */  +    uint64_t period_duration;  +    uint64_t period_start;  +  +    int is_live;  +    AVIOInterruptCB *interrupt_callback;  +    char *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 get_current_time_in_sec(void)  +{  +    return  av_gettime() / 1000000;  +}  +  +static uint64_t get_utc_date_time_insec(AVForm *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, &#34;%d-%d-%dT%d:%d:%fZ&#34;, &amp;year, &amp;month, &amp;day, &amp;hour, &amp;minute, &amp;second);  +    /* year, month, day, hour, minute, second  6 arguments */  +    if (ret != 6) {  +        av_log(s, AV_LOG_WARNING, &#34;get_utc_date_time_insec get a wrong time format\n&#34;);  +    }  +    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(&amp;timeinfo);  +}  +  +static uint32_t get_duration_insec(AVFormatCon *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 == &#39;P&#39; || *ptr == &#39;T&#39;) {  +            ptr++;  +            continue  +        }  +  +        if (sscanf(ptr, &#34;%f%c%n&#34;, &amp;value, &amp;type, &amp;size) != 2) {  +            av_log(s AV_LOG_WARNING, &#34;get_duration_insec get a wrong time format\n&#34;);  +            return 0; /* parser error */  +        }  +        switch (type) {  +            case &#39;D&#39;:  +                 = (uint32_t)value;  +                  +            case &#39;H&#39;:  +                 = (uint32_t)value;  +                  +            case &#39;M&#39;:  +                 = (uint32_t)value;  +                  +            case &#39;S&#39;:  +                 = (uint32_t)value;  +                  +            default:  +                 handle invalid type  +                  +        }  +        ptr += size;  +    }  +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;  +}  +  +static int64_t get_segment_start_time_based_o representation *pls, int64_t cur_seq_no)  +{  +    int64_t start_time = 0;  +    int64_t i = 0;  +    int64_t j = 0;  +    int64_t num = 0;  +  +    if (pls-&gt;n_timelines) {  +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) {  +            if (pls-&gt;timelines[i]-&gt;t &gt; 0) {  +                 = pls-&gt;timelines[i]-&gt;t;  +            }  +            if (num == cur_seq_no)  +                 finish;  +  +            start_ti += pls-&gt;timelines[i]-&gt;d;  +            for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) {  +                  +                 (num == cur_seq_no)  +                 finish;  +                 += pls-&gt;timelines[i]-&gt;d;  +            }  +            num++;  +        }  +    }  +finish:  +    return start_time;  +}  +  +static int64_t calc_next_seg_no_from_timeline representation *pls, int64_t cur_time)  +{  +    int64_t i = 0;  +    int64_t j = 0;  +    int64_t num = 0;  +    int64_t start_time = 0;  +  +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) {  +        if (pls-&gt;timelines[i]-&gt;t &gt; 0) {  +            start_ti = pls-&gt;timelines[i]-&gt;t;  +        }  +        if (start_time &gt; cur_time)  +            goto finish;  +  +        start_time += pls-&gt;timelines[i]-&gt;d;  +        for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) {  +            num++;  +            if (start_time &gt; cur_time)  +                 finish;  +            start_ti += pls-&gt;timelines[i]-&gt;d;  +        }  +        num++;  +    }  +  +    return -1;  +  +finish:  +    return num;  +}  +  +static void free_fragment(struct fragment **seg)  +{  +    if (!(*seg)) {  +        return;  +    }  +    av_freep(&amp;(*seg)-&gt;url);  +    av_freep(seg);  +}  +  +static void free_fragment_list(struct representation *pls)  +{  +    int i;  +  +    for (i = 0; i &lt; pls-&gt;n_fragments; i++) {  +        free_fragment(&amp;p  +    }  +    av_freep(&amp;pls-&gt;fragments  +    pls-&gt;n_fragments = 0;  +}  +  +static void free_timelines_list(struct representation *pls)  +{  +    int i;  +  +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) {  +        av_freep(&amp;pls-&gt;t  +    }  +    av_freep(&amp;pls-&gt;timelines  +    pls-&gt;n_timelines = 0;  +}  +  +static void free_representation(struct representation *pls)  +{  +    free_fragment_list(pls);  +    free_timelines_list(pls)  +    free_fragment(&amp;pls-&gt;cur_  +    free_fragment(&amp;pls-&gt;init  +    av_freep(&amp;pls-&gt;init_sec_  +    av_freep(&amp;pls-&gt;pb.buffer  +    if (pls-&gt;input)  +        ff_format_io_clo &amp;pls-&gt;input);  +    if (pls-&gt;ctx) {  +        pls-&gt;ctx-&gt;pb = NULL;  +        avformat_close_i  +    }  +  +    av_free(pls-&gt;url_templat  +    av_free(pls-&gt;url_templat  +    av_free(pls-&gt;url_templat  +    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,  +                 *opts, AVDictionary *opts2, int *is_http)  +{  +    DASHContext *c = s-&gt;priv_data;  +    AVDictionary *tmp = NULL;  +    const char *proto_name = NULL;  +    int ret;  +    void *p = NULL;  +  +    av_dict_copy(&amp;tmp, opts, 0);  +    av_dict_copy(&amp;tmp, opts2, 0);  +  +    if (av_strstart(url, &#34;crypto&#34;, NULL)) {  +        if (url[6] == &#39;+&#39; || url[6] == &#39;:&#39;)  +            proto_na = 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) &amp; file are allowed  +    if (!av_strstart(proto_name, &#34;http&#34;, NULL) &amp;&amp; !av_strstart(proto_name, &#34;file&#34;, NULL)) {  +        return AVERROR_INVALIDDATA;  +    }  +    if (!strncmp(proto_name, url, strlen(proto_name)) &amp;&amp; url[strlen(proto_name)] == &#39;:&#39;) {  +        ;  +    } else if (av_strstart(url, &#34;crypto&#34;, NULL) &amp;&amp; !strncmp(proto_name, url + 7, strlen(proto_name)) &amp;&amp; url[7 + strlen(proto_name)] == &#39;:&#39;) {  +        ;  +    } else if (strcmp(proto_name, &#34;file&#34;) || !strncmp(url, &#34;file,&#34;, 5)) {  +        return AVERROR_INVALIDDATA;  +    }  +    ret = s-&gt;io_open(s, pb, url, AVIO_FLAG_READ, &amp;tmp);  +    if (ret &gt;= 0) {  +        // update cookies on http response with setcookies.  +        p = (s-&gt;flags &amp; AVFMT_FLAG_CUSTOM_IO) ? NULL : s-&gt;pb;  +        update_options(&amp; &#34;cookies&#34;, p);  +        av_dict_set(&amp;opt &#34;cookies&#34;, c-&gt;cookies, 0);  +    }  +  +    av_dict_free(&amp;tmp);  +  +    if (is_http)  +        *is_http = av_strstart(proto_name, &#34;http&#34;, NULL);  +  +    return ret;  +  +}  +  +static char *get_content_url(xmlNodePtr *baseurl_nodes,  +                 n_baseurl_nodes,  +                 *rep_id_val,  +                 *rep_bandwidth_val,  +                 *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 &lt; n_baseurl_nodes; ++i) {  +        if (baseurl_nodes[i] &amp;&amp;  +            baseurl_ &amp;&amp;  +            baseurl_ == XML_TEXT_NODE) {  +            text = xmlNodeGetContent(baseurl_node  +            if (text) {  +                 = av_mallocz(MAX_URL_SIZE);  +                 (!tmp_str_2) {  +                  +                 NULL;  +                  +                 MAX_URL_SIZE, tmp_str, text);  +                  +                 = tmp_str_2;  +                  +            }  +        }  +    }  +    if (val)  +        av_strlcat(tmp_s (const char*)val, MAX_URL_SIZE);  +  +    if (rep_id_val) {  +        url = av_strireplace(tmp_str, &#34;$RepresentationID$&#34;, (const char*)rep_id_val);  +        av_free(tmp_str)  +        tmp_str = url;  +    }  +    if (rep_bandwidth_val &amp;&amp; tmp_str)  +        url = av_strireplace(tmp_str, &#34;$Bandwidth$&#34;, (const char*)rep_bandwidth_val);  +    if (tmp_str != url)  +        av_free(tmp_str)  +    return url;  +}  +  +static xmlChar *get_val_from_nodes_tab(xmlNod *nodes, const int n_nodes, const xmlChar *attrname)  +{  +    int i;  +    xmlChar *val;  +  +    for (i = 0; i &lt; n_nodes; ++i) {  +        if (nodes[i]) {  +            val = xmlGetProp(nodes[i], attrname);  +            if (val)  +                 val;  +        }  +    }  +  +    return NULL;  +}  +  +static xmlNodePtr find_child_node_by_name(xmlNod rootnode, const xmlChar *nodename)  +{  +    xmlNodePtr node = rootnode;  +    if (!node) {  +        return NULL;  +    }  +  +    node = xmlFirstElementChild(node);  +    while (node) {  +        if (!xmlStrcmp(node-&gt;name, nodename)) {  +            return node;  +        }  +        node = xmlNextElementSibling(node);  +    }  +    return NULL;  +}  +  +static int get_repl_pattern_and_format(co char *i_url, const char *i_marker, char **o_pattern, char **o_format)  +{  +    int ret = -1;  +    int marker_len = 0;  +    int format_len = 0;  +    char *prefix = NULL;  +    char *start = NULL;  +    char *end = NULL;  +  +  +    if (av_stristr(i_url, i_marker)) {  +        *o_pattern = av_strdup(i_marker);  +        *o_format = av_strdup(&#34;%&#34;PRId64);  +        ret = 0;  +    } else {  +        prefix = av_strdup(i_marker);  +        marker_len = strlen(prefix)-1;  +        prefix[marker_le = &#39;\0&#39;;  +        start = av_stristr(i_url, prefix);  +        if (!start)  +            goto finish;  +  +        end = strchr(start + 1, &#39;$&#39;);  +        if (!end)  +            goto finish;  +  +        if (start[marker_len] != &#39;%&#39;)  +            goto finish;  +  +        if (end[-1] != &#39;d&#39;)  +            goto finish;  +  +        format_len = end - start - marker_len - 1 + strlen(PRId64);  +        *o_format = av_mallocz(format_len+1);  +        av_strlcpy(*o_fo start + marker_len, end - start - marker_len -1);  +        av_strlcat(*o_fo PRId64, strlen(*o_format) + strlen(PRId64));  +        *o_pattern = av_mallocz(end - start + 2);  +        if (*o_pattern) {  +            ret = AVERROR(EINVAL);  +            goto finish;  +        }  +        av_strlcpy(*o_pa start, end - start + 1);  +        ret = 0;  +  +finish:  +        av_free(prefix);  +    }  +  +    return ret;  +}  +  +static enum AVMediaType get_content_type(xmlNodePtr node)  +{  +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;  +    int i = 0;  +    const char *attr;  +    xmlChar *val = NULL;  +  +    if (node) {  +        while (type == AVMEDIA_TYPE_UNKNOWN &amp;&amp; i &lt; 2) {  +            attr = (i) ? &#34;mimeType&#34; : &#34;contentType&#34;;  +            val = xmlGetProp(node, attr);  +            if (val) {  +                 (av_stristr((const char *)val, &#34;video&#34;)) {  +                 = AVMEDIA_TYPE_VIDEO;  +                 else if (av_stristr((const char *)val, &#34;audio&#34;)) {  +                 = AVMEDIA_TYPE_AUDIO;  +                  +                  +            }  +            i++;  +        }  +    }  +    return type;  +}  +  +static int parse_manifest_segmenturlnode( *s, struct representation *rep,  +                 fragmenturl_node,  +                 *baseurl_nodes,  +                 *rep_id_val,  +                 *rep_bandwidth_val)  +{  +    xmlChar *initialization_val = NULL;  +    xmlChar *media_val = NULL;  +  +    if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;Initialization&#34;)) {  +        initialization_v = xmlGetProp(fragmenturl_node, &#34;sourceURL&#34;);  +        if (initialization_val) {  +            rep-&gt;ini = av_mallocz(sizeof(struct fragment));  +            if (!rep-&gt;init_section) {  +                  +                 AVERROR(ENOMEM);  +            }  +            rep-&gt;ini = get_content_url(baseurl_nodes, 4,  +                  +                  +                  +            if (!rep-&gt;init_section-&gt;url) {  +                  +                  +                 AVERROR(ENOMEM);  +            }  +            rep-&gt;ini = -1;  +            xmlFree(  +        }  +    } else if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;SegmentURL&#34;)) {  +        media_val = xmlGetProp(fragmenturl_node, &#34;media&#34;);  +        if (media_val) {  +            struct fragment *seg = av_mallocz(sizeof(struct fragment));  +            if (!seg) {  +                  +                 AVERROR(ENOMEM);  +            }  +            seg-&gt;url = get_content_url(baseurl_nodes, 4,  +                  +                  +                  +            if (!seg-&gt;url) {  +                  +                  +                 AVERROR(ENOMEM);  +            }  +            seg-&gt;siz = -1;  +            dynarray &amp;rep-&gt;n_fragments, seg);  +            xmlFree(  +        }  +    }  +  +    return 0;  +}  +  +static int parse_manifest_segmenttimeline *s, struct representation *rep,  +                 fragment_timeline_node)  +{  +    xmlAttrPtr attr = NULL;  +    xmlChar *val  = NULL;  +  +    if (!xmlStrcmp(fragment_timeline_ (const xmlChar *)&#34;S&#34;)) {  +        struct timeline *tml = av_mallocz(sizeof(struct timeline));  +        if (!tml) {  +            return AVERROR(ENOMEM);  +        }  +        attr = fragment_timeline_node-&gt;proper  +        while (attr) {  +            val = xmlGetProp(fragment_timeline_n attr-&gt;name);  +  +            if (!val) {  +                 AV_LOG_WARNING, &#34;parse_manifest_segmenttimelin attr-&gt;name = %s val is NULL\n&#34;, attr-&gt;name);  +                  +            }  +  +            if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;t&#34;)) {  +                 = (int64_t)strtoll(val, NULL, 10);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;r&#34;)) {  +                 =(int64_t) strtoll(val, NULL, 10);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;d&#34;)) {  +                 = (int64_t)strtoll(val, NULL, 10);  +//                 = (int64_t) strtoll(val, NULL, 10);  +            }  +            attr = attr-&gt;next;  +            xmlFree(  +        }  +        dynarray_add(&amp;re &amp;rep-&gt;n_timelines, tml);  +    }  +  +    return 0;  +}  +  +static int parse_manifest_representation( *s, const char *url,  +                 node,  +                 adaptionset_node,  +                 mpd_baseurl_node,  +                 period_baseurl_node,  +                 fragment_template_node,  +                 content_component_node,  +                 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-&gt;priv_data;  +    struct representation *rep = NULL;  +    struct fragment *seg = NULL;  +    xmlNodePtr representation_segmenttemplate = NULL;  +    xmlNodePtr representation_baseurl_node = NULL;  +    xmlNodePtr representation_segmentlist_nod = NULL;  +    xmlNodePtr fragment_timeline_node = NULL;  +    xmlNodePtr fragment_templates_tab[2];  +    xmlChar *duration_val = NULL;  +    xmlChar *presentation_timeoffset_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 &#34;id&#34;);  +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node &#34;bandwidth&#34;);  +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;  +  +    // try get information from representation  +    if (type == AVMEDIA_TYPE_UNKNOWN)  +        type = get_content_type(representatio  +    // try get information from contentComponen  +    if (type == AVMEDIA_TYPE_UNKNOWN)  +        type = get_content_type(content_compo  +    // try get information from adaption set  +    if (type == AVMEDIA_TYPE_UNKNOWN)  +        type = get_content_type(adaptionset_n  +    if (type == AVMEDIA_TYPE_UNKNOWN) {  +        av_log(s, AV_LOG_VERBOSE, &#34;Parsing &#39;%s&#39; - skipp not supported representation type\n&#34;, url);  +    } else if ((type == AVMEDIA_TYPE_VIDEO &amp;&amp; !c-&gt;cur_video) || (type == AVMEDIA_TYPE_AUDIO &amp;&amp; !c-&gt;cur_audio)) {  +        // convert selected representation to our internal struct  +        rep = av_mallocz(sizeof(struct representation));  +        if (!rep) {  +            ret = AVERROR(ENOMEM);  +            goto end;  +        }  +        representation_s = find_child_node_by_name(repres &#34;SegmentTemplate&#34;);  +        representation_b = find_child_node_by_name(repres &#34;BaseURL&#34;);  +        representation_s = find_child_node_by_name(repres &#34;SegmentList&#34;);  +  +        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_segmenttemplat || fragment_template_node) {  +            fragment = NULL;  +            fragment = representation_segmenttemplate  +            fragment = fragment_template_node;  +  +            presenta = get_val_from_nodes_tab(fragmen 2, &#34;presentationTimeOffset&#34;);  +            duration = get_val_from_nodes_tab(fragmen 2, &#34;duration&#34;);  +            startnum = get_val_from_nodes_tab(fragmen 2, &#34;startNumber&#34;);  +            timescal = get_val_from_nodes_tab(fragmen 2, &#34;timescale&#34;);  +            initiali = get_val_from_nodes_tab(fragmen 2, &#34;initialization&#34;);  +            media_va = get_val_from_nodes_tab(fragmen 2, &#34;media&#34;);  +  +            if (initialization_val) {  +                 = av_mallocz(sizeof(struct fragment));  +                 (!rep-&gt;init_section) {  +                  +                 = AVERROR(ENOMEM);  +                 end;  +                  +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);  +                 (!rep-&gt;init_section-&gt;url) {  +                  +                  +                 = AVERROR(ENOMEM);  +                 end;  +                  +                 = -1;  +                  +            }  +  +            if (media_val) {  +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);  +                 = rep-&gt;url_template;  +                 (temp_string) {  +                 (av_stristr(temp_string, &#34;$Number&#34;)) {  +                 &#34;$Number$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format));  +                 = TMP_URL_TYPE_NUMBER;  /* Number-Based. */  +                 else if (av_stristr(temp_string, &#34;$Time&#34;)) {  +                 &#34;$Time$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format));  +                 = TMP_URL_TYPE_TIME; /* Time-Based. */  +                 else {  +                 = NULL;  +                  +                  +                  +            }  +  +            if (presentation_timeoffset_val) {  +                 = (int64_t) strtoll(presentation_timeoffse NULL, 10);  +                  +            }  +            if (duration_val) {  +                 = (int64_t) strtoll(duration_val, NULL, 10);  +                  +            }  +            if (timescale_val) {  +                 = (int64_t) strtoll(timescale_val, NULL, 10);  +                  +            }  +            if (startnumber_val) {  +                 = (int64_t) strtoll(startnumber_val, NULL, 10);  +                  +            }  +  +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;);  +  +            if (!fragment_timeline_node)  +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;);  +            if (fragment_timeline_node) {  +                 = xmlFirstElementChild(fragment_  +                 (fragment_timeline_node) {  +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node);  +                 (ret &lt; 0) {  +                 ret;  +                  +                 = xmlNextElementSibling(fragment  +                  +            }  +        } else if (representation_baseurl_node &amp;&amp; !representation_segmentlist_no {  +            seg = av_mallocz(sizeof(struct fragment));  +            if (!seg) {  +                 = AVERROR(ENOMEM);  +                 end;  +            }  +            seg-&gt;url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);  +            if (!seg-&gt;url) {  +                  +                 = AVERROR(ENOMEM);  +                 end;  +            }  +            seg-&gt;siz = -1;  +            dynarray &amp;rep-&gt;n_fragments, seg);  +        } else if (representation_segmentlist_no {  +            // TODO:  www.brendanlong.com www.brendanlong.com  +            //  www-itec.uni-klu.ac.at www-itec.uni-klu.ac.at  +            xmlNodeP fragmenturl_node = NULL;  +            duration = xmlGetProp(representation_segm &#34;duration&#34;);  +            timescal = xmlGetProp(representation_segm &#34;timescale&#34;);  +            if (duration_val) {  +                 = (int64_t) strtoll(duration_val, NULL, 10);  +                  +            }  +            if (timescale_val) {  +                 = (int64_t) strtoll(timescale_val, NULL, 10);  +                  +            }  +            fragment = xmlFirstElementChild(represent  +            while (fragmenturl_node) {  +                 = parse_manifest_segmenturlnode( rep, fragmenturl_node,  +                  +                  +                  +                 (ret &lt; 0) {  +                 ret;  +                  +                 = xmlNextElementSibling(fragment  +            }  +  +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;);  +  +            if (!fragment_timeline_node)  +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;);  +            if (fragment_timeline_node) {  +                 = xmlFirstElementChild(fragment_  +                 (fragment_timeline_node) {  +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node);  +                 (ret &lt; 0) {  +                 ret;  +                  +                 = xmlNextElementSibling(fragment  +                  +            }  +        } else {  +            free_rep  +            rep = NULL;  +            av_log(s AV_LOG_ERROR, &#34;Unknown format of Representation node id[%s] \n&#34;, (const char *)rep_id_val);  +        }  +  +        if (rep) {  +            if (rep-&gt;fragment_duration &gt; 0 &amp;&amp; !rep-&gt;fragment_timescale)  +                 = 1;  +            if (type == AVMEDIA_TYPE_VIDEO) {  +                 = video_rep_idx;  +                 = rep;  +            } else {  +                 = audio_rep_idx;  +                 = rep;  +            }  +        }  +    }  +  +    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;  +    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;  +  +end:  +    if (rep_id_val)  +        xmlFree(rep_id_v  +    if (rep_bandwidth_val)  +        xmlFree(rep_band  +  +    return ret;  +}  +  +static int parse_manifest_adaptationset(A *s, const char *url,  +                 adaptionset_node,  +                 mpd_baseurl_node,  +                 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(adaptions  +    while (node) {  +        if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;SegmentTemplate&#34;)) {  +            fragment = node;  +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;ContentComponent&#34;)) {  +            content_ = node;  +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;BaseURL&#34;)) {  +            adaption = node;  +        } else if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;Representation&#34;)) {  +            ret = parse_manifest_representation( url, node,  +                  +                  +                  +                  +                  +                  +            if (ret &lt; 0) {  +                 ret;  +            }  +        }  +        node = xmlNextElementSibling(node);  +    }  +    return 0;  +}  +  +  +static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)  +{  +    DASHContext *c = s-&gt;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(&amp;opt &#34;seekable&#34;, &#34;0&#34;, 0);  +        // broker prior HTTP options that should be consistent across requests  +        av_dict_set(&amp;opt &#34;user-agent&#34;, c-&gt;user_agent, 0);  +        av_dict_set(&amp;opt &#34;cookies&#34;, c-&gt;cookies, 0);  +        av_dict_set(&amp;opt &#34;headers&#34;, c-&gt;headers, 0);  +  +        ret = avio_open2(&amp;in, url, AVIO_FLAG_READ, c-&gt;interrupt_callback, &amp;opts);  +        av_dict_free(&amp;op  +        if (ret &lt; 0)  +            return ret;  +    }  +  +    if (av_opt_get(in, &#34;location&#34;, AV_OPT_SEARCH_CHILDREN, &amp;new_url) &gt;= 0) {  +        c-&gt;base_url = av_strdup(new_url);  +    } else {  +        c-&gt;base_url = av_strdup(url);  +    }  +  +    filesize = avio_size(in);  +    if (filesize &lt;= 0) {  +        filesize = 8 * 1024;  +    }  +  +    buffer = av_mallocz(filesize);  +    if (!buffer) {  +        return AVERROR(ENOMEM);  +    }  +  +    filesize = avio_read(in, buffer, filesize);  +    if (filesize &gt; 0) {  +        LIBXML_TEST_VERS  +  +        doc = xmlReadMemory(buffer, filesize, c-&gt;base_url, NULL, 0);  +        root_element = xmlDocGetRootElement(doc);  +        node = root_element;  +  +        if (!node) {  +            ret = AVERROR_INVALIDDATA;  +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing root node\n&#34;, url);  +            goto cleanup;  +        }  +  +        if (node-&gt;type != XML_ELEMENT_NODE ||  +            xmlStrcm (const xmlChar *)&#34;MPD&#34;)) {  +            ret = AVERROR_INVALIDDATA;  +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - wrong root node name[%s] type[%d]\n&#34;, url, node-&gt;name, (int)node-&gt;type);  +            goto cleanup;  +        }  +  +        val = xmlGetProp(node, &#34;type&#34;);  +        if (!val) {  +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing type attrib\n&#34;, url);  +            ret = AVERROR_INVALIDDATA;  +            goto cleanup;  +        }  +        if (!xmlStrcmp(val, (const xmlChar *)&#34;dynamic&#34;))  +            c-&gt;is_li = 1;  +        xmlFree(val);  +  +        attr = node-&gt;properties;  +        while (attr) {  +            val = xmlGetProp(node, attr-&gt;name);  +  +            if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;availabilityStartTime&#34;)) {  +                 = get_utc_date_time_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;publishTime&#34;)) {  +                 = get_utc_date_time_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;minimumUpdatePeriod&#34;)) {  +                 = get_duration_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;timeShiftBufferDepth&#34;)) {  +                 = get_duration_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;minBufferTime&#34;)) {  +                 = get_duration_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;suggestedPresentationDelay&#34; {  +                 = get_duration_insec(s, (const char *)val);  +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;mediaPresentationDuration&#34;) {  +                 = get_duration_insec(s, (const char *)val);  +            }  +            attr = attr-&gt;next;  +            xmlFree(  +        }  +  +        mpd_baseurl_node = find_child_node_by_name(node, &#34;BaseURL&#34;);  +  +        // at now we can handle only one period, with the longest duration  +        node = xmlFirstElementChild(node);  +        while (node) {  +            if (!xmlStrcmp(node-&gt;name, (const xmlChar *)&#34;Period&#34;)) {  +                 = 0;  +                 = 0;  +                 = node-&gt;properties;  +                 (attr) {  +                 = xmlGetProp(node, attr-&gt;name);  +                 (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;duration&#34;)) {  +                 = get_duration_insec(s, (const char *)val);  +                 else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;start&#34;)) {  +                 = get_duration_insec(s, (const char *)val);  +                  +                 = attr-&gt;next;  +                  +                  +                 ((perdiod_duration_sec) &gt;= (c-&gt;period_duration)) {  +                 = node;  +                 = perdiod_duration_sec;  +                 = perdiod_start_sec;  +                 (c-&gt;period_start &gt; 0)  +                 = c-&gt;period_duration;  +                  +            }  +            node = xmlNextElementSibling(node);  +        }  +        if (!period_node) {  +            av_log(s AV_LOG_ERROR, &#34;Unable to parse &#39;%s&#39; - missing Period node\n&#34;, url);  +            ret = AVERROR_INVALIDDATA;  +            goto cleanup;  +        }  +  +        adaptionset_node = xmlFirstElementChild(period_no  +        while (adaptionset_node) {  +            if (!xmlStrcmp(adaptionset_node-&gt; (const xmlChar *)&#34;BaseURL&#34;)) {  +                 = adaptionset_node;  +            } else if (!xmlStrcmp(adaptionset_node-&gt; (const xmlChar *)&#34;AdaptationSet&#34;)) {  +                 url, adaptionset_node, mpd_baseurl_node, period_baseurl_node);  +            }  +            adaption = xmlNextElementSibling(adaption  +        }  +        if (c-&gt;cur_video) {  +            c-&gt;cur_v = video_rep_idx;  +            c-&gt;cur_v = 1;  +            av_log(s AV_LOG_VERBOSE, &#34;rep_idx[%d]\n&#34;, (int)c-&gt;cur_video-&gt;rep_idx);  +            av_log(s AV_LOG_VERBOSE, &#34;rep_count[%d]\n&#34;, (int)video_rep_idx);  +        }  +        if (c-&gt;cur_audio) {  +            c-&gt;cur_a = audio_rep_idx;  +        }  +cleanup:  +        /*free the document */  +        xmlFreeDoc(doc);  +        xmlCleanupParser  +    } else {  +        av_log(s, AV_LOG_ERROR, &#34;Unable to read to offset &#39;%s&#39;\n&#34;, 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(AVFormatContex *s, struct representation *pls)  +{  +    DASHContext *c = s-&gt;priv_data;  +    int64_t num = 0;  +    int64_t start_time_offset = 0;  +  +    if (c-&gt;is_live) {  +        if (pls-&gt;n_fragments) {  +            num = pls-&gt;first_seq_no;  +        } else if (pls-&gt;n_timelines) {  +            start_ti = get_segment_start_time_based_o 0xFFFFFFFF) - pls-&gt;timelines[pls-&gt;first_seq_ // total duration of playlist  +            if (start_time_offset &lt; 60 * pls-&gt;fragment_timescale)  +                 = 0;  +            else  +                 = start_time_offset - 60 * pls-&gt;fragment_timescale;  +  +            num = calc_next_seg_no_from_timeline pls-&gt;timelines[pls-&gt;first_seq_ + start_time_offset);  +            if (num == -1)  +                 = pls-&gt;first_seq_no;  +        } else {  +            if (pls-&gt;presentation_timeoffset) {  +                 = pls-&gt;presentation_timeoffset * pls-&gt;fragment_timescale / pls-&gt;fragment_duration;  +            } else if (c-&gt;publish_time &gt; 0) {  +                 = pls-&gt;first_seq_no + (((c-&gt;publish_time - c-&gt;availability_start_time) - c-&gt;suggested_presentation_dela * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration;  +            } else {  +                 = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time) - c-&gt;suggested_presentation_dela * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration;  +            }  +        }  +    } else {  +        num = pls-&gt;first_seq_no;  +    }  +    return num;  +}  +  +static int64_t calc_min_seg_no(AVFormatContex *s, struct representation *pls)  +{  +    DASHContext *c = s-&gt;priv_data;  +    int64_t num = 0;  +  +    if (c-&gt;is_live &amp;&amp; pls-&gt;fragment_duration) {  +        num = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time) - c-&gt;time_shift_buffer_depth) * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration;  +    } else {  +        num = pls-&gt;first_seq_no;  +    }  +    return num;  +}  +  +static int64_t calc_max_seg_no(struct representation *pls)  +{  +    DASHContext *c = pls-&gt;parent-&gt;priv_data;  +    int64_t num = 0;  +  +    if (pls-&gt;n_fragments) {  +        num = pls-&gt;first_seq_no + pls-&gt;n_fragments - 1;  +    } else if (pls-&gt;n_timelines) {  +        int i = 0;  +        num = pls-&gt;first_seq_no + pls-&gt;n_timelines - 1;  +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) {  +            num += pls-&gt;timelines[i]-&gt;r;  +        }  +    } else if (c-&gt;is_live) {  +        num = pls-&gt;first_seq_no + (((get_current_time_in_sec() - c-&gt;availability_start_time)) * pls-&gt;fragment_timescale)  / pls-&gt;fragment_duration;  +    } else {  +        num = pls-&gt;first_seq_no + (c-&gt;media_presentation_duratio * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration;  +    }  +  +    return num;  +}  +  +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)  +{  +    if (rep_dest &amp;&amp; rep_src ) {  +        free_timelines_l  +        rep_dest-&gt;timeli    = rep_src-&gt;timelines;  +        rep_dest-&gt;n_time  = rep_src-&gt;n_timelines;  +        rep_dest-&gt;first_ = rep_src-&gt;first_seq_no;  +        rep_dest-&gt;last_s = calc_max_seg_no(rep_dest);  +        rep_src-&gt;timelin = NULL;  +        rep_src-&gt;n_timel = 0;  +        rep_dest-&gt;cur_se = rep_src-&gt;cur_seq_no;  +    }  +}  +  +static void move_segments(struct representation *rep_src, struct representation *rep_dest)  +{  +    if (rep_dest &amp;&amp; rep_src ) {  +        free_fragment_li  +        if (rep_src-&gt;start_number &gt; (rep_dest-&gt;start_number + rep_dest-&gt;n_fragments))  +            rep_dest = 0;  +        else  +            rep_dest += rep_src-&gt;start_number - rep_dest-&gt;start_number;  +        rep_dest-&gt;fragme    = rep_src-&gt;fragments;  +        rep_dest-&gt;n_frag  = rep_src-&gt;n_fragments;  +        rep_dest-&gt;parent  = rep_src-&gt;parent;  +        rep_dest-&gt;last_s = calc_max_seg_no(rep_dest);  +        rep_src-&gt;fragmen = NULL;  +        rep_src-&gt;n_fragm = 0;  +    }  +}  +  +  +static int refresh_manifest(AVFormatConte *s)  +{  +  +    int ret = 0;  +    DASHContext *c = s-&gt;priv_data;  +  +    // save current context  +    struct representation *cur_video =  c-&gt;cur_video;  +    struct representation *cur_audio =  c-&gt;cur_audio;  +    char *base_url = c-&gt;base_url;  +  +    c-&gt;base_url = NULL;  +    c-&gt;cur_video = NULL;  +    c-&gt;cur_audio = NULL;  +    ret = parse_manifest(s, s-&gt;filename, NULL);  +    if (ret)  +        goto finish;  +  +    if (cur_video &amp;&amp; cur_video-&gt;timelines || cur_audio &amp;&amp; cur_audio-&gt;timelines) {  +        // calc current time  +        int64_t currentVideoTime = 0;  +        int64_t currentAudioTime = 0;  +        if (cur_video &amp;&amp; cur_video-&gt;timelines)  +            currentV = get_segment_start_time_based_o cur_video-&gt;cur_seq_no) / cur_video-&gt;fragment_timescale;  +        if (cur_audio &amp;&amp; cur_audio-&gt;timelines)  +            currentA = get_segment_start_time_based_o cur_audio-&gt;cur_seq_no) / cur_audio-&gt;fragment_timescale;  +        // update segments  +        if (cur_video &amp;&amp; cur_video-&gt;timelines) {  +            c-&gt;cur_v = calc_next_seg_no_from_timeline currentVideoTime * cur_video-&gt;fragment_timescale - 1);  +            if (c-&gt;cur_video-&gt;cur_seq_no &gt;= 0) {  +                 cur_video);  +            }  +        }  +        if (cur_audio &amp;&amp; cur_audio-&gt;timelines) {  +            c-&gt;cur_a = calc_next_seg_no_from_timeline currentAudioTime * cur_audio-&gt;fragment_timescale - 1);  +            if (c-&gt;cur_audio-&gt;cur_seq_no &gt;= 0) {  +               mo cur_audio);  +            }  +        }  +    }  +    if (cur_video &amp;&amp; cur_video-&gt;fragments) {  +        move_segments(c- cur_video);  +    }  +    if (cur_audio &amp;&amp; cur_audio-&gt;fragments) {  +        move_segments(c- cur_audio);  +    }  +  +finish:  +    // restore context  +    if (c-&gt;base_url)  +        av_free(base_url  +    else  +        c-&gt;base_url  = base_url;  +    if (c-&gt;cur_audio)  +        free_representat  +    if (c-&gt;cur_video)  +        free_representat  +    c-&gt;cur_audio = cur_audio;  +    c-&gt;cur_video = cur_video;  +    return ret;  +}  +  +static struct fragment *get_current_fragment(struct representation *pls)  +{  +    int64_t min_seq_no = 0;  +    int64_t max_seq_no = 0;  +    struct fragment *seg = NULL;  +    struct fragment *seg_ptr = NULL;  +    DASHContext *c = pls-&gt;parent-&gt;priv_data;  +  +    while (( !ff_check_interrupt(c-&gt;interru pls-&gt;n_fragments &gt; 0)) {  +        if (pls-&gt;cur_seq_no &lt; pls-&gt;n_fragments) {  +            seg_ptr = pls-&gt;fragments[pls-&gt;cur_seq_no  +            seg = av_mallocz(sizeof(struct fragment));  +            if (!seg) {  +                 NULL;  +            }  +            seg-&gt;url = av_strdup(seg_ptr-&gt;url);  +            if (!seg-&gt;url) {  +                  +                 NULL;  +            }  +            seg-&gt;siz = seg_ptr-&gt;size;  +            seg-&gt;url = seg_ptr-&gt;url_offset;  +            return seg;  +        } else if (c-&gt;is_live) {  +            av_uslee  +            refresh_  +        } else {  +            break;  +        }  +    }  +    if (c-&gt;is_live) {  +        while (!ff_check_interrupt(c-&gt;interr {  +            min_seq_ = calc_min_seg_no(pls-&gt;parent, pls);  +            max_seq_ = calc_max_seg_no(pls);  +  +            if (pls-&gt;cur_seq_no &lt;= min_seq_no) {  +                 AV_LOG_VERBOSE, &#34;old fragment: cur[%&#34;PRId64&#34;] min[%&#34;PRId64&#34;] max[%&#34;PRId64&#34;], playlist %d\n&#34;, (int64_t)pls-&gt;cur_seq_no, min_seq_no, max_seq_no, (int)pls-&gt;rep_idx);  +                 (c-&gt;is_live &amp;&amp; (pls-&gt;timelines || pls-&gt;fragments)) {  +                  +                  +                 = calc_cur_seg_no(pls-&gt;parent, pls);  +            } else if (pls-&gt;cur_seq_no &gt; max_seq_no) {  +                 AV_LOG_VERBOSE, &#34;new fragment: min[%&#34;PRId64&#34;] max[%&#34;PRId64&#34;], playlist %d\n&#34;, min_seq_no, max_seq_no, (int)pls-&gt;rep_idx);  +                  +                 (c-&gt;is_live &amp;&amp; (pls-&gt;timelines || pls-&gt;fragments)) {  +                  +                  +                  +            }  +            break;  +        }  +        seg = av_mallocz(sizeof(struct fragment));  +        if (!seg) {  +            return NULL;  +        }  +    } else if (pls-&gt;cur_seq_no &lt;= pls-&gt;last_seq_no) {  +        seg = av_mallocz(sizeof(struct fragment));  +        if (!seg) {  +            return NULL;  +        }  +    }  +    if (seg) {  +        if (pls-&gt;tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {  +            int64_t val = pls-&gt;tmp_url_type == TMP_URL_TYPE_NUMBER ? pls-&gt;cur_seq_no : get_segment_start_time_based_o pls-&gt;cur_seq_no);  +            int size = snprintf(NULL, 0, pls-&gt;url_template_format, val); // calc needed buffer size  +  +            if (size &gt; 0) {  +                 *tmp_val = av_mallocz(size + 1);  +                 size+1, pls-&gt;url_template_format, val);  +                 = av_strireplace(pls-&gt;url_templa pls-&gt;url_template_pattern, tmp_val);  +                  +            }  +        }  +  +        if (!seg-&gt;url) {  +            av_log(p AV_LOG_ERROR, &#34;Unable to resolve template url &#39;%s&#39;\n&#34;, pls-&gt;url_template);  +            seg-&gt;url = av_strdup(pls-&gt;url_template);  +            if (!seg-&gt;url) {  +                 NULL;  +            }  +        }  +  +        seg-&gt;size = -1;  +    }  +  +    return seg;  +}  +  +enum ReadFromURLMode {  +    READ_NORMAL,  +    READ_COMPLETE,  +};  +  +static int read_from_url(struct representation *pls, struct fragment *seg,  +                 *buf, int buf_size,  +                 ReadFromURLMode mode)  +{  +    int ret;  +  +    /* limit read if the fragment was only a part of a file */  +    if (seg-&gt;size &gt;= 0)  +        buf_size = FFMIN(buf_size, pls-&gt;cur_seg_size - pls-&gt;cur_seg_offset);  +  +    if (mode == READ_COMPLETE) {  +        ret = avio_read(pls-&gt;input, buf, buf_size);  +        if (ret &lt; buf_size) {  +            av_log(p AV_LOG_WARNING, &#34;Could not read complete fragment.\n&#34;);  +        }  +    } else {  +        ret = avio_read(pls-&gt;input, buf, buf_size);  +    }  +    if (ret &gt; 0)  +        pls-&gt;cur_seg_off += 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(&amp;opts, &#34;user-agent&#34;, c-&gt;user_agent, 0);  +    av_dict_set(&amp;opts, &#34;cookies&#34;, c-&gt;cookies, 0);  +    av_dict_set(&amp;opts, &#34;headers&#34;, c-&gt;headers, 0);  +    if (c-&gt;is_live) {  +        av_dict_set(&amp;opt &#34;seekable&#34;, &#34;0&#34;, 0);  +    }  +  +    if (seg-&gt;size &gt;= 0) {  +        /* try to restrict the HTTP request to the part we want  +         * (if this is in fact a HTTP request) */  +        av_dict_set_int( &#34;offset&#34;, seg-&gt;url_offset, 0);  +        av_dict_set_int( &#34;end_offset&#34;, seg-&gt;url_offset + seg-&gt;size, 0);  +    }  +  +    ff_make_absolute_url(url MAX_URL_SIZE, c-&gt;base_url, seg-&gt;url);  +    av_log(pls-&gt;parent, AV_LOG_VERBOSE, &#34;DASH request for url &#39;%s&#39;, offset %&#34;PRId64&#34;, playlist %d\n&#34;,  +           url, seg-&gt;url_offset, pls-&gt;rep_idx);  +    ret = open_url(pls-&gt;parent, &amp;pls-&gt;input, url, c-&gt;avio_opts, opts, NULL);  +    if (ret &lt; 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 &amp;&amp; seg-&gt;url_offset) {  +        int64_t seekret = avio_seek(pls-&gt;input, seg-&gt;url_offset, SEEK_SET);  +        if (seekret &lt; 0) {  +            av_log(p AV_LOG_ERROR, &#34;Unable to seek to offset %&#34;PRId64&#34; of DASH fragment &#39;%s&#39;\n&#34;, seg-&gt;url_offset, seg-&gt;url);  +            ret = (int) seekret;  +            ff_forma &amp;pls-&gt;input);  +        }  +    }  +  +cleanup:  +    av_dict_free(&amp;opts);  +    pls-&gt;cur_seg_offset = 0;  +    pls-&gt;cur_seg_size = seg-&gt;size;  +    return ret;  +}  +  +static int update_init_section(struct representation *pls)  +{  +    static const int max_init_section_size = 1024*1024;  +    DASHContext *c = pls-&gt;parent-&gt;priv_data;  +    int64_t sec_size = 0;  +    int64_t urlsize = 0;  +    int ret = 0;  +  +    /* read init section only once per representation */  +    if (!pls-&gt;init_section || pls-&gt;init_sec_buf) {  +        return 0;  +    }  +  +    ret = open_input(c, pls, pls-&gt;init_section);  +    if (ret &lt; 0) {  +        av_log(pls-&gt;pare AV_LOG_WARNING, &#34;Failed to open an initialization section in playlist %d\n&#34;, pls-&gt;rep_idx);  +        return ret;  +    }  +  +    if (pls-&gt;init_section-&gt;size &gt;= 0) {  +        sec_size = pls-&gt;init_section-&gt;size;  +    } else if ((urlsize = avio_size(pls-&gt;input)) &gt;= 0) {  +        sec_size = urlsize;  +    } else {  +        sec_size = max_init_section_size;  +    }  +    av_log(pls-&gt;parent, AV_LOG_DEBUG, &#34;Downloading an initialization section of size %&#34;PRId64&#34;\n&#34;, sec_size);  +    sec_size = FFMIN(sec_size, max_init_section_size);  +    av_fast_malloc(&amp;pls-&gt;ini &amp;pls-&gt;init_sec_buf_size, sec_size);  +    ret = read_from_url(pls, pls-&gt;init_section, pls-&gt;init_sec_buf, pls-&gt;init_sec_buf_size, READ_COMPLETE);  +    ff_format_io_close(pls-&gt; &amp;pls-&gt;input);  +    if (ret &lt; 0)  +        return ret;  +  +    if (pls-&gt;fix_multiple_stsd_order &amp;&amp; pls-&gt;rep_idx &gt; 0) {  +        uint8_t **stsd_entries = NULL;  +        int *stsd_entries_size = NULL;  +        int i = 4;  +  +        while (i &lt;= (ret - 4)) {  +            // find start stsd atom  +            if (!memcmp(pls-&gt;init_sec_buf + i, &#34;stsd&#34;, 4)) {  +                 1B version  +                 3B flags  +                 4B num of entries */  +                 stsd_first_offset = i + 8;  +                 stsd_offset = 0;  +                 j = 0;  +                 stsd_count = AV_RB32(pls-&gt;init_sec_buf + stsd_first_offset);  +                 += 4;  +                 (stsd_count != pls-&gt;rep_count) {  +                  +                  +                  +                 find all stsd entries  +                 = av_mallocz_array(stsd_count, sizeof(*stsd_entries));  +                 = av_mallocz_array(stsd_count, sizeof(*stsd_entries_size));  +                 (j = 0; j &lt; stsd_count; ++j) {  +                 4B - size  +                 4B - format */  +                 = AV_RB32(pls-&gt;init_sec_buf + stsd_first_offset + stsd_offset);  +                 = av_malloc(stsd_entries_size[j]  +                 pls-&gt;init_sec_buf + stsd_first_offset + stsd_offset, stsd_entries_size[j]);  +                 += stsd_entries_size[j];  +                  +                 reorder stsd entries  +                 as first put stsd entry for current representation  +                 = pls-&gt;rep_idx;  +                 = stsd_first_offset;  +                 + stsd_offset, stsd_entries[j], stsd_entries_size[j]);  +                 += stsd_entries_size[j];  +                 (j = 0; j &lt; stsd_count; ++j) {  +                 (j != pls-&gt;rep_idx) {  +                 + stsd_offset, stsd_entries[j], stsd_entries_size[j]);  +                 += stsd_entries_size[j];  +                  +                  +                  +                  +                  +                  +            }  +            i++;  +        }  +    }  +  +    av_log(pls-&gt;parent, AV_LOG_TRACE, &#34;pls[%p] init section size[%d]\n&#34;, pls, (int)ret);  +    pls-&gt;init_sec_data_len = ret;  +    pls-&gt;init_sec_buf_read_o = 0;  +  +    return 0;  +}  +  +static int64_t seek_data(void *opaque, int64_t offset, int whence)  +{  +    struct representation *v = opaque;  +    if (v-&gt;n_fragments &amp;&amp; !v-&gt;init_sec_data_len) {  +        return avio_seek(v-&gt;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-&gt;parent-&gt;priv_data;  +  +restart:  +    if (!v-&gt;input) {  +        free_fragment(&amp;v  +        v-&gt;cur_seg = get_current_fragment(v);  +        if (!v-&gt;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-&gt;cur_seg);  +        if (ret &lt; 0) {  +            if (ff_check_interrupt(c-&gt;interru {  +                 end;  +                 = AVERROR_EXIT;  +            }  +            av_log(v AV_LOG_WARNING, &#34;Failed to open fragment of playlist %d\n&#34;, v-&gt;rep_idx);  +            v-&gt;cur_s  +            goto restart;  +        }  +    }  +  +    if (v-&gt;init_sec_buf_read_offset &lt; v-&gt;init_sec_data_len) {  +        /* Push init section out first before first actual fragment */  +        int copy_size = FFMIN(v-&gt;init_sec_data_len - v-&gt;init_sec_buf_read_offset, buf_size);  +        memcpy(buf, v-&gt;init_sec_buf, copy_size);  +        v-&gt;init_sec_buf_ += copy_size;  +        ret = copy_size;  +        goto end;  +    }  +  +    /* check the v-&gt;cur_seg, if it is null, get current and double check if the new v-&gt;cur_seg*/  +    if (!v-&gt;cur_seg) {  +        v-&gt;cur_seg = get_current_fragment(v);  +    }  +    if (!v-&gt;cur_seg) {  +        ret = AVERROR_EOF;  +        goto end;  +    }  +    ret = read_from_url(v, v-&gt;cur_seg, buf, buf_size, READ_NORMAL);  +    if (ret &gt; 0)  +        goto end;  +  +    if (!v-&gt;is_restart_needed)  +        v-&gt;cur_seq_no++;  +    v-&gt;is_restart_needed = 1;  +  +/*  +    ff_format_io_close(v-&gt;pa &amp;v-&gt;input);  +    v-&gt;cur_seq_no++;  +    goto restart;  +*/  +end:  +    return ret;  +}  +  +static int save_avio_options(AVFormatCont *s)  +{  +    DASHContext *c = s-&gt;priv_data;  +    const char *opts[] = { &#34;headers&#34;, &#34;user_agent&#34;, &#34;user-agent&#34;, &#34;cookies&#34;, NULL }, **opt = opts;  +    uint8_t *buf = NULL;  +    int ret = 0;  +  +    while (*opt) {  +        if (av_opt_get(s-&gt;pb, *opt, AV_OPT_SEARCH_CHILDREN, &amp;buf) &gt;= 0) {  +            if (buf[0] != &#39;\0&#39;) {  +                 = av_dict_set(&amp;c-&gt;avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL);  +                 (ret &lt; 0)  +                 ret;  +            }  +        }  +        opt++;  +    }  +  +    return ret;  +}  +  +static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,  +                 flags, AVDictionary **opts)  +{  +    av_log(s, AV_LOG_ERROR,  +           &#34;A DASH playlist item &#39;%s&#39; referred to an external file &#39;%s&#39;. &#34;  +           &#34;Opening this file was forbidden for security reasons\n&#34;,  +           s-&gt;filenam url);  +    return AVERROR(EPERM);  +}  +  +static int reopen_demux_for_component(AVF *s, struct representation *pls)  +{  +    DASHContext *c = s-&gt;priv_data;  +    AVInputFormat *in_fmt = NULL;  +    AVDictionary  *in_fmt_opts = NULL;  +    uint8_t *avio_ctx_buffer  = NULL;  +    int ret = 0;  +  +    if (pls-&gt;ctx) {  +        /* note: the internal buffer could have changed, and be != avio_ctx_buffer */  +        av_freep(&amp;pls-&gt;p  +        memset(&amp;pls-&gt;pb, 0x00, sizeof(AVIOContext));  +        pls-&gt;ctx-&gt;pb = NULL;  +        avformat_close_i  +        pls-&gt;ctx = NULL;  +    }  +    if (!(pls-&gt;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_co  +        pls-&gt;ctx = NULL;  +        goto fail;  +    }  +    if (c-&gt;is_live) {  +        ffio_init_contex avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);  +    } else {  +        ffio_init_contex avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);  +    }  +    pls-&gt;pb.seekable = 0;  +  +    if ((ret = ff_copy_whiteblacklists(pls-&gt;c s)) &lt; 0)  +        goto fail;  +  +    pls-&gt;ctx-&gt;flags = AVFMT_FLAG_CUSTOM_IO;  +    pls-&gt;ctx-&gt;probesize = 1024 * 4;  +    pls-&gt;ctx-&gt;max_analyze_du = 4 * AV_TIME_BASE;  +    ret = av_probe_input_buffer(&amp;pls-&gt;pb &amp;in_fmt, &#34;&#34;, NULL, 0, 0);  +    if (ret &lt; 0) {  +        av_log(s, AV_LOG_ERROR, &#34;Error when loading first fragment, playlist %d\n&#34;, (int)pls-&gt;rep_idx);  +        avformat_free_co  +        pls-&gt;ctx = NULL;  +        goto fail;  +    }  +  +    pls-&gt;ctx-&gt;pb = &amp;pls-&gt;pb;  +    pls-&gt;ctx-&gt;io_open  = nested_io_open;  +  +    // provide additional information from mpd if available  +    ret = avformat_open_input(&amp;pls-&gt;ctx, &#34;&#34;, in_fmt, &amp;in_fmt_opts); //pls-&gt;init_section-&gt;url  +    av_dict_free(&amp;in_fmt_opt  +    if (ret &lt; 0)  +        goto fail;  +    if (pls-&gt;n_fragments) {  +        ret = avformat_find_stream_info(pls- NULL);  +        if (ret &lt; 0)  +            goto fail;  +    }  +  +fail:  +    return ret;  +}  +  +static int open_demux_for_component(AVFor *s, struct representation *pls)  +{  +    int ret = 0;  +    int i;  +  +    pls-&gt;parent = s;  +    pls-&gt;cur_seq_no  = calc_cur_seg_no(s, pls);  +    pls-&gt;last_seq_no = calc_max_seg_no(pls);  +  +    ret = reopen_demux_for_component(s, pls);  +    if (ret &lt; 0) {  +        goto fail;  +    }  +    for (i = 0; i &lt; pls-&gt;ctx-&gt;nb_streams; i++) {  +        AVStream *st = avformat_new_stream(s, NULL);  +        AVStream *ist = pls-&gt;ctx-&gt;streams[i];  +        if (!st) {  +            ret = AVERROR(ENOMEM);  +            goto fail;  +        }  +        st-&gt;id = i;  +        avcodec_paramete pls-&gt;ctx-&gt;streams[i]-&gt;codecpar  +        avpriv_set_pts_i ist-&gt;pts_wrap_bits, ist-&gt;time_base.num, ist-&gt;time_base.den);  +    }  +  +    return 0;  +fail:  +    return ret;  +}  +  +static int dash_read_header(AVFormatConte *s)  +{  +    void *u = (s-&gt;flags &amp; AVFMT_FLAG_CUSTOM_IO) ? NULL : s-&gt;pb;  +    DASHContext *c = s-&gt;priv_data;  +    int ret = 0;  +    int stream_index = 0;  +  +    c-&gt;interrupt_callback = &amp;s-&gt;interrupt_callback;  +    // if the URL context is good, read important options we must broker later  +    if (u) {  +        update_options(&amp; &#34;user-agent&#34;, u);  +        update_options(&amp; &#34;cookies&#34;, u);  +        update_options(&amp; &#34;headers&#34;, u);  +    }  +  +    if ((ret = parse_manifest(s, s-&gt;filename, s-&gt;pb)) &lt; 0)  +        goto fail;  +  +    if ((ret = save_avio_options(s)) &lt; 0)  +        goto fail;  +  +    /* If this isn&#39;t a live stream, fill the total duration of the  +     * stream. */  +    if (!c-&gt;is_live) {  +        s-&gt;duration = (int64_t) c-&gt;media_presentation_duration * AV_TIME_BASE;  +    }  +  +    /* Open the demuxer for curent video and current audio components if available */  +    if (!ret &amp;&amp; c-&gt;cur_video) {  +        ret = open_demux_for_component(s, c-&gt;cur_video);  +        if (!ret) {  +            c-&gt;cur_v = stream_index;  +            ++stream  +        } else {  +            free_rep  +            c-&gt;cur_v = NULL;  +        }  +    }  +  +    if (!ret &amp;&amp; c-&gt;cur_audio) {  +        ret = open_demux_for_component(s, c-&gt;cur_audio);  +        if (!ret) {  +            c-&gt;cur_a = stream_index;  +            ++stream  +        } else {  +            free_rep  +            c-&gt;cur_a = 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-&gt;cur_video) {  +            av_progr 0, c-&gt;cur_video-&gt;stream_index);  +        }  +        if (c-&gt;cur_audio) {  +            av_progr 0, c-&gt;cur_audio-&gt;stream_index);  +        }  +    }  +  +    return 0;  +fail:  +    return ret;  +}  +  +static int dash_read_packet(AVFormatConte *s, AVPacket *pkt)  +{  +    DASHContext *c = s-&gt;priv_data;  +    int ret = 0;  +    struct representation *cur = NULL;  +  +    if (!c-&gt;cur_audio &amp;&amp; !c-&gt;cur_video ) {  +        return AVERROR_INVALIDDATA;  +    }  +    if (c-&gt;cur_audio &amp;&amp; !c-&gt;cur_video) {  +        cur = c-&gt;cur_audio;  +    } else if (!c-&gt;cur_audio &amp;&amp; c-&gt;cur_video) {  +        cur = c-&gt;cur_video;  +    } else if (c-&gt;cur_video-&gt;cur_timestamp &lt; c-&gt;cur_audio-&gt;cur_timestamp) {  +        cur = c-&gt;cur_video;  +    } else {  +        cur = c-&gt;cur_audio;  +    }  +  +    if (cur-&gt;ctx) {  +        while (!ff_check_interrupt(c-&gt;interr &amp;&amp; !ret) {  +            ret = av_read_frame(cur-&gt;ctx, pkt);  +            if (ret &gt;= 0) {  +                 If we got a packet, return it */  +                 = av_rescale(pkt-&gt;pts, (int64_t)cur-&gt;ctx-&gt;streams[0]- * 90000, cur-&gt;ctx-&gt;streams[0]-&gt;time_bas  +                 = cur-&gt;stream_index;  +                 0;  +            }  +            if (cur-&gt;is_restart_needed) {  +                 (!ff_check_interrupt(c-&gt;interr {  +                 = 0;  +                 = 0;  +                 (cur-&gt;input)  +                 &amp;cur-&gt;input);  +                 = reopen_demux_for_component(s, cur);  +                 (c-&gt;is_live &amp;&amp; ret) {  +                  +                  +                  +                  +                  +                 = 0;  +            }  +  +        }  +    }  +    return AVERROR_EOF;  +}  +  +static int dash_close(AVFormatContext *s)  +{  +    DASHContext *c = s-&gt;priv_data;  +    if (c-&gt;cur_audio) {  +        free_representat  +    }  +    if (c-&gt;cur_video) {  +        free_representat  +    }  +  +    av_freep(&amp;c-&gt;cookies);  +    av_freep(&amp;c-&gt;user_agent)  +    av_dict_free(&amp;c-&gt;avio_op  +    av_freep(&amp;c-&gt;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-&gt;parent, AV_LOG_VERBOSE, &#34;DASH seek pos[%&#34;PRId64&#34;ms], playlist %d\n&#34;, seek_pos_msec, pls-&gt;rep_idx);  +  +    // single fragment mode  +    if (pls-&gt;n_fragments == 1) {  +        pls-&gt;cur_timesta = 0;  +        pls-&gt;cur_seg_off = 0;  +        ff_read_frame_fl  +        return av_seek_frame(pls-&gt;ctx, -1, seek_pos_msec * 1000, flags);  +    }  +  +    if (pls-&gt;input)  +        ff_format_io_clo &amp;pls-&gt;input);  +  +    // find the nearest fragment  +    if (pls-&gt;n_timelines &gt; 0 &amp;&amp; pls-&gt;fragment_timescale &gt; 0) {  +        int64_t num = pls-&gt;first_seq_no;  +        av_log(pls-&gt;pare AV_LOG_VERBOSE, &#34;dash_seek with SegmentTimeline start n_timelines[%d] &#34;  +               &#34;l playlist %d.\n&#34;,  +               (i (int64_t)pls-&gt;last_seq_no, (int)pls-&gt;rep_idx);  +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) {  +            if (pls-&gt;timelines[i]-&gt;t &gt; 0) {  +                 = pls-&gt;timelines[i]-&gt;t;  +            }  +            duration += pls-&gt;timelines[i]-&gt;d;  +            if (seek_pos_msec &lt; ((duration * 1000) /  pls-&gt;fragment_timescale)) {  +                 set_seq_num;  +            }  +            for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) {  +                 += pls-&gt;timelines[i]-&gt;d;  +                  +                 (seek_pos_msec &lt; ((duration * 1000) /  pls-&gt;fragment_timescale)) {  +                 set_seq_num;  +                  +            }  +            num++;  +        }  +  +set_seq_num:  +        pls-&gt;cur_seq_no = num &gt; pls-&gt;last_seq_no ? pls-&gt;last_seq_no : num;  +        av_log(pls-&gt;pare AV_LOG_VERBOSE, &#34;dash_seek with SegmentTimeline end cur_seq_no[%&#34;PRId64&#34;], playlist %d.\n&#34;,  +               (i (int)pls-&gt;rep_idx);  +    } else if (pls-&gt;fragment_duration &gt; 0) {  +        pls-&gt;cur_seq_no = pls-&gt;first_seq_no + ((seek_pos_msec * pls-&gt;fragment_timescale) / pls-&gt;fragment_duration) / 1000;  +    } else {  +        av_log(pls-&gt;pare AV_LOG_ERROR, &#34;dash_seek missing fragment_duration\n&#34;);  +        pls-&gt;cur_seq_no = pls-&gt;first_seq_no;  +    }  +    pls-&gt;cur_timestamp = 0;  +    pls-&gt;cur_seg_offset = 0;  +    pls-&gt;init_sec_buf_read_o = 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-&gt;priv_data;  +    int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,  +                  +                 &amp; AVSEEK_FLAG_BACKWARD ?  +                 : AV_ROUND_UP);  +    if ((flags &amp; AVSEEK_FLAG_BYTE) || c-&gt;is_live)  +        return AVERROR(ENOSYS);  +    if (c-&gt;cur_audio) {  +        ret = dash_seek(s, c-&gt;cur_audio, seek_pos_msec, flags);  +    }  +    if (!ret &amp;&amp; c-&gt;cur_video) {  +        ret = dash_seek(s, c-&gt;cur_video, seek_pos_msec, flags);  +    }  +    return ret;  +}  +  +static int dash_probe(AVProbeData *p)  +{  +    if (!av_stristr(p-&gt;buf, &#34;&lt;MPD&#34;))  +        return 0;  +  +    if (av_stristr(p-&gt;buf, &#34;dash:profile:isoff-on-demand: ||  +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-live:2011&#34; ||  +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-live:2012&#34; ||  +        av_stristr(p-&gt;bu &#34;dash:profile:isoff-main:2011&#34; {  +        return AVPROBE_SCORE_MAX;  +    }  +    if (av_stristr(p-&gt;buf, &#34;dash:profile&#34;)) {  +        return AVPROBE_SCORE_MAX / 2;  +    }  +  +    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 = &#34;dash&#34;,  +    .item_name  = av_default_item_name,  +    .option     = dash_options,  +    .version    = LIBAVUTIL_VERSION_INT,  +};  +  +AVInputFormat ff_dash_demuxer = {  +    .name           = &#34;dash&#34;,  +    .long_name      = NULL_IF_CONFIG_SMALL(&#34;Dynamic Adaptive Streaming over HTTP&#34;),  +    .priv_class     = &amp;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.11.0 (Apple Git-81)     ______________________________  ffmpeg-devel mailing list    ffmpeg-devel@ffmpeg.org  ffmpeg.org ffmpeg.org
Rodger Combs Aug. 28, 2017, 9:30 a.m. UTC | #5
I would expect parsing the number internally and using the additional arg to be simpler and easier to verify than format string validation.

> On Aug 28, 2017, at 04:28, samsamsam <samsamsam@o2.pl> wrote:
> 
> OK. I will.
> What about adding validation of format instead of adding "something like `"%0*"PRId64`"?
> 
> Dnia 28 sierpnia 2017 03:30 Rodger Combs <rodger.combs@gmail.com> napisał(a):
> 
> If you know of such a vulnerability, report it to  <mailto:ffmpeg-security@ffmpeg.org>ffmpeg-security@ffmpeg.org <mailto:ffmpeg-security@ffmpeg.org>. New code with known vulnerabilities will not be accepted.
> 
> Sent from my iPhone
> 
> On Aug 27, 2017, at 14:04, samsamsam < <mailto:samsamsam@o2.pl>samsamsam@o2.pl <mailto:samsamsam@o2.pl>> wrote:
>> get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`
>> 
>> If you afraid about safety then the only thing which need to be added to get_repl_pattern_and_format is validation of format.
>> Simple loop to validate format will be enough. Do you agree? 
>> 
>> Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file.
>> 
>> I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety.
>> 
>> Regards,
>> SSS
>> 
>> Dnia 27 sierpnia 2017 16:34 Rodger Combs < <mailto:rodger.combs@gmail.com>rodger.combs@gmail.com <mailto:rodger.combs@gmail.com>> napisał(a):
>> 
>> You're still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`, and specify an additional "precision" argument you parse from the XML yourself. I can't reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_.
>> 
>> Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That's what it does internally anyway.
>> 
>> On Aug 27, 2017, at 09:19, Steven Liu < <mailto:lq@chinaffmpeg.org>lq@chinaffmpeg.org <mailto:lq@chinaffmpeg.org>> wrote:
>> 
>> ffmpeg need a dash demuxer for demux the dash formats base on
>> https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch <https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch>
>> 
>> TODO:
>> 1. support multi bitrate dash
>> 
>> 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
>> 
>> v10 fixed:
>> 1. fix function API name code style
>> 2. remove redundant strreplace call
>> 3. remove redundant memory operation and check return value from get_content_url()
>> 4. add space between ) and {
>> 5. remove no need to log the value for print
>> 
>> v11 fixed:
>> 1. from atoll to strtoll
>> 
>> v12 fixed:
>> 1. remove strreplace and instead by av_strreplace
>> 
>> v13 fixed:
>> 1. fix bug: cannot play:
>> http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd <http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd>
>> 
>> v14 fixed:
>> 1. fix bug: TLS connection was non-properly terminated
>> 2. fix bug: No trailing CRLF found in HTTP header
>> 
>> v15 fixed:
>> 1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" <https://www.youtube.com/watch?v=XmL19DOP_Ls> | jq -r ".requested_formats[0].manifest_url")
>> 2. code refine for timeline living stream
>> 
>> Reviewed-by: Clément Bœsch < <mailto:u@pkh.me>u@pkh.me <mailto:u@pkh.me>>
>> Reviewed-by: Michael Niedermayer < <mailto:michael@niedermayer.cc>michael@niedermayer.cc <mailto:michael@niedermayer.cc>>
>> Reviewed-by: Carl Eugen Hoyos < <mailto:cehoyos@ag.or.at>cehoyos@ag.or.at <mailto:cehoyos@ag.or.at>>
>> Reviewed-by: Rodger Combs < <mailto:rodger.combs@gmail.com>rodger.combs@gmail.com <mailto:rodger.combs@gmail.com>>
>> Reviewed-by: Moritz Barsnick < <mailto:barsnick@gmx.net>barsnick@gmx.net <mailto:barsnick@gmx.net>>
>> Reviewed-by: Nicolas George < <mailto:george@nsup.org>george@nsup.org <mailto:george@nsup.org>>
>> Reviewed-by: Ricardo Constantino < <mailto:wiiaboo@gmail.com>wiiaboo@gmail.com <mailto:wiiaboo@gmail.com>>
>> Reviewed-by: wm4 < <mailto:nfxjfg@googlemail.com>nfxjfg@googlemail.com <mailto:nfxjfg@googlemail.com>>
>> Tested-by: Andy Furniss < <mailto:adf.lists@gmail.com>adf.lists@gmail.com <mailto:adf.lists@gmail.com>>
>> Reported-by: Andy Furniss < <mailto:adf.lists@gmail.com>adf.lists@gmail.com <mailto:adf.lists@gmail.com>>
>> Signed-off-by: Steven Liu < <mailto:lq@chinaffmpeg.org>lq@chinaffmpeg.org <mailto:lq@chinaffmpeg.org>>
>> Signed-off-by: samsamsam < <mailto:samsamsam@o2.pl>samsamsam@o2.pl <mailto:samsamsam@o2.pl>>
>> ---
>> configure                |    4 +
>> libavformat/Makefile     |    1 +
>> libavformat/allformats.c |    2 +-
>> libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 1987 insertions(+), 1 deletion(-)
>> create mode 100644 libavformat/dashdec.c
>> 
>> diff --git a/configure b/configure
>> index 05f6dcc99a..7a7d61fa13 100755
>> --- a/configure
>> +++ b/configure
>> @@ -272,6 +272,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]
>> @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST="
>>    libvpx
>>    libwavpack
>>    libwebp
>> +    libxml2
>>    libzimg
>>    libzmq
>>    libzvbi
>> @@ -2937,6 +2939,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"
>> @@ -5996,6 +5999,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>>                               check_lib openssl 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
>> 
>> if enabled gcrypt; then
>>    GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index f2b465cfa2..3d478749d0 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab
>> --- /dev/null
>> +++ b/libavformat/dashdec.c
>> @@ -0,0 +1,1981 @@
>> +/*
>> + * Dynamic Adaptive Streaming over HTTP demux
>> + * Copyright (c) 2017  <mailto:samsamsam@o2.pl>samsamsam@o2.pl <mailto: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).
>> +     * */
>> +    int64_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;
>> +    char *url_template_pattern;
>> +    char *url_template_format;
>> +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
>> +
>> +    int64_t fragment_duration;
>> +    int64_t fragment_timescale;
>> +
>> +    int64_t presentation_timeoffset;
>> +
>> +    int64_t cur_seq_no;
>> +    int64_t cur_seg_offset;
>> +    int64_t cur_seg_size;
>> +    struct fragment *cur_seg;
>> +
>> +    /* Currently active Media Initialization Section */
>> +    struct fragment *init_section;
>> +    uint8_t *init_sec_buf;
>> +    uint32_t init_sec_buf_size;
>> +    uint32_t init_sec_data_len;
>> +    uint32_t init_sec_buf_read_offset;
>> +    int fix_multiple_stsd_order;
>> +    int64_t cur_timestamp;
>> +    int is_restart_needed;
>> +};
>> +
>> +typedef struct DASHContext {
>> +    const AVClass *class;
>> +    char *base_url;
>> +    struct representation *cur_video;
>> +    struct representation *cur_audio;
>> +
>> +    /* MediaPresentationDescription Attribute */
>> +    uint64_t media_presentation_duration;
>> +    uint64_t suggested_presentation_delay;
>> +    uint64_t availability_start_time;
>> +    uint64_t publish_time;
>> +    uint64_t minimum_update_period;
>> +    uint64_t time_shift_buffer_depth;
>> +    uint64_t min_buffer_time;
>> +
>> +    /* Period Attribute */
>> +    uint64_t period_duration;
>> +    uint64_t period_start;
>> +
>> +    int is_live;
>> +    AVIOInterruptCB *interrupt_callback;
>> +    char *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 get_current_time_in_sec(void)
>> +{
>> +    return  av_gettime() / 1000000;
>> +}
>> +
>> +static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
>> +{
>> +    struct tm timeinfo;
>> +    int year = 0;
>> +    int month = 0;
>> +    int day = 0;
>> +    int hour = 0;
>> +    int minute = 0;
>> +    int ret = 0;
>> +    float second = 0.0;
>> +
>> +    /* ISO-8601 date parser */
>> +    if (!datetime)
>> +        return 0;
>> +
>> +    ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
>> +    /* year, month, day, hour, minute, second  6 arguments */
>> +    if (ret != 6) {
>> +        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
>> +    }
>> +    timeinfo.tm_year = year - 1900;
>> +    timeinfo.tm_mon  = month - 1;
>> +    timeinfo.tm_mday = day;
>> +    timeinfo.tm_hour = hour;
>> +    timeinfo.tm_min  = minute;
>> +    timeinfo.tm_sec  = (int)second;
>> +
>> +    return av_timegm(&timeinfo);
>> +}
>> +
>> +static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
>> +{
>> +    /* ISO-8601 duration parser */
>> +    uint32_t days = 0;
>> +    uint32_t hours = 0;
>> +    uint32_t mins = 0;
>> +    uint32_t secs = 0;
>> +    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, "get_duration_insec get a wrong time format\n");
>> +            return 0; /* parser error */
>> +        }
>> +        switch (type) {
>> +            case 'D':
>> +                days = (uint32_t)value;
>> +                break;
>> +            case 'H':
>> +                hours = (uint32_t)value;
>> +                break;
>> +            case 'M':
>> +                mins = (uint32_t)value;
>> +                break;
>> +            case 'S':
>> +                secs = (uint32_t)value;
>> +                break;
>> +            default:
>> +                // handle invalid type
>> +                break;
>> +        }
>> +        ptr += size;
>> +    }
>> +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;
>> +}
>> +
>> +static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
>> +{
>> +    int64_t start_time = 0;
>> +    int64_t i = 0;
>> +    int64_t j = 0;
>> +    int64_t num = 0;
>> +
>> +    if (pls->n_timelines) {
>> +        for (i = 0; i < pls->n_timelines; i++) {
>> +            if (pls->timelines[i]->t > 0) {
>> +                start_time = pls->timelines[i]->t;
>> +            }
>> +            if (num == cur_seq_no)
>> +                goto finish;
>> +
>> +            start_time += pls->timelines[i]->d;
>> +            for (j = 0; j < pls->timelines[i]->r; j++) {
>> +                num++;
>> +                if (num == cur_seq_no)
>> +                    goto finish;
>> +                start_time += pls->timelines[i]->d;
>> +            }
>> +            num++;
>> +        }
>> +    }
>> +finish:
>> +    return start_time;
>> +}
>> +
>> +static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
>> +{
>> +    int64_t i = 0;
>> +    int64_t j = 0;
>> +    int64_t num = 0;
>> +    int64_t start_time = 0;
>> +
>> +    for (i = 0; i < pls->n_timelines; i++) {
>> +        if (pls->timelines[i]->t > 0) {
>> +            start_time = pls->timelines[i]->t;
>> +        }
>> +        if (start_time > cur_time)
>> +            goto finish;
>> +
>> +        start_time += pls->timelines[i]->d;
>> +        for (j = 0; j < pls->timelines[i]->r; j++) {
>> +            num++;
>> +            if (start_time > cur_time)
>> +                goto finish;
>> +            start_time += pls->timelines[i]->d;
>> +        }
>> +        num++;
>> +    }
>> +
>> +    return -1;
>> +
>> +finish:
>> +    return num;
>> +}
>> +
>> +static void free_fragment(struct fragment **seg)
>> +{
>> +    if (!(*seg)) {
>> +        return;
>> +    }
>> +    av_freep(&(*seg)->url);
>> +    av_freep(seg);
>> +}
>> +
>> +static void free_fragment_list(struct representation *pls)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < pls->n_fragments; i++) {
>> +        free_fragment(&pls->fragments[i]);
>> +    }
>> +    av_freep(&pls->fragments);
>> +    pls->n_fragments = 0;
>> +}
>> +
>> +static void free_timelines_list(struct representation *pls)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < pls->n_timelines; i++) {
>> +        av_freep(&pls->timelines[i]);
>> +    }
>> +    av_freep(&pls->timelines);
>> +    pls->n_timelines = 0;
>> +}
>> +
>> +static void free_representation(struct representation *pls)
>> +{
>> +    free_fragment_list(pls);
>> +    free_timelines_list(pls);
>> +    free_fragment(&pls->cur_seg);
>> +    free_fragment(&pls->init_section);
>> +    av_freep(&pls->init_sec_buf);
>> +    av_freep(&pls->pb.buffer);
>> +    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_pattern);
>> +    av_free(pls->url_template_format);
>> +    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;
>> +    void *p = NULL;
>> +
>> +    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.
>> +        p = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
>> +        update_options(&c->cookies, "cookies", p);
>> +        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 *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 = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
>> +        av_free(tmp_str);
>> +        tmp_str = url;
>> +    }
>> +    if (rep_bandwidth_val && tmp_str)
>> +        url = av_strireplace(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 find_child_node_by_name(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 int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format)
>> +{
>> +    int ret = -1;
>> +    int marker_len = 0;
>> +    int format_len = 0;
>> +    char *prefix = NULL;
>> +    char *start = NULL;
>> +    char *end = NULL;
>> +
>> +
>> +    if (av_stristr(i_url, i_marker)) {
>> +        *o_pattern = av_strdup(i_marker);
>> +        *o_format = av_strdup("%"PRId64);
>> +        ret = 0;
>> +    } else {
>> +        prefix = av_strdup(i_marker);
>> +        marker_len = strlen(prefix)-1;
>> +        prefix[marker_len] = '\0';
>> +        start = av_stristr(i_url, prefix);
>> +        if (!start)
>> +            goto finish;
>> +
>> +        end = strchr(start + 1, '$');
>> +        if (!end)
>> +            goto finish;
>> +
>> +        if (start[marker_len] != '%')
>> +            goto finish;
>> +
>> +        if (end[-1] != 'd')
>> +            goto finish;
>> +
>> +        format_len = end - start - marker_len - 1 + strlen(PRId64);
>> +        *o_format = av_mallocz(format_len+1);
>> +        av_strlcpy(*o_format, start + marker_len, end - start - marker_len -1);
>> +        av_strlcat(*o_format, PRId64, strlen(*o_format) + strlen(PRId64));
>> +        *o_pattern = av_mallocz(end - start + 2);
>> +        if (*o_pattern) {
>> +            ret = AVERROR(EINVAL);
>> +            goto finish;
>> +        }
>> +        av_strlcpy(*o_pattern, start, end - start + 1);
>> +        ret = 0;
>> +
>> +finish:
>> +        av_free(prefix);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static enum AVMediaType get_content_type(xmlNodePtr node)
>> +{
>> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
>> +    int i = 0;
>> +    const char *attr;
>> +    xmlChar *val = NULL;
>> +
>> +    if (node) {
>> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
>> +            attr = (i) ? "mimeType" : "contentType";
>> +            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);
>> +            if (!rep->init_section->url) {
>> +                av_free(rep->init_section);
>> +                xmlFree(initialization_val);
>> +                return AVERROR(ENOMEM);
>> +            }
>> +            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);
>> +            if (!seg->url) {
>> +                av_free(seg);
>> +                xmlFree(media_val);
>> +                return AVERROR(ENOMEM);
>> +            }
>> +            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)strtoll(val, NULL, 10);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
>> +                tml->r =(int64_t) strtoll(val, NULL, 10);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
>> +                tml->d = (int64_t)strtoll(val, NULL, 10);
>> +//                rep->fragment_duration = (int64_t) strtoll(val, NULL, 10);
>> +            }
>> +            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 *presentation_timeoffset_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 = find_child_node_by_name(representation_node, "SegmentTemplate");
>> +        representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
>> +        representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
>> +
>> +        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;
>> +
>> +            presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
>> +            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);
>> +                if (!rep->init_section->url) {
>> +                    av_free(rep->init_section);
>> +                    av_free(rep);
>> +                    ret = AVERROR(ENOMEM);
>> +                    goto end;
>> +                }
>> +                rep->init_section->size = -1;
>> +                xmlFree(initialization_val);
>> +            }
>> +
>> +            if (media_val) {
>> +                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
>> +                temp_string = rep->url_template;
>> +                if (temp_string) {
>> +                    if (av_stristr(temp_string, "$Number")) {
>> +                        get_repl_pattern_and_format(temp_string, "$Number$", &(rep->url_template_pattern), &(rep->url_template_format));
>> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
>> +                    } else if (av_stristr(temp_string, "$Time")) {
>> +                        get_repl_pattern_and_format(temp_string, "$Time$", &(rep->url_template_pattern), &(rep->url_template_format));
>> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
>> +                    } else {
>> +                        temp_string = NULL;
>> +                    }
>> +                }
>> +                xmlFree(media_val);
>> +            }
>> +
>> +            if (presentation_timeoffset_val) {
>> +                rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
>> +                xmlFree(presentation_timeoffset_val);
>> +            }
>> +            if (duration_val) {
>> +                rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
>> +                xmlFree(duration_val);
>> +            }
>> +            if (timescale_val) {
>> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
>> +                xmlFree(timescale_val);
>> +            }
>> +            if (startnumber_val) {
>> +                rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
>> +                xmlFree(startnumber_val);
>> +            }
>> +
>> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
>> +
>> +            if (!fragment_timeline_node)
>> +                fragment_timeline_node = find_child_node_by_name(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);
>> +            if (!seg->url) {
>> +                av_free(seg);
>> +                ret = AVERROR(ENOMEM);
>> +                goto end;
>> +            }
>> +            seg->size = -1;
>> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
>> +        } else if (representation_segmentlist_node) {
>> +            // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html <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 <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) strtoll(duration_val, NULL, 10);
>> +                xmlFree(duration_val);
>> +            }
>> +            if (timescale_val) {
>> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
>> +                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);
>> +            }
>> +
>> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
>> +
>> +            if (!fragment_timeline_node)
>> +                fragment_timeline_node = find_child_node_by_name(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 {
>> +            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 = get_utc_date_time_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
>> +                c->publish_time = get_utc_date_time_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
>> +                c->minimum_update_period = get_duration_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
>> +                c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
>> +                c->min_buffer_time = get_duration_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
>> +                c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
>> +                c->media_presentation_duration = get_duration_insec(s, (const char *)val);
>> +            }
>> +            attr = attr->next;
>> +            xmlFree(val);
>> +        }
>> +
>> +        mpd_baseurl_node = find_child_node_by_name(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 = get_duration_insec(s, (const char *)val);
>> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
>> +                        perdiod_start_sec = get_duration_insec(s, (const char *)val);
>> +                    }
>> +                    attr = attr->next;
>> +                    xmlFree(val);
>> +                }
>> +                if ((perdiod_duration_sec) >= (c->period_duration)) {
>> +                    period_node = node;
>> +                    c->period_duration = perdiod_duration_sec;
>> +                    c->period_start = perdiod_start_sec;
>> +                    if (c->period_start > 0)
>> +                        c->media_presentation_duration = c->period_duration;
>> +                }
>> +            }
>> +            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;
>> +        }
>> +
>> +        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 = s->priv_data;
>> +    int64_t num = 0;
>> +    int64_t start_time_offset = 0;
>> +
>> +    if (c->is_live) {
>> +        if (pls->n_fragments) {
>> +            num = pls->first_seq_no;
>> +        } else if (pls->n_timelines) {
>> +            start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->t; // total duration of playlist
>> +            if (start_time_offset < 60 * pls->fragment_timescale)
>> +                start_time_offset = 0;
>> +            else
>> +                start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
>> +
>> +            num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->t + start_time_offset);
>> +            if (num == -1)
>> +                num = pls->first_seq_no;
>> +        } else {
>> +            if (pls->presentation_timeoffset) {
>> +                num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
>> +            } else if (c->publish_time > 0) {
>> +                num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
>> +            } else {
>> +                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * 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 = s->priv_data;
>> +    int64_t num = 0;
>> +
>> +    if (c->is_live && pls->fragment_duration) {
>> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
>> +    } else {
>> +        num = pls->first_seq_no;
>> +    }
>> +    return num;
>> +}
>> +
>> +static int64_t calc_max_seg_no(struct representation *pls)
>> +{
>> +    DASHContext *c = pls->parent->priv_data;
>> +    int64_t num = 0;
>> +
>> +    if (pls->n_fragments) {
>> +        num = pls->first_seq_no + pls->n_fragments - 1;
>> +    } else if (pls->n_timelines) {
>> +        int i = 0;
>> +        num = pls->first_seq_no + pls->n_timelines - 1;
>> +        for (i = 0; i < pls->n_timelines; i++) {
>> +            num += pls->timelines[i]->r;
>> +        }
>> +    } else if (c->is_live) {
>> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
>> +    } else {
>> +        num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
>> +    }
>> +
>> +    return num;
>> +}
>> +
>> +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
>> +{
>> +    if (rep_dest && rep_src ) {
>> +        free_timelines_list(rep_dest);
>> +        rep_dest->timelines    = rep_src->timelines;
>> +        rep_dest->n_timelines  = rep_src->n_timelines;
>> +        rep_dest->first_seq_no = rep_src->first_seq_no;
>> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
>> +        rep_src->timelines = NULL;
>> +        rep_src->n_timelines = 0;
>> +        rep_dest->cur_seq_no = rep_src->cur_seq_no;
>> +    }
>> +}
>> +
>> +static void move_segments(struct representation *rep_src, struct representation *rep_dest)
>> +{
>> +    if (rep_dest && rep_src ) {
>> +        free_fragment_list(rep_dest);
>> +        if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
>> +            rep_dest->cur_seq_no = 0;
>> +        else
>> +            rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
>> +        rep_dest->fragments    = rep_src->fragments;
>> +        rep_dest->n_fragments  = rep_src->n_fragments;
>> +        rep_dest->parent  = rep_src->parent;
>> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
>> +        rep_src->fragments = NULL;
>> +        rep_src->n_fragments = 0;
>> +    }
>> +}
>> +
>> +
>> +static int refresh_manifest(AVFormatContext *s)
>> +{
>> +
>> +    int ret = 0;
>> +    DASHContext *c = s->priv_data;
>> +
>> +    // save current context
>> +    struct representation *cur_video =  c->cur_video;
>> +    struct representation *cur_audio =  c->cur_audio;
>> +    char *base_url = c->base_url;
>> +
>> +    c->base_url = NULL;
>> +    c->cur_video = NULL;
>> +    c->cur_audio = NULL;
>> +    ret = parse_manifest(s, s->filename, NULL);
>> +    if (ret)
>> +        goto finish;
>> +
>> +    if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
>> +        // calc current time
>> +        int64_t currentVideoTime = 0;
>> +        int64_t currentAudioTime = 0;
>> +        if (cur_video && cur_video->timelines)
>> +            currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
>> +        if (cur_audio && cur_audio->timelines)
>> +            currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
>> +        // update segments
>> +        if (cur_video && cur_video->timelines) {
>> +            c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
>> +            if (c->cur_video->cur_seq_no >= 0) {
>> +                move_timelines(c->cur_video, cur_video);
>> +            }
>> +        }
>> +        if (cur_audio && cur_audio->timelines) {
>> +            c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
>> +            if (c->cur_audio->cur_seq_no >= 0) {
>> +               move_timelines(c->cur_audio, cur_audio);
>> +            }
>> +        }
>> +    }
>> +    if (cur_video && cur_video->fragments) {
>> +        move_segments(c->cur_video, cur_video);
>> +    }
>> +    if (cur_audio && cur_audio->fragments) {
>> +        move_segments(c->cur_audio, cur_audio);
>> +    }
>> +
>> +finish:
>> +    // restore context
>> +    if (c->base_url)
>> +        av_free(base_url);
>> +    else
>> +        c->base_url  = base_url;
>> +    if (c->cur_audio)
>> +        free_representation(c->cur_audio);
>> +    if (c->cur_video)
>> +        free_representation(c->cur_video);
>> +    c->cur_audio = cur_audio;
>> +    c->cur_video = cur_video;
>> +    return ret;
>> +}
>> +
>> +static struct fragment *get_current_fragment(struct representation *pls)
>> +{
>> +    int64_t min_seq_no = 0;
>> +    int64_t max_seq_no = 0;
>> +    struct fragment *seg = NULL;
>> +    struct fragment *seg_ptr = NULL;
>> +    DASHContext *c = pls->parent->priv_data;
>> +
>> +    while (( !ff_check_interrupt(c->interrupt_callback)&& 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;
>> +        } else if (c->is_live) {
>> +            av_usleep(1000*1000);
>> +            refresh_manifest(pls->parent);
>> +        } else {
>> +            break;
>> +        }
>> +    }
>> +    if (c->is_live) {
>> +        while (!ff_check_interrupt(c->interrupt_callback)) {
>> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
>> +            max_seq_no = calc_max_seg_no(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);
>> +                if (c->is_live && (pls->timelines || pls->fragments)) {
>> +                    refresh_manifest(pls->parent);
>> +                }
>> +                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*1000);
>> +                if (c->is_live && (pls->timelines || pls->fragments)) {
>> +                    refresh_manifest(pls->parent);
>> +                }
>> +                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) {
>> +            int64_t val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no);
>> +            int size = snprintf(NULL, 0, pls->url_template_format, val); // calc needed buffer size
>> +
>> +            if (size > 0) {
>> +                char *tmp_val = av_mallocz(size + 1);
>> +                snprintf(tmp_val, size+1, pls->url_template_format, val);
>> +                seg->url = av_strireplace(pls->url_template, pls->url_template_pattern, tmp_val);
>> +                av_free(tmp_val);
>> +            }
>> +        }
>> +
>> +        if (!seg->url) {
>> +            av_log(pls->parent, AV_LOG_ERROR, "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;
>> +    }
>> +
>> +    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;
>> +
>> +    if (!v->is_restart_needed)
>> +        v->cur_seq_no++;
>> +    v->is_restart_needed = 1;
>> +
>> +/*
>> +    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[0] != '\0') {
>> +                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 DASH 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(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 * 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) {
>> +        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
>> +            ret = av_read_frame(cur->ctx, pkt);
>> +            if (ret >= 0) {
>> +                /* If we got a packet, return it */
>> +                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;
>> +                return 0;
>> +            }
>> +            if (cur->is_restart_needed) {
>> +                while (!ff_check_interrupt(c->interrupt_callback)) {
>> +                    cur->cur_seg_offset = 0;
>> +                    cur->init_sec_buf_read_offset = 0;
>> +                    if (cur->input)
>> +                        ff_format_io_close(cur->parent, &cur->input);
>> +                    ret = reopen_demux_for_component(s, cur);
>> +                    if (c->is_live && ret) {
>> +                        av_usleep(1000*1000);
>> +                        continue;
>> +                    }
>> +                    break;
>> +                }
>> +                cur->is_restart_needed = 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 == 1) {
>> +        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 / 2;
>> +    }
>> +
>> +    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.11.0 (Apple Git-81)
>> 
>> 
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>>  <mailto:ffmpeg-devel@ffmpeg.org>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 Aug. 28, 2017, 10:27 a.m. UTC | #6
> 在 2017年8月28日,18:12,samsamsam <samsamsam@o2.pl> 写道:
> 
> Validation will be very simple. I am talking about something like this:
> static int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format) 
> {
> ...
> +        for(ptr=start + marker_len; ptr < (end - 1); ++ptr) {  /*there is need to check this condition :P */
> +            if (*ptr != '0') {
> +               // Unknown format add log here 
> +                goto finish;
> +            }
> +        }
>         format_len = end - start - marker_len - 1 + strlen(PRId64);
>         *o_format = av_mallocz(format_len+1);
>         strncpy(*o_format, start + marker_len, end - start - marker_len -1);
>         strcat(*o_format, PRId64);
> …
> }
maybe more complex than this way, for example:
%d
%lld
%04lld
%PRId64
%PRId32
%PRId16

and think about the safety :
%02c%lld
%s%d%d%d%d

and so on,blablabla.

maybe we need to think a perfect solution.


> 
> 
> Dnia 28 sierpnia 2017 11:30 Rodger Combs <rodger.combs@gmail.com> napisał(a):
> 
> I would expect parsing the number internally and using the additional arg to be simpler and easier to verify than format string validation.
> 
>> On Aug 28, 2017, at 04:28, samsamsam <samsamsam@o2.pl> wrote:
>> 
>> OK. I will.
>> What about adding validation of format instead of adding "something like `"%0*"PRId64`"?
>> 
>> Dnia 28 sierpnia 2017 03:30 Rodger Combs <rodger.combs@gmail.com> napisał(a):
>> 
>> If you know of such a vulnerability, report it to ffmpeg-security@ffmpeg.org. New code with known vulnerabilities will not be accepted.
>> 
>> Sent from my iPhone
>> 
>> On Aug 27, 2017, at 14:04, samsamsam <samsamsam@o2.pl> wrote:
>>> get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`
>>> 
>>> If you afraid about safety then the only thing which need to be added to get_repl_pattern_and_format is validation of format.
>>> Simple loop to validate format will be enough. Do you agree? 
>>> 
>>> Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file.
>>> 
>>> I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety.
>>> 
>>> Regards,
>>> SSS
>>> 
>>> Dnia 27 sierpnia 2017 16:34 Rodger Combs <rodger.combs@gmail.com> napisał(a):
>>> 
>>> You're still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `"%0*"PRId64`, and specify an additional "precision" argument you parse from the XML yourself. I can't reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_.
>>> 
>>> Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That's what it does internally anyway.
>>> 
>>> On Aug 27, 2017, at 09:19, Steven Liu <lq@chinaffmpeg.org> wrote:
>>> 
>>> ffmpeg need a dash demuxer for demux the dash formats base on
>>> https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch
>>> 
>>> TODO:
>>> 1. support multi bitrate dash
>>> 
>>> 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
>>> 
>>> v10 fixed:
>>> 1. fix function API name code style
>>> 2. remove redundant strreplace call
>>> 3. remove redundant memory operation and check return value from get_content_url()
>>> 4. add space between ) and {
>>> 5. remove no need to log the value for print
>>> 
>>> v11 fixed:
>>> 1. from atoll to strtoll
>>> 
>>> v12 fixed:
>>> 1. remove strreplace and instead by av_strreplace
>>> 
>>> v13 fixed:
>>> 1. fix bug: cannot play:
>>> http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd
>>> 
>>> v14 fixed:
>>> 1. fix bug: TLS connection was non-properly terminated
>>> 2. fix bug: No trailing CRLF found in HTTP header
>>> 
>>> v15 fixed:
>>> 1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" | jq -r ".requested_formats[0].manifest_url")
>>> 2. code refine for timeline living stream
>>> 
>>> 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>
>>> Reviewed-by: wm4 <nfxjfg@googlemail.com>
>>> Tested-by: Andy Furniss <adf.lists@gmail.com>
>>> Reported-by: Andy Furniss <adf.lists@gmail.com>
>>> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
>>> Signed-off-by: samsamsam <samsamsam@o2.pl>
>>> ---
>>> configure                |    4 +
>>> libavformat/Makefile     |    1 +
>>> libavformat/allformats.c |    2 +-
>>> libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
>>> 4 files changed, 1987 insertions(+), 1 deletion(-)
>>> create mode 100644 libavformat/dashdec.c
>>> 
>>> diff --git a/configure b/configure
>>> index 05f6dcc99a..7a7d61fa13 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -272,6 +272,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]
>>> @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST="
>>>    libvpx
>>>    libwavpack
>>>    libwebp
>>> +    libxml2
>>>    libzimg
>>>    libzmq
>>>    libzvbi
>>> @@ -2937,6 +2939,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"
>>> @@ -5996,6 +5999,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>>>                               check_lib openssl 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
>>> 
>>> if enabled gcrypt; then
>>>    GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>> index f2b465cfa2..3d478749d0 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab
>>> --- /dev/null
>>> +++ b/libavformat/dashdec.c
>>> @@ -0,0 +1,1981 @@
>>> +/*
>>> + * 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).
>>> +     * */
>>> +    int64_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;
>>> +    char *url_template_pattern;
>>> +    char *url_template_format;
>>> +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
>>> +
>>> +    int64_t fragment_duration;
>>> +    int64_t fragment_timescale;
>>> +
>>> +    int64_t presentation_timeoffset;
>>> +
>>> +    int64_t cur_seq_no;
>>> +    int64_t cur_seg_offset;
>>> +    int64_t cur_seg_size;
>>> +    struct fragment *cur_seg;
>>> +
>>> +    /* Currently active Media Initialization Section */
>>> +    struct fragment *init_section;
>>> +    uint8_t *init_sec_buf;
>>> +    uint32_t init_sec_buf_size;
>>> +    uint32_t init_sec_data_len;
>>> +    uint32_t init_sec_buf_read_offset;
>>> +    int fix_multiple_stsd_order;
>>> +    int64_t cur_timestamp;
>>> +    int is_restart_needed;
>>> +};
>>> +
>>> +typedef struct DASHContext {
>>> +    const AVClass *class;
>>> +    char *base_url;
>>> +    struct representation *cur_video;
>>> +    struct representation *cur_audio;
>>> +
>>> +    /* MediaPresentationDescription Attribute */
>>> +    uint64_t media_presentation_duration;
>>> +    uint64_t suggested_presentation_delay;
>>> +    uint64_t availability_start_time;
>>> +    uint64_t publish_time;
>>> +    uint64_t minimum_update_period;
>>> +    uint64_t time_shift_buffer_depth;
>>> +    uint64_t min_buffer_time;
>>> +
>>> +    /* Period Attribute */
>>> +    uint64_t period_duration;
>>> +    uint64_t period_start;
>>> +
>>> +    int is_live;
>>> +    AVIOInterruptCB *interrupt_callback;
>>> +    char *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 get_current_time_in_sec(void)
>>> +{
>>> +    return  av_gettime() / 1000000;
>>> +}
>>> +
>>> +static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
>>> +{
>>> +    struct tm timeinfo;
>>> +    int year = 0;
>>> +    int month = 0;
>>> +    int day = 0;
>>> +    int hour = 0;
>>> +    int minute = 0;
>>> +    int ret = 0;
>>> +    float second = 0.0;
>>> +
>>> +    /* ISO-8601 date parser */
>>> +    if (!datetime)
>>> +        return 0;
>>> +
>>> +    ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
>>> +    /* year, month, day, hour, minute, second  6 arguments */
>>> +    if (ret != 6) {
>>> +        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
>>> +    }
>>> +    timeinfo.tm_year = year - 1900;
>>> +    timeinfo.tm_mon  = month - 1;
>>> +    timeinfo.tm_mday = day;
>>> +    timeinfo.tm_hour = hour;
>>> +    timeinfo.tm_min  = minute;
>>> +    timeinfo.tm_sec  = (int)second;
>>> +
>>> +    return av_timegm(&timeinfo);
>>> +}
>>> +
>>> +static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
>>> +{
>>> +    /* ISO-8601 duration parser */
>>> +    uint32_t days = 0;
>>> +    uint32_t hours = 0;
>>> +    uint32_t mins = 0;
>>> +    uint32_t secs = 0;
>>> +    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, "get_duration_insec get a wrong time format\n");
>>> +            return 0; /* parser error */
>>> +        }
>>> +        switch (type) {
>>> +            case 'D':
>>> +                days = (uint32_t)value;
>>> +                break;
>>> +            case 'H':
>>> +                hours = (uint32_t)value;
>>> +                break;
>>> +            case 'M':
>>> +                mins = (uint32_t)value;
>>> +                break;
>>> +            case 'S':
>>> +                secs = (uint32_t)value;
>>> +                break;
>>> +            default:
>>> +                // handle invalid type
>>> +                break;
>>> +        }
>>> +        ptr += size;
>>> +    }
>>> +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;
>>> +}
>>> +
>>> +static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
>>> +{
>>> +    int64_t start_time = 0;
>>> +    int64_t i = 0;
>>> +    int64_t j = 0;
>>> +    int64_t num = 0;
>>> +
>>> +    if (pls->n_timelines) {
>>> +        for (i = 0; i < pls->n_timelines; i++) {
>>> +            if (pls->timelines[i]->t > 0) {
>>> +                start_time = pls->timelines[i]->t;
>>> +            }
>>> +            if (num == cur_seq_no)
>>> +                goto finish;
>>> +
>>> +            start_time += pls->timelines[i]->d;
>>> +            for (j = 0; j < pls->timelines[i]->r; j++) {
>>> +                num++;
>>> +                if (num == cur_seq_no)
>>> +                    goto finish;
>>> +                start_time += pls->timelines[i]->d;
>>> +            }
>>> +            num++;
>>> +        }
>>> +    }
>>> +finish:
>>> +    return start_time;
>>> +}
>>> +
>>> +static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
>>> +{
>>> +    int64_t i = 0;
>>> +    int64_t j = 0;
>>> +    int64_t num = 0;
>>> +    int64_t start_time = 0;
>>> +
>>> +    for (i = 0; i < pls->n_timelines; i++) {
>>> +        if (pls->timelines[i]->t > 0) {
>>> +            start_time = pls->timelines[i]->t;
>>> +        }
>>> +        if (start_time > cur_time)
>>> +            goto finish;
>>> +
>>> +        start_time += pls->timelines[i]->d;
>>> +        for (j = 0; j < pls->timelines[i]->r; j++) {
>>> +            num++;
>>> +            if (start_time > cur_time)
>>> +                goto finish;
>>> +            start_time += pls->timelines[i]->d;
>>> +        }
>>> +        num++;
>>> +    }
>>> +
>>> +    return -1;
>>> +
>>> +finish:
>>> +    return num;
>>> +}
>>> +
>>> +static void free_fragment(struct fragment **seg)
>>> +{
>>> +    if (!(*seg)) {
>>> +        return;
>>> +    }
>>> +    av_freep(&(*seg)->url);
>>> +    av_freep(seg);
>>> +}
>>> +
>>> +static void free_fragment_list(struct representation *pls)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < pls->n_fragments; i++) {
>>> +        free_fragment(&pls->fragments[i]);
>>> +    }
>>> +    av_freep(&pls->fragments);
>>> +    pls->n_fragments = 0;
>>> +}
>>> +
>>> +static void free_timelines_list(struct representation *pls)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < pls->n_timelines; i++) {
>>> +        av_freep(&pls->timelines[i]);
>>> +    }
>>> +    av_freep(&pls->timelines);
>>> +    pls->n_timelines = 0;
>>> +}
>>> +
>>> +static void free_representation(struct representation *pls)
>>> +{
>>> +    free_fragment_list(pls);
>>> +    free_timelines_list(pls);
>>> +    free_fragment(&pls->cur_seg);
>>> +    free_fragment(&pls->init_section);
>>> +    av_freep(&pls->init_sec_buf);
>>> +    av_freep(&pls->pb.buffer);
>>> +    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_pattern);
>>> +    av_free(pls->url_template_format);
>>> +    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;
>>> +    void *p = NULL;
>>> +
>>> +    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.
>>> +        p = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
>>> +        update_options(&c->cookies, "cookies", p);
>>> +        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 *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 = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
>>> +        av_free(tmp_str);
>>> +        tmp_str = url;
>>> +    }
>>> +    if (rep_bandwidth_val && tmp_str)
>>> +        url = av_strireplace(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 find_child_node_by_name(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 int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format)
>>> +{
>>> +    int ret = -1;
>>> +    int marker_len = 0;
>>> +    int format_len = 0;
>>> +    char *prefix = NULL;
>>> +    char *start = NULL;
>>> +    char *end = NULL;
>>> +
>>> +
>>> +    if (av_stristr(i_url, i_marker)) {
>>> +        *o_pattern = av_strdup(i_marker);
>>> +        *o_format = av_strdup("%"PRId64);
>>> +        ret = 0;
>>> +    } else {
>>> +        prefix = av_strdup(i_marker);
>>> +        marker_len = strlen(prefix)-1;
>>> +        prefix[marker_len] = '\0';
>>> +        start = av_stristr(i_url, prefix);
>>> +        if (!start)
>>> +            goto finish;
>>> +
>>> +        end = strchr(start + 1, '$');
>>> +        if (!end)
>>> +            goto finish;
>>> +
>>> +        if (start[marker_len] != '%')
>>> +            goto finish;
>>> +
>>> +        if (end[-1] != 'd')
>>> +            goto finish;
>>> +
>>> +        format_len = end - start - marker_len - 1 + strlen(PRId64);
>>> +        *o_format = av_mallocz(format_len+1);
>>> +        av_strlcpy(*o_format, start + marker_len, end - start - marker_len -1);
>>> +        av_strlcat(*o_format, PRId64, strlen(*o_format) + strlen(PRId64));
>>> +        *o_pattern = av_mallocz(end - start + 2);
>>> +        if (*o_pattern) {
>>> +            ret = AVERROR(EINVAL);
>>> +            goto finish;
>>> +        }
>>> +        av_strlcpy(*o_pattern, start, end - start + 1);
>>> +        ret = 0;
>>> +
>>> +finish:
>>> +        av_free(prefix);
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static enum AVMediaType get_content_type(xmlNodePtr node)
>>> +{
>>> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
>>> +    int i = 0;
>>> +    const char *attr;
>>> +    xmlChar *val = NULL;
>>> +
>>> +    if (node) {
>>> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
>>> +            attr = (i) ? "mimeType" : "contentType";
>>> +            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);
>>> +            if (!rep->init_section->url) {
>>> +                av_free(rep->init_section);
>>> +                xmlFree(initialization_val);
>>> +                return AVERROR(ENOMEM);
>>> +            }
>>> +            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);
>>> +            if (!seg->url) {
>>> +                av_free(seg);
>>> +                xmlFree(media_val);
>>> +                return AVERROR(ENOMEM);
>>> +            }
>>> +            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)strtoll(val, NULL, 10);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
>>> +                tml->r =(int64_t) strtoll(val, NULL, 10);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
>>> +                tml->d = (int64_t)strtoll(val, NULL, 10);
>>> +//                rep->fragment_duration = (int64_t) strtoll(val, NULL, 10);
>>> +            }
>>> +            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 *presentation_timeoffset_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 = find_child_node_by_name(representation_node, "SegmentTemplate");
>>> +        representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
>>> +        representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
>>> +
>>> +        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;
>>> +
>>> +            presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
>>> +            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);
>>> +                if (!rep->init_section->url) {
>>> +                    av_free(rep->init_section);
>>> +                    av_free(rep);
>>> +                    ret = AVERROR(ENOMEM);
>>> +                    goto end;
>>> +                }
>>> +                rep->init_section->size = -1;
>>> +                xmlFree(initialization_val);
>>> +            }
>>> +
>>> +            if (media_val) {
>>> +                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
>>> +                temp_string = rep->url_template;
>>> +                if (temp_string) {
>>> +                    if (av_stristr(temp_string, "$Number")) {
>>> +                        get_repl_pattern_and_format(temp_string, "$Number$", &(rep->url_template_pattern), &(rep->url_template_format));
>>> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
>>> +                    } else if (av_stristr(temp_string, "$Time")) {
>>> +                        get_repl_pattern_and_format(temp_string, "$Time$", &(rep->url_template_pattern), &(rep->url_template_format));
>>> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
>>> +                    } else {
>>> +                        temp_string = NULL;
>>> +                    }
>>> +                }
>>> +                xmlFree(media_val);
>>> +            }
>>> +
>>> +            if (presentation_timeoffset_val) {
>>> +                rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
>>> +                xmlFree(presentation_timeoffset_val);
>>> +            }
>>> +            if (duration_val) {
>>> +                rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
>>> +                xmlFree(duration_val);
>>> +            }
>>> +            if (timescale_val) {
>>> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
>>> +                xmlFree(timescale_val);
>>> +            }
>>> +            if (startnumber_val) {
>>> +                rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
>>> +                xmlFree(startnumber_val);
>>> +            }
>>> +
>>> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
>>> +
>>> +            if (!fragment_timeline_node)
>>> +                fragment_timeline_node = find_child_node_by_name(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);
>>> +            if (!seg->url) {
>>> +                av_free(seg);
>>> +                ret = AVERROR(ENOMEM);
>>> +                goto end;
>>> +            }
>>> +            seg->size = -1;
>>> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
>>> +        } else if (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) strtoll(duration_val, NULL, 10);
>>> +                xmlFree(duration_val);
>>> +            }
>>> +            if (timescale_val) {
>>> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
>>> +                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);
>>> +            }
>>> +
>>> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
>>> +
>>> +            if (!fragment_timeline_node)
>>> +                fragment_timeline_node = find_child_node_by_name(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 {
>>> +            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 = get_utc_date_time_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
>>> +                c->publish_time = get_utc_date_time_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
>>> +                c->minimum_update_period = get_duration_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
>>> +                c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
>>> +                c->min_buffer_time = get_duration_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
>>> +                c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
>>> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
>>> +                c->media_presentation_duration = get_duration_insec(s, (const char *)val);
>>> +            }
>>> +            attr = attr->next;
>>> +            xmlFree(val);
>>> +        }
>>> +
>>> +        mpd_baseurl_node = find_child_node_by_name(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 = get_duration_insec(s, (const char *)val);
>>> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
>>> +                        perdiod_start_sec = get_duration_insec(s, (const char *)val);
>>> +                    }
>>> +                    attr = attr->next;
>>> +                    xmlFree(val);
>>> +                }
>>> +                if ((perdiod_duration_sec) >= (c->period_duration)) {
>>> +                    period_node = node;
>>> +                    c->period_duration = perdiod_duration_sec;
>>> +                    c->period_start = perdiod_start_sec;
>>> +                    if (c->period_start > 0)
>>> +                        c->media_presentation_duration = c->period_duration;
>>> +                }
>>> +            }
>>> +            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;
>>> +        }
>>> +
>>> +        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 = s->priv_data;
>>> +    int64_t num = 0;
>>> +    int64_t start_time_offset = 0;
>>> +
>>> +    if (c->is_live) {
>>> +        if (pls->n_fragments) {
>>> +            num = pls->first_seq_no;
>>> +        } else if (pls->n_timelines) {
>>> +            start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->t; // total duration of playlist
>>> +            if (start_time_offset < 60 * pls->fragment_timescale)
>>> +                start_time_offset = 0;
>>> +            else
>>> +                start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
>>> +
>>> +            num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->t + start_time_offset);
>>> +            if (num == -1)
>>> +                num = pls->first_seq_no;
>>> +        } else {
>>> +            if (pls->presentation_timeoffset) {
>>> +                num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
>>> +            } else if (c->publish_time > 0) {
>>> +                num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
>>> +            } else {
>>> +                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * 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 = s->priv_data;
>>> +    int64_t num = 0;
>>> +
>>> +    if (c->is_live && pls->fragment_duration) {
>>> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
>>> +    } else {
>>> +        num = pls->first_seq_no;
>>> +    }
>>> +    return num;
>>> +}
>>> +
>>> +static int64_t calc_max_seg_no(struct representation *pls)
>>> +{
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +    int64_t num = 0;
>>> +
>>> +    if (pls->n_fragments) {
>>> +        num = pls->first_seq_no + pls->n_fragments - 1;
>>> +    } else if (pls->n_timelines) {
>>> +        int i = 0;
>>> +        num = pls->first_seq_no + pls->n_timelines - 1;
>>> +        for (i = 0; i < pls->n_timelines; i++) {
>>> +            num += pls->timelines[i]->r;
>>> +        }
>>> +    } else if (c->is_live) {
>>> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
>>> +    } else {
>>> +        num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
>>> +    }
>>> +
>>> +    return num;
>>> +}
>>> +
>>> +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
>>> +{
>>> +    if (rep_dest && rep_src ) {
>>> +        free_timelines_list(rep_dest);
>>> +        rep_dest->timelines    = rep_src->timelines;
>>> +        rep_dest->n_timelines  = rep_src->n_timelines;
>>> +        rep_dest->first_seq_no = rep_src->first_seq_no;
>>> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
>>> +        rep_src->timelines = NULL;
>>> +        rep_src->n_timelines = 0;
>>> +        rep_dest->cur_seq_no = rep_src->cur_seq_no;
>>> +    }
>>> +}
>>> +
>>> +static void move_segments(struct representation *rep_src, struct representation *rep_dest)
>>> +{
>>> +    if (rep_dest && rep_src ) {
>>> +        free_fragment_list(rep_dest);
>>> +        if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
>>> +            rep_dest->cur_seq_no = 0;
>>> +        else
>>> +            rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
>>> +        rep_dest->fragments    = rep_src->fragments;
>>> +        rep_dest->n_fragments  = rep_src->n_fragments;
>>> +        rep_dest->parent  = rep_src->parent;
>>> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
>>> +        rep_src->fragments = NULL;
>>> +        rep_src->n_fragments = 0;
>>> +    }
>>> +}
>>> +
>>> +
>>> +static int refresh_manifest(AVFormatContext *s)
>>> +{
>>> +
>>> +    int ret = 0;
>>> +    DASHContext *c = s->priv_data;
>>> +
>>> +    // save current context
>>> +    struct representation *cur_video =  c->cur_video;
>>> +    struct representation *cur_audio =  c->cur_audio;
>>> +    char *base_url = c->base_url;
>>> +
>>> +    c->base_url = NULL;
>>> +    c->cur_video = NULL;
>>> +    c->cur_audio = NULL;
>>> +    ret = parse_manifest(s, s->filename, NULL);
>>> +    if (ret)
>>> +        goto finish;
>>> +
>>> +    if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
>>> +        // calc current time
>>> +        int64_t currentVideoTime = 0;
>>> +        int64_t currentAudioTime = 0;
>>> +        if (cur_video && cur_video->timelines)
>>> +            currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
>>> +        if (cur_audio && cur_audio->timelines)
>>> +            currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
>>> +        // update segments
>>> +        if (cur_video && cur_video->timelines) {
>>> +            c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
>>> +            if (c->cur_video->cur_seq_no >= 0) {
>>> +                move_timelines(c->cur_video, cur_video);
>>> +            }
>>> +        }
>>> +        if (cur_audio && cur_audio->timelines) {
>>> +            c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
>>> +            if (c->cur_audio->cur_seq_no >= 0) {
>>> +               move_timelines(c->cur_audio, cur_audio);
>>> +            }
>>> +        }
>>> +    }
>>> +    if (cur_video && cur_video->fragments) {
>>> +        move_segments(c->cur_video, cur_video);
>>> +    }
>>> +    if (cur_audio && cur_audio->fragments) {
>>> +        move_segments(c->cur_audio, cur_audio);
>>> +    }
>>> +
>>> +finish:
>>> +    // restore context
>>> +    if (c->base_url)
>>> +        av_free(base_url);
>>> +    else
>>> +        c->base_url  = base_url;
>>> +    if (c->cur_audio)
>>> +        free_representation(c->cur_audio);
>>> +    if (c->cur_video)
>>> +        free_representation(c->cur_video);
>>> +    c->cur_audio = cur_audio;
>>> +    c->cur_video = cur_video;
>>> +    return ret;
>>> +}
>>> +
>>> +static struct fragment *get_current_fragment(struct representation *pls)
>>> +{
>>> +    int64_t min_seq_no = 0;
>>> +    int64_t max_seq_no = 0;
>>> +    struct fragment *seg = NULL;
>>> +    struct fragment *seg_ptr = NULL;
>>> +    DASHContext *c = pls->parent->priv_data;
>>> +
>>> +    while (( !ff_check_interrupt(c->interrupt_callback)&& 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;
>>> +        } else if (c->is_live) {
>>> +            av_usleep(1000*1000);
>>> +            refresh_manifest(pls->parent);
>>> +        } else {
>>> +            break;
>>> +        }
>>> +    }
>>> +    if (c->is_live) {
>>> +        while (!ff_check_interrupt(c->interrupt_callback)) {
>>> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
>>> +            max_seq_no = calc_max_seg_no(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);
>>> +                if (c->is_live && (pls->timelines || pls->fragments)) {
>>> +                    refresh_manifest(pls->parent);
>>> +                }
>>> +                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*1000);
>>> +                if (c->is_live && (pls->timelines || pls->fragments)) {
>>> +                    refresh_manifest(pls->parent);
>>> +                }
>>> +                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) {
>>> +            int64_t val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no);
>>> +            int size = snprintf(NULL, 0, pls->url_template_format, val); // calc needed buffer size
>>> +
>>> +            if (size > 0) {
>>> +                char *tmp_val = av_mallocz(size + 1);
>>> +                snprintf(tmp_val, size+1, pls->url_template_format, val);
>>> +                seg->url = av_strireplace(pls->url_template, pls->url_template_pattern, tmp_val);
>>> +                av_free(tmp_val);
>>> +            }
>>> +        }
>>> +
>>> +        if (!seg->url) {
>>> +            av_log(pls->parent, AV_LOG_ERROR, "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;
>>> +    }
>>> +
>>> +    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;
>>> +
>>> +    if (!v->is_restart_needed)
>>> +        v->cur_seq_no++;
>>> +    v->is_restart_needed = 1;
>>> +
>>> +/*
>>> +    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[0] != '\0') {
>>> +                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 DASH 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(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 * 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) {
>>> +        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
>>> +            ret = av_read_frame(cur->ctx, pkt);
>>> +            if (ret >= 0) {
>>> +                /* If we got a packet, return it */
>>> +                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;
>>> +                return 0;
>>> +            }
>>> +            if (cur->is_restart_needed) {
>>> +                while (!ff_check_interrupt(c->interrupt_callback)) {
>>> +                    cur->cur_seg_offset = 0;
>>> +                    cur->init_sec_buf_read_offset = 0;
>>> +                    if (cur->input)
>>> +                        ff_format_io_close(cur->parent, &cur->input);
>>> +                    ret = reopen_demux_for_component(s, cur);
>>> +                    if (c->is_live && ret) {
>>> +                        av_usleep(1000*1000);
>>> +                        continue;
>>> +                    }
>>> +                    break;
>>> +                }
>>> +                cur->is_restart_needed = 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 == 1) {
>>> +        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 / 2;
>>> +    }
>>> +
>>> +    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.11.0 (Apple Git-81)
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>
wm4 Aug. 28, 2017, 11:48 a.m. UTC | #7
On Sun, 27 Aug 2017 22:19:31 +0800
Steven Liu <lq@chinaffmpeg.org> wrote:

> ffmpeg need a dash demuxer for demux the dash formats base on
> https://github.com/samsamsam-iptvplayer/exteplayer3/blob/master/tmp/ffmpeg/patches/3.2.2/000001_add_dash_demux.patch
> 
> TODO:
> 1. support multi bitrate dash
> 
> 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
> 
> v10 fixed:
> 1. fix function API name code style
> 2. remove redundant strreplace call
> 3. remove redundant memory operation and check return value from get_content_url()
> 4. add space between ) and {
> 5. remove no need to log the value for print
> 
> v11 fixed:
> 1. from atoll to strtoll
> 
> v12 fixed:
> 1. remove strreplace and instead by av_strreplace
> 
> v13 fixed:
> 1. fix bug: cannot play:
> http://dash.edgesuite.net/akamai/bbb_30fps/bbb_30fps.mpd
> 
> v14 fixed:
> 1. fix bug: TLS connection was non-properly terminated
> 2. fix bug: No trailing CRLF found in HTTP header
> 
> v15 fixed:
> 1. play youtube link: ffmpeg -i $(youtube-dl -J "https://www.youtube.com/watch?v=XmL19DOP_Ls" | jq -r ".requested_formats[0].manifest_url")
> 2. code refine for timeline living stream
> 
> 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>
> Reviewed-by: wm4 <nfxjfg@googlemail.com>
> Tested-by: Andy Furniss <adf.lists@gmail.com>
> Reported-by: Andy Furniss <adf.lists@gmail.com>
> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
> Signed-off-by: samsamsam <samsamsam@o2.pl>

I only have some superficial comments, since I don't think I have a
full overview over the DASH protocol and this code to tell whether the
more intricate details are handled correctly. As such it's not a full
review.

But I'd like to object to the code duplication. Also, stuffing this
into a single source file isn't ideal.

Also, I don't remember having given a full review on this before.

> ---
>  configure                |    4 +
>  libavformat/Makefile     |    1 +
>  libavformat/allformats.c |    2 +-
>  libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1987 insertions(+), 1 deletion(-)
>  create mode 100644 libavformat/dashdec.c
> 
> diff --git a/configure b/configure
> index 05f6dcc99a..7a7d61fa13 100755
> --- a/configure
> +++ b/configure
> @@ -272,6 +272,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]
> @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST="
>      libvpx
>      libwavpack
>      libwebp
> +    libxml2
>      libzimg
>      libzmq
>      libzvbi
> @@ -2937,6 +2939,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"
> @@ -5996,6 +5999,7 @@ enabled openssl           && { use_pkg_config openssl openssl/ssl.h OPENSSL_init
>                                 check_lib openssl 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
>  
>  if enabled gcrypt; then
>      GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index f2b465cfa2..3d478749d0 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab
> --- /dev/null
> +++ b/libavformat/dashdec.c
> @@ -0,0 +1,1981 @@
> +/*
> + * 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).
> +     * */
> +    int64_t r;
> +    /* d: Element or Attribute Name
> +     * specifies the Segment duration, in units of the value of the @timescale.
> +     * */
> +    int64_t d;

Maybe these fields should have better names...

> +};
> +
> +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;
> +    char *url_template_pattern;
> +    char *url_template_format;
> +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
> +
> +    int64_t fragment_duration;
> +    int64_t fragment_timescale;
> +
> +    int64_t presentation_timeoffset;
> +
> +    int64_t cur_seq_no;
> +    int64_t cur_seg_offset;
> +    int64_t cur_seg_size;
> +    struct fragment *cur_seg;
> +
> +    /* Currently active Media Initialization Section */
> +    struct fragment *init_section;
> +    uint8_t *init_sec_buf;
> +    uint32_t init_sec_buf_size;
> +    uint32_t init_sec_data_len;
> +    uint32_t init_sec_buf_read_offset;
> +    int fix_multiple_stsd_order;
> +    int64_t cur_timestamp;
> +    int is_restart_needed;
> +};
> +
> +typedef struct DASHContext {
> +    const AVClass *class;
> +    char *base_url;
> +    struct representation *cur_video;
> +    struct representation *cur_audio;
> +
> +    /* MediaPresentationDescription Attribute */
> +    uint64_t media_presentation_duration;
> +    uint64_t suggested_presentation_delay;
> +    uint64_t availability_start_time;
> +    uint64_t publish_time;
> +    uint64_t minimum_update_period;
> +    uint64_t time_shift_buffer_depth;
> +    uint64_t min_buffer_time;
> +
> +    /* Period Attribute */
> +    uint64_t period_duration;
> +    uint64_t period_start;
> +
> +    int is_live;
> +    AVIOInterruptCB *interrupt_callback;
> +    char *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 get_current_time_in_sec(void)
> +{
> +    return  av_gettime() / 1000000;
> +}
> +
> +static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
> +{
> +    struct tm timeinfo;
> +    int year = 0;
> +    int month = 0;
> +    int day = 0;
> +    int hour = 0;
> +    int minute = 0;
> +    int ret = 0;
> +    float second = 0.0;
> +
> +    /* ISO-8601 date parser */
> +    if (!datetime)
> +        return 0;
> +
> +    ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
> +    /* year, month, day, hour, minute, second  6 arguments */
> +    if (ret != 6) {
> +        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
> +    }
> +    timeinfo.tm_year = year - 1900;
> +    timeinfo.tm_mon  = month - 1;
> +    timeinfo.tm_mday = day;
> +    timeinfo.tm_hour = hour;
> +    timeinfo.tm_min  = minute;
> +    timeinfo.tm_sec  = (int)second;
> +
> +    return av_timegm(&timeinfo);
> +}
> +
> +static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
> +{
> +    /* ISO-8601 duration parser */
> +    uint32_t days = 0;
> +    uint32_t hours = 0;
> +    uint32_t mins = 0;
> +    uint32_t secs = 0;
> +    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) {

%c takes a char*, %n takes a int*.

> +            av_log(s, AV_LOG_WARNING, "get_duration_insec get a wrong time format\n");
> +            return 0; /* parser error */
> +        }
> +        switch (type) {
> +            case 'D':
> +                days = (uint32_t)value;
> +                break;
> +            case 'H':
> +                hours = (uint32_t)value;
> +                break;
> +            case 'M':
> +                mins = (uint32_t)value;
> +                break;
> +            case 'S':
> +                secs = (uint32_t)value;
> +                break;
> +            default:
> +                // handle invalid type
> +                break;
> +        }
> +        ptr += size;
> +    }
> +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;
> +}
> +
> +static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
> +{
> +    int64_t start_time = 0;
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +
> +    if (pls->n_timelines) {
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            if (pls->timelines[i]->t > 0) {
> +                start_time = pls->timelines[i]->t;
> +            }
> +            if (num == cur_seq_no)
> +                goto finish;
> +
> +            start_time += pls->timelines[i]->d;
> +            for (j = 0; j < pls->timelines[i]->r; j++) {
> +                num++;
> +                if (num == cur_seq_no)
> +                    goto finish;
> +                start_time += pls->timelines[i]->d;
> +            }
> +            num++;
> +        }
> +    }
> +finish:
> +    return start_time;
> +}
> +
> +static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
> +{
> +    int64_t i = 0;
> +    int64_t j = 0;
> +    int64_t num = 0;
> +    int64_t start_time = 0;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        if (pls->timelines[i]->t > 0) {
> +            start_time = pls->timelines[i]->t;
> +        }
> +        if (start_time > cur_time)
> +            goto finish;
> +
> +        start_time += pls->timelines[i]->d;
> +        for (j = 0; j < pls->timelines[i]->r; j++) {
> +            num++;
> +            if (start_time > cur_time)
> +                goto finish;
> +            start_time += pls->timelines[i]->d;
> +        }
> +        num++;
> +    }
> +
> +    return -1;
> +
> +finish:
> +    return num;
> +}
> +
> +static void free_fragment(struct fragment **seg)
> +{
> +    if (!(*seg)) {
> +        return;
> +    }
> +    av_freep(&(*seg)->url);
> +    av_freep(seg);
> +}
> +
> +static void free_fragment_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_fragments; i++) {
> +        free_fragment(&pls->fragments[i]);
> +    }
> +    av_freep(&pls->fragments);
> +    pls->n_fragments = 0;
> +}
> +
> +static void free_timelines_list(struct representation *pls)
> +{
> +    int i;
> +
> +    for (i = 0; i < pls->n_timelines; i++) {
> +        av_freep(&pls->timelines[i]);
> +    }
> +    av_freep(&pls->timelines);
> +    pls->n_timelines = 0;
> +}
> +
> +static void free_representation(struct representation *pls)
> +{
> +    free_fragment_list(pls);
> +    free_timelines_list(pls);
> +    free_fragment(&pls->cur_seg);
> +    free_fragment(&pls->init_section);
> +    av_freep(&pls->init_sec_buf);
> +    av_freep(&pls->pb.buffer);
> +    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_pattern);
> +    av_free(pls->url_template_format);
> +    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;
> +    void *p = NULL;
> +
> +    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)] == ':') {

That's awfully convoluted, and I'd probably take a while to figure out
what this even does.

In fact, this is just copy&pasted from hls.c.

> +        ;
> +    } else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5)) {
> +        return AVERROR_INVALIDDATA;
> +    }

(What does it mean if proto_name starts with "file,"?)

> +    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);

I think demuxers are supposed to call some wrapper? 

> +    if (ret >= 0) {
> +        // update cookies on http response with setcookies.
> +        p = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
> +        update_options(&c->cookies, "cookies", p);
> +        av_dict_set(&opts, "cookies", c->cookies, 0);
> +    }

Some questionable code copy&pasted from HLS, I guess. I'm not sure if I
ever found out why it even checks for AVFMT_FLAG_CUSTOM_IO.

> +    av_dict_free(&tmp);
> +
> +    if (is_http)
> +        *is_http = av_strstart(proto_name, "http", NULL);
> +
> +    return ret;
> +
> +}
> +
> +static char *get_content_url(xmlNodePtr *baseurl_nodes,
> +                             int n_baseurl_nodes,
> +                             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);

I wonder if a static buffer would make the code below simpler, since
this is a fixed size buffer anyway.

> +    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 = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
> +        av_free(tmp_str);
> +        tmp_str = url;
> +    }
> +    if (rep_bandwidth_val && tmp_str)
> +        url = av_strireplace(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 find_child_node_by_name(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 int get_repl_pattern_and_format(const char *i_url, const char *i_marker, char **o_pattern, char **o_format)
> +{
> +    int ret = -1;
> +    int marker_len = 0;
> +    int format_len = 0;
> +    char *prefix = NULL;
> +    char *start = NULL;
> +    char *end = NULL;
> +
> +
> +    if (av_stristr(i_url, i_marker)) {
> +        *o_pattern = av_strdup(i_marker);
> +        *o_format = av_strdup("%"PRId64);
> +        ret = 0;
> +    } else {
> +        prefix = av_strdup(i_marker);
> +        marker_len = strlen(prefix)-1;
> +        prefix[marker_len] = '\0';
> +        start = av_stristr(i_url, prefix);
> +        if (!start)
> +            goto finish;
> +
> +        end = strchr(start + 1, '$');
> +        if (!end)
> +            goto finish;
> +
> +        if (start[marker_len] != '%')
> +            goto finish;
> +
> +        if (end[-1] != 'd')
> +            goto finish;
> +
> +        format_len = end - start - marker_len - 1 + strlen(PRId64);
> +        *o_format = av_mallocz(format_len+1);
> +        av_strlcpy(*o_format, start + marker_len, end - start - marker_len -1);
> +        av_strlcat(*o_format, PRId64, strlen(*o_format) + strlen(PRId64));

*o_format is later passed as format string to snprintf(). This is
probably not safe, although I have a hard time understand just what
this code outputs.

Also, the entire function is missing a lot of allocation failure error
checks.

> +        *o_pattern = av_mallocz(end - start + 2);
> +        if (*o_pattern) {
> +            ret = AVERROR(EINVAL);
> +            goto finish;
> +        }
> +        av_strlcpy(*o_pattern, start, end - start + 1);
> +        ret = 0;
> +
> +finish:
> +        av_free(prefix);
> +    }
> +
> +    return ret;
> +}
> +
> +static enum AVMediaType get_content_type(xmlNodePtr node)
> +{
> +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
> +    int i = 0;
> +    const char *attr;
> +    xmlChar *val = NULL;
> +
> +    if (node) {
> +        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
> +            attr = (i) ? "mimeType" : "contentType";
> +            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++;
> +        }

Making this a loop looks pretty convoluted.

> +    }
> +    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);
> +            if (!rep->init_section->url) {
> +                av_free(rep->init_section);
> +                xmlFree(initialization_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                xmlFree(media_val);
> +                return AVERROR(ENOMEM);
> +            }
> +            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)strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"r")) {
> +                tml->r =(int64_t) strtoll(val, NULL, 10);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"d")) {
> +                tml->d = (int64_t)strtoll(val, NULL, 10);
> +//                rep->fragment_duration = (int64_t) strtoll(val, NULL, 10);

Stray comment?

> +            }
> +            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 *presentation_timeoffset_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 = find_child_node_by_name(representation_node, "SegmentTemplate");
> +        representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
> +        representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
> +
> +        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;
> +
> +            presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
> +            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);
> +                if (!rep->init_section->url) {
> +                    av_free(rep->init_section);
> +                    av_free(rep);
> +                    ret = AVERROR(ENOMEM);
> +                    goto end;
> +                }
> +                rep->init_section->size = -1;
> +                xmlFree(initialization_val);
> +            }
> +
> +            if (media_val) {
> +                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
> +                temp_string = rep->url_template;
> +                if (temp_string) {
> +                    if (av_stristr(temp_string, "$Number")) {
> +                        get_repl_pattern_and_format(temp_string, "$Number$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
> +                    } else if (av_stristr(temp_string, "$Time")) {
> +                        get_repl_pattern_and_format(temp_string, "$Time$", &(rep->url_template_pattern), &(rep->url_template_format));
> +                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
> +                    } else {
> +                        temp_string = NULL;
> +                    }
> +                }
> +                xmlFree(media_val);
> +            }
> +
> +            if (presentation_timeoffset_val) {
> +                rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
> +                xmlFree(presentation_timeoffset_val);
> +            }
> +            if (duration_val) {
> +                rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                xmlFree(timescale_val);
> +            }
> +            if (startnumber_val) {
> +                rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
> +                xmlFree(startnumber_val);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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);
> +            if (!seg->url) {
> +                av_free(seg);
> +                ret = AVERROR(ENOMEM);
> +                goto end;
> +            }
> +            seg->size = -1;
> +            dynarray_add(&rep->fragments, &rep->n_fragments, seg);
> +        } else if (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) strtoll(duration_val, NULL, 10);

Doesn't check for errors. Not sure if that's a problem. Same with the
call below.

> +                xmlFree(duration_val);
> +            }
> +            if (timescale_val) {
> +                rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
> +                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);
> +            }
> +
> +            fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
> +
> +            if (!fragment_timeline_node)
> +                fragment_timeline_node = find_child_node_by_name(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 {
> +            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")) {

Why xmlStrcmp, and not strcmp?

> +            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 = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
> +                c->publish_time = get_utc_date_time_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
> +                c->minimum_update_period = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
> +                c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
> +                c->min_buffer_time = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
> +                c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
> +            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
> +                c->media_presentation_duration = get_duration_insec(s, (const char *)val);
> +            }
> +            attr = attr->next;
> +            xmlFree(val);
> +        }
> +
> +        mpd_baseurl_node = find_child_node_by_name(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 = get_duration_insec(s, (const char *)val);
> +                    } else if (!xmlStrcmp(attr->name, (const xmlChar *)"start")) {
> +                        perdiod_start_sec = get_duration_insec(s, (const char *)val);
> +                    }
> +                    attr = attr->next;
> +                    xmlFree(val);
> +                }
> +                if ((perdiod_duration_sec) >= (c->period_duration)) {
> +                    period_node = node;
> +                    c->period_duration = perdiod_duration_sec;
> +                    c->period_start = perdiod_start_sec;
> +                    if (c->period_start > 0)
> +                        c->media_presentation_duration = c->period_duration;
> +                }
> +            }
> +            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;
> +        }
> +
> +        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;

Having error handling so far from where it happens doesn't make it very
readable.

> +    }
> +
> +    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 = s->priv_data;
> +    int64_t num = 0;
> +    int64_t start_time_offset = 0;
> +
> +    if (c->is_live) {
> +        if (pls->n_fragments) {
> +            num = pls->first_seq_no;
> +        } else if (pls->n_timelines) {
> +            start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->t; // total duration of playlist
> +            if (start_time_offset < 60 * pls->fragment_timescale)
> +                start_time_offset = 0;
> +            else
> +                start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
> +
> +            num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->t + start_time_offset);
> +            if (num == -1)
> +                num = pls->first_seq_no;
> +        } else {
> +            if (pls->presentation_timeoffset) {
> +                num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
> +            } else if (c->publish_time > 0) {
> +                num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
> +            } else {
> +                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * 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 = s->priv_data;
> +    int64_t num = 0;
> +
> +    if (c->is_live && pls->fragment_duration) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no;
> +    }
> +    return num;
> +}
> +
> +static int64_t calc_max_seg_no(struct representation *pls)
> +{
> +    DASHContext *c = pls->parent->priv_data;
> +    int64_t num = 0;
> +
> +    if (pls->n_fragments) {
> +        num = pls->first_seq_no + pls->n_fragments - 1;
> +    } else if (pls->n_timelines) {
> +        int i = 0;
> +        num = pls->first_seq_no + pls->n_timelines - 1;
> +        for (i = 0; i < pls->n_timelines; i++) {
> +            num += pls->timelines[i]->r;
> +        }
> +    } else if (c->is_live) {
> +        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
> +    } else {
> +        num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
> +    }
> +
> +    return num;
> +}
> +
> +static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_timelines_list(rep_dest);
> +        rep_dest->timelines    = rep_src->timelines;
> +        rep_dest->n_timelines  = rep_src->n_timelines;
> +        rep_dest->first_seq_no = rep_src->first_seq_no;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->timelines = NULL;
> +        rep_src->n_timelines = 0;
> +        rep_dest->cur_seq_no = rep_src->cur_seq_no;
> +    }
> +}
> +
> +static void move_segments(struct representation *rep_src, struct representation *rep_dest)
> +{
> +    if (rep_dest && rep_src ) {
> +        free_fragment_list(rep_dest);
> +        if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
> +            rep_dest->cur_seq_no = 0;
> +        else
> +            rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
> +        rep_dest->fragments    = rep_src->fragments;
> +        rep_dest->n_fragments  = rep_src->n_fragments;
> +        rep_dest->parent  = rep_src->parent;
> +        rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
> +        rep_src->fragments = NULL;
> +        rep_src->n_fragments = 0;
> +    }
> +}
> +
> +
> +static int refresh_manifest(AVFormatContext *s)
> +{
> +
> +    int ret = 0;
> +    DASHContext *c = s->priv_data;
> +
> +    // save current context
> +    struct representation *cur_video =  c->cur_video;
> +    struct representation *cur_audio =  c->cur_audio;
> +    char *base_url = c->base_url;
> +
> +    c->base_url = NULL;
> +    c->cur_video = NULL;
> +    c->cur_audio = NULL;
> +    ret = parse_manifest(s, s->filename, NULL);
> +    if (ret)
> +        goto finish;
> +
> +    if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
> +        // calc current time
> +        int64_t currentVideoTime = 0;
> +        int64_t currentAudioTime = 0;
> +        if (cur_video && cur_video->timelines)
> +            currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
> +        if (cur_audio && cur_audio->timelines)
> +            currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
> +        // update segments
> +        if (cur_video && cur_video->timelines) {
> +            c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
> +            if (c->cur_video->cur_seq_no >= 0) {
> +                move_timelines(c->cur_video, cur_video);
> +            }
> +        }
> +        if (cur_audio && cur_audio->timelines) {
> +            c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
> +            if (c->cur_audio->cur_seq_no >= 0) {
> +               move_timelines(c->cur_audio, cur_audio);
> +            }
> +        }
> +    }
> +    if (cur_video && cur_video->fragments) {
> +        move_segments(c->cur_video, cur_video);
> +    }
> +    if (cur_audio && cur_audio->fragments) {
> +        move_segments(c->cur_audio, cur_audio);
> +    }
> +
> +finish:
> +    // restore context
> +    if (c->base_url)
> +        av_free(base_url);
> +    else
> +        c->base_url  = base_url;
> +    if (c->cur_audio)
> +        free_representation(c->cur_audio);
> +    if (c->cur_video)
> +        free_representation(c->cur_video);
> +    c->cur_audio = cur_audio;
> +    c->cur_video = cur_video;
> +    return ret;
> +}
> +
> +static struct fragment *get_current_fragment(struct representation *pls)
> +{
> +    int64_t min_seq_no = 0;
> +    int64_t max_seq_no = 0;
> +    struct fragment *seg = NULL;
> +    struct fragment *seg_ptr = NULL;
> +    DASHContext *c = pls->parent->priv_data;
> +
> +    while (( !ff_check_interrupt(c->interrupt_callback)&& 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;
> +        } else if (c->is_live) {
> +            av_usleep(1000*1000);

Why does it sleep an arbitrary amount of time?

> +            refresh_manifest(pls->parent);
> +        } else {
> +            break;
> +        }
> +    }
> +    if (c->is_live) {
> +        while (!ff_check_interrupt(c->interrupt_callback)) {
> +            min_seq_no = calc_min_seg_no(pls->parent, pls);
> +            max_seq_no = calc_max_seg_no(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);
> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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*1000);

Same. I also wonder if it wouldn't be better to avoid the duplicated
retry loop, if possible.

> +                if (c->is_live && (pls->timelines || pls->fragments)) {
> +                    refresh_manifest(pls->parent);
> +                }
> +                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) {
> +            int64_t val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no);
> +            int size = snprintf(NULL, 0, pls->url_template_format, val); // calc needed buffer size

Others have pointed it out: it's not ok to use input data as format
printf string. Also, I don't think passing NULL as buffer is allowed
(even if the buffer size is 0).

> +
> +            if (size > 0) {
> +                char *tmp_val = av_mallocz(size + 1);
> +                snprintf(tmp_val, size+1, pls->url_template_format, val);
> +                seg->url = av_strireplace(pls->url_template, pls->url_template_pattern, tmp_val);
> +                av_free(tmp_val);
> +            }
> +        }
> +
> +        if (!seg->url) {
> +            av_log(pls->parent, AV_LOG_ERROR, "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;
> +    }
> +
> +    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);
> +    }

Code repeated from somewhere else in this patch.

> +    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);

Doesn't that need an error check or so?

> +    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++;
> +        }

GAAAAAAH. Is that really necessary, and what does it do?

> +    }
> +
> +    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;

Apparently a third retry loop... this time without av_usleep, and with
a goto instead of a loop.

> +        }
> +    }
> +
> +    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;
> +
> +    if (!v->is_restart_needed)
> +        v->cur_seq_no++;
> +    v->is_restart_needed = 1;
> +
> +/*
> +    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[0] != '\0') {
> +                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 DASH 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(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 * 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) {
> +        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
> +            ret = av_read_frame(cur->ctx, pkt);
> +            if (ret >= 0) {
> +                /* If we got a packet, return it */
> +                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;
> +                return 0;
> +            }
> +            if (cur->is_restart_needed) {
> +                while (!ff_check_interrupt(c->interrupt_callback)) {
> +                    cur->cur_seg_offset = 0;
> +                    cur->init_sec_buf_read_offset = 0;
> +                    if (cur->input)
> +                        ff_format_io_close(cur->parent, &cur->input);
> +                    ret = reopen_demux_for_component(s, cur);
> +                    if (c->is_live && ret) {
> +                        av_usleep(1000*1000);
> +                        continue;

A 4th retry loop! This time it's following the pattern of the 1st and
2nd loop.

> +                    }
> +                    break;
> +                }
> +                cur->is_restart_needed = 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 == 1) {
> +        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 / 2;
> +    }
> +
> +    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,
> +};
wm4 Aug. 28, 2017, 7:47 p.m. UTC | #8
On Mon, 28 Aug 2017 11:28:33 +0200
samsamsam <samsamsam@o2.pl> wrote:

> OK. I will. What about adding validation of format instead of adding &#34; something like `&#34;%0*&#34;PRId64`&#34;?   Dnia 28 sierpnia 2017 03:30 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a):  If you know of such a vulnerability, report it to    ffmpeg-security@ffmpeg.org . New code with known vulnerabilities will not be accepted.   Sent from my iPhone   On Aug 27, 2017, at 14:04, samsamsam &lt;   samsamsam@o2.pl &gt; wrote:  get_repl_pattern_and_format, you should have a fixed value of something like 

Your mails are unreadable. In your previous mail, I couldn't
distinguish text you quoted and which you wrote. In this one, your mail
client seems to have removed all line breaks.
samsamsam Aug. 29, 2017, 7:30 p.m. UTC | #9
and think about the safety :  %02c%lld  %s%d%d%d%d   What? With my solution this is not problem.  Why you think %s%d%d%d%d or %02c%lld give any problem? 
                
              
               
                 
                  Dnia 28 sierpnia 2017 12:27 刘歧 &lt;lq@chinaffmpeg.org&gt; napisał(a):
                 
                 
                   
 在 2017年8月28日,18:12,sam &lt;samsamsam@o2.pl&gt; 写道: 
  
 Validation will be very simple. I am talking about something like this: 
 static int get_repl_pattern_and_format(co char *i_url, const char *i_marker, char **o_pattern, char **o_format)  
 { 
 ... 
 +        for(ptr=start + marker_len; ptr &lt; (end - 1); ++ptr) {  /*there is need to check this condition :P */ 
 +            if (*ptr != &#39;0&#39;) { 
 +               // Unknown format add log here  
 +                 finish; 
 +            } 
 +        } 
         format_len = end - start - marker_len - 1 + strlen(PRId64); 
         *o_format = av_mallocz(format_len+1); 
         strncpy(*o_forma start + marker_len, end - start - marker_len -1); 
         strcat(*o_format PRId64); 
 … 
 } 
 
 maybe more complex than this way, for example: 
 %d 
 %lld 
 %04lld 
 %PRId64 
 %PRId32 
 %PRId16 
  
 and think about the safety : 
 %02c%lld 
 %s%d%d%d%d 
  
 and so on,blablabla. 
  
 maybe we need to think a perfect solution. 
  
  
 
  
  
 Dnia 28 sierpnia 2017 11:30 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a): 
  
 I would expect parsing the number internally and using the additional arg to be simpler and easier to verify than format string validation. 
  
 
 On Aug 28, 2017, at 04:28, samsamsam &lt;samsamsam@o2.pl&gt; wrote: 
  
 OK. I will. 
 What about adding validation of format instead of adding &#34;something like `&#34;%0*&#34;PRId64`&#34;? 
  
 Dnia 28 sierpnia 2017 03:30 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a): 
  
 If you know of such a vulnerability, report it to ffmpeg-security@ffmpeg.org. New code with known vulnerabilities will not be accepted. 
  
 Sent from my iPhone 
  
 On Aug 27, 2017, at 14:04, samsamsam &lt;samsamsam@o2.pl&gt; wrote: 
 
 get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64` 
  
 If you afraid about safety then the only thing which need to be added to get_repl_pattern_and_format is validation of format. 
 Simple loop to validate format will be enough. Do you agree?  
  
 Anyway we are talking about safety but parser for mp4 atoms missing checking and there is quite easy to make segfault of the libavformat when try to prepared mp4 file. 
  
 I understand that you want to have maximum safety with new code but I hope you know that ffmpeg at all is not safety. 
  
 Regards, 
 SSS 
  
 Dnia 27 sierpnia 2017 16:34 Rodger Combs &lt;rodger.combs@gmail.com&gt; napisał(a): 
  
 You&#39;re still calling snprintf with a string derived from the XML, which is still not safe. Rather than having a format copied from the source in get_repl_pattern_and_format, you should have a fixed value of something like `&#34;%0*&#34;PRId64`, and specify an additional &#34;precision&#34; argument you parse from the XML yourself. I can&#39;t reiterate this enough: _never pass data from the XML into the format-string arg of a printf-family function_. 
  
 Also, rather than calling snprintf() twice with an av_malloc() in between, you can just call av_asprintf(). That&#39;s what it does internally anyway. 
  
 On Aug 27, 2017, at 09:19, Steven Liu &lt;lq@chinaffmpeg.org&gt; wrote: 
  
 ffmpeg need a dash demuxer for demux the dash formats base on 
 github.com github.com 
  
 TODO: 
 1. support multi bitrate dash 
  
 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 &#39;time(NULL)`, except it is not 2038-safe.&#39; 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-&gt;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 
  
 v10 fixed: 
 1. fix function API name code style 
 2. remove redundant strreplace call 
 3. remove redundant memory operation and check return value from get_content_url() 
 4. add space between ) and { 
 5. remove no need to log the value for print 
  
 v11 fixed: 
 1. from atoll to strtoll 
  
 v12 fixed: 
 1. remove strreplace and instead by av_strreplace 
  
 v13 fixed: 
 1. fix bug: cannot play: 
 dash.edgesuite.net dash.edgesuite.net 
  
 v14 fixed: 
 1. fix bug: TLS connection was non-properly terminated 
 2. fix bug: No trailing CRLF found in HTTP header 
  
 v15 fixed: 
 1. play youtube link: ffmpeg -i $(youtube-dl -J &#34; www.youtube.com www.youtube.com  | jq -r &#34;.requested_formats[0].manifes 
 2. code refine for timeline living stream 
  
 Reviewed-by: Clément Bœsch &lt;u@pkh.me&gt; 
 Reviewed-by: Michael Niedermayer &lt;michael@niedermayer.cc&gt; 
 Reviewed-by: Carl Eugen Hoyos &lt;cehoyos@ag.or.at&gt; 
 Reviewed-by: Rodger Combs &lt;rodger.combs@gmail.com&gt; 
 Reviewed-by: Moritz Barsnick &lt;barsnick@gmx.net&gt; 
 Reviewed-by: Nicolas George &lt;george@nsup.org&gt; 
 Reviewed-by: Ricardo Constantino &lt;wiiaboo@gmail.com&gt; 
 Reviewed-by: wm4 &lt;nfxjfg@googlemail.com&gt; 
 Tested-by: Andy Furniss &lt;adf.lists@gmail.com&gt; 
 Reported-by: Andy Furniss &lt;adf.lists@gmail.com&gt; 
 Signed-off-by: Steven Liu &lt;lq@chinaffmpeg.org&gt; 
 Signed-off-by: samsamsam &lt;samsamsam@o2.pl&gt; 
 --- 
 configure                    4 + 
 libavformat/Makefile     |    1 + 
 libavformat/allformats.c |    2 +- 
 libavformat/dashdec.c    | 1981 ++++++++++++++++++++++++++++++ 
 4 files changed, 1987 insertions(+), 1 deletion(-) 
 create mode 100644 libavformat/dashdec.c 
  
 diff --git a/configure b/configure 
 index 05f6dcc99a..7a7d61fa13 100755 
 --- a/configure 
 +++ b/configure 
 @@ -272,6 +272,7 @@ External library support: 
  --enable-libxcb-shape    enable X11 grabbing shape rendering [autodetect] 
  --enable-libxvid         enable Xvid encoding via xvidcore, 
                  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] 
 @@ -1576,6 +1577,7 @@ EXTERNAL_LIBRARY_LIST=&#34; 
    libvpx 
    libwavpack 
    libwebp 
 +    libxml2 
    libzimg 
    libzmq 
    libzvbi 
 @@ -2937,6 +2939,7 @@ avi_muxer_select=&#34;riffenc&#34; 
 caf_demuxer_select=&#34;iso_media riffdec&#34; 
 caf_muxer_select=&#34;iso_media&#34; 
 dash_muxer_select=&#34;mp4_muxer&#34; 
 +dash_demuxer_deps=&#34;libxml2&#34; 
 dirac_demuxer_select=&#34;dirac_pa 
 dts_demuxer_select=&#34;dca_parser 
 dtshd_demuxer_select=&#34;dca_pars 
 @@ -5996,6 +5999,7 @@ enabled openssl           &amp;&amp; { use_pkg_config openssl openssl/ssl.h OPENSSL_init 
                  openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 || 
                  &#34;ERROR: openssl not found&#34;; } 
 enabled qtkit_indev      &amp;&amp; { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; } 
 +enabled libxml2          &amp;&amp; require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion 
  
 if enabled gcrypt; then 
    GCRYPT_CONFIG=&#34;${cross_pre 
 diff --git a/libavformat/Makefile b/libavformat/Makefile 
 index f2b465cfa2..3d478749d0 100644 
 --- a/libavformat/Makefile 
 +++ b/libavformat/Makefile 
 @@ -133,6 +133,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 cd8200ea1c..aeb9b710fe 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 0000000000..4718ce24ab 
 --- /dev/null 
 +++ b/libavformat/dashdec.c 
 @@ -0,0 +1,1981 @@ 
 +/* 
 + * 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 &lt;libxml/parser.h&gt; 
 +#include &#34;libavutil/intreadwrite.h&#34; 
 +#include &#34;libavutil/opt.h&#34; 
 +#include &#34;libavutil/time.h&#34; 
 +#include &#34;libavutil/parseutils.h&#34; 
 +#include &#34;internal.h&#34; 
 +#include &#34;avio_internal.h&#34; 
 + 
 +#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&#39;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). 
 +     * */ 
 +    int64_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; 
 +    char *url_template_pattern; 
 +    char *url_template_format; 
 +    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 start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/ 
 + 
 +    int64_t fragment_duration; 
 +    int64_t fragment_timescale; 
 + 
 +    int64_t presentation_timeoffset; 
 + 
 +    int64_t cur_seq_no; 
 +    int64_t cur_seg_offset; 
 +    int64_t cur_seg_size; 
 +    struct fragment *cur_seg; 
 + 
 +    /* Currently active Media Initialization Section */ 
 +    struct fragment *init_section; 
 +    uint8_t *init_sec_buf; 
 +    uint32_t init_sec_buf_size; 
 +    uint32_t init_sec_data_len; 
 +    uint32_t init_sec_buf_read_offset; 
 +    int fix_multiple_stsd_order; 
 +    int64_t cur_timestamp; 
 +    int is_restart_needed; 
 +}; 
 + 
 +typedef struct DASHContext { 
 +    const AVClass *class; 
 +    char *base_url; 
 +    struct representation *cur_video; 
 +    struct representation *cur_audio; 
 + 
 +    /* MediaPresentationDescription Attribute */ 
 +    uint64_t media_presentation_duration; 
 +    uint64_t suggested_presentation_delay; 
 +    uint64_t availability_start_time; 
 +    uint64_t publish_time; 
 +    uint64_t minimum_update_period; 
 +    uint64_t time_shift_buffer_depth; 
 +    uint64_t min_buffer_time; 
 + 
 +    /* Period Attribute */ 
 +    uint64_t period_duration; 
 +    uint64_t period_start; 
 + 
 +    int is_live; 
 +    AVIOInterruptCB *interrupt_callback; 
 +    char *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 get_current_time_in_sec(void) 
 +{ 
 +    return  av_gettime() / 1000000; 
 +} 
 + 
 +static uint64_t get_utc_date_time_insec(AVForm *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, &#34;%d-%d-%dT%d:%d:%fZ&#34;, &amp;year, &amp;month, &amp;day, &amp;hour, &amp;minute, &amp;second); 
 +    /* year, month, day, hour, minute, second  6 arguments */ 
 +    if (ret != 6) { 
 +        av_log(s, AV_LOG_WARNING, &#34;get_utc_date_time_insec get a wrong time format\n&#34;); 
 +    } 
 +    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(&amp;timeinfo); 
 +} 
 + 
 +static uint32_t get_duration_insec(AVFormatCon *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 == &#39;P&#39; || *ptr == &#39;T&#39;) { 
 +            ptr++; 
 +            continue 
 +        } 
 + 
 +        if (sscanf(ptr, &#34;%f%c%n&#34;, &amp;value, &amp;type, &amp;size) != 2) { 
 +            av_log(s AV_LOG_WARNING, &#34;get_duration_insec get a wrong time format\n&#34;); 
 +            return 0; /* parser error */ 
 +        } 
 +        switch (type) { 
 +            case &#39;D&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;H&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;M&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            case &#39;S&#39;: 
 +                 = (uint32_t)value; 
 +                 
 +            default: 
 +                 handle invalid type 
 +                 
 +        } 
 +        ptr += size; 
 +    } 
 +    return  ((days * 24 + hours) * 60 + mins) * 60 + secs; 
 +} 
 + 
 +static int64_t get_segment_start_time_based_o representation *pls, int64_t cur_seq_no) 
 +{ 
 +    int64_t start_time = 0; 
 +    int64_t i = 0; 
 +    int64_t j = 0; 
 +    int64_t num = 0; 
 + 
 +    if (pls-&gt;n_timelines) { 
 +        for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +            if (pls-&gt;timelines[i]-&gt;t &gt; 0) { 
 +                 = pls-&gt;timelines[i]-&gt;t; 
 +            } 
 +            if (num == cur_seq_no) 
 +                 finish; 
 + 
 +            start_ti += pls-&gt;timelines[i]-&gt;d; 
 +            for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) { 
 +                 
 +                 (num == cur_seq_no) 
 +                 finish; 
 +                 += pls-&gt;timelines[i]-&gt;d; 
 +            } 
 +            num++; 
 +        } 
 +    } 
 +finish: 
 +    return start_time; 
 +} 
 + 
 +static int64_t calc_next_seg_no_from_timeline representation *pls, int64_t cur_time) 
 +{ 
 +    int64_t i = 0; 
 +    int64_t j = 0; 
 +    int64_t num = 0; 
 +    int64_t start_time = 0; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +        if (pls-&gt;timelines[i]-&gt;t &gt; 0) { 
 +            start_ti = pls-&gt;timelines[i]-&gt;t; 
 +        } 
 +        if (start_time &gt; cur_time) 
 +            goto finish; 
 + 
 +        start_time += pls-&gt;timelines[i]-&gt;d; 
 +        for (j = 0; j &lt; pls-&gt;timelines[i]-&gt;r; j++) { 
 +            num++; 
 +            if (start_time &gt; cur_time) 
 +                 finish; 
 +            start_ti += pls-&gt;timelines[i]-&gt;d; 
 +        } 
 +        num++; 
 +    } 
 + 
 +    return -1; 
 + 
 +finish: 
 +    return num; 
 +} 
 + 
 +static void free_fragment(struct fragment **seg) 
 +{ 
 +    if (!(*seg)) { 
 +        return; 
 +    } 
 +    av_freep(&amp;(*seg)-&gt;url); 
 +    av_freep(seg); 
 +} 
 + 
 +static void free_fragment_list(struct representation *pls) 
 +{ 
 +    int i; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_fragments; i++) { 
 +        free_fragment(&amp;p 
 +    } 
 +    av_freep(&amp;pls-&gt;fragments 
 +    pls-&gt;n_fragments = 0; 
 +} 
 + 
 +static void free_timelines_list(struct representation *pls) 
 +{ 
 +    int i; 
 + 
 +    for (i = 0; i &lt; pls-&gt;n_timelines; i++) { 
 +        av_freep(&amp;pls-&gt;t 
 +    } 
 +    av_freep(&amp;pls-&gt;timelines 
 +    pls-&gt;n_timelines = 0; 
 +} 
 + 
 +static void free_representation(struct representation *pls) 
 +{ 
 +    free_fragment_list(pls); 
 +    free_timelines_list(pls) 
 +    free_fragment(&amp;pls-&gt;cur_ 
 +    free_fragment(&amp;pls-&gt;init 
 +    av_freep(&amp;pls-&gt;init_sec_ 
 +    av_freep(&amp;pls-&gt;pb.buffer 
 +    if (pls-&gt;input) 
 +        ff_format_io_clo &amp;pls-&gt;input); 
 +    if (pls-&gt;ctx) { 
 +        pls-&gt;ctx-&gt;pb = NULL; 
 +        avformat_close_i 
 +    } 
 + 
 +    av_free(pls-&gt;url_templat 
 +    av_free(pls-&gt;url_templat 
 +    av_free(pls-&gt;url_templat 
 +    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, 
 +                 *opts, AVDictionary *opts2, int *is_http) 
 +{ 
 +    DASHContext *c = s-&gt;priv_data; 
 +    AVDictionary *tmp = NULL; 
 +    const char *proto_name = NULL; 
 +    int ret; 
 +    void *p = NULL; 
 + 
 +    av_dict_copy(&amp;tmp, opts, 0); 
 +    av_dict_copy(&amp;tmp, opts2, 0); 
 + 
 +    if (av_strstart(url, &#34;crypto&#34;, NULL)) { 
 +        if (url[6] == &#39;+&#39; || url[6] == &#39;:&#39;) 
 +            proto_na = 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) &amp; file are allowed 
 +    if (!av_strstart(proto_name, &#34;http&#34;, NULL) &amp;&amp; !av_strstart(proto_name, &#34;file&#34;, NULL)) { 
 +        return AVERROR_INVALIDDATA; 
 +    } 
 +    if (!strncmp(proto_name, url, strlen(proto_name)) &amp;&amp; url[strlen(proto_name)] == &#39;:&#39;) { 
 +        ; 
 +    } else if (av_strstart(url, &#34;crypto&#34;, NULL) &amp;&amp; !strncmp(proto_name, url + 7, strlen(proto_name)) &amp;&amp; url[7 + strlen(proto_name)] == &#39;:&#39;) { 
 +        ; 
 +    } else if (strcmp(proto_name, &#34;file&#34;) || !strncmp(url, &#34;file,&#34;, 5)) { 
 +        return AVERROR_INVALIDDATA; 
 +    } 
 +    ret = s-&gt;io_open(s, pb, url, AVIO_FLAG_READ, &amp;tmp); 
 +    if (ret &gt;= 0) { 
 +        // update cookies on http response with setcookies. 
 +        p = (s-&gt;flags &amp; AVFMT_FLAG_CUSTOM_IO) ? NULL : s-&gt;pb; 
 +        update_options(&amp; &#34;cookies&#34;, p); 
 +        av_dict_set(&amp;opt &#34;cookies&#34;, c-&gt;cookies, 0); 
 +    } 
 + 
 +    av_dict_free(&amp;tmp); 
 + 
 +    if (is_http) 
 +        *is_http = av_strstart(proto_name, &#34;http&#34;, NULL); 
 + 
 +    return ret; 
 + 
 +} 
 + 
 +static char *get_content_url(xmlNodePtr *baseurl_nodes, 
 +                 n_baseurl_nodes, 
 +                 *rep_id_val, 
 +                 *rep_bandwidth_val, 
 +                 *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 &lt; n_baseurl_nodes; ++i) { 
 +        if (baseurl_nodes[i] &amp;&amp; 
 +            baseurl_ &amp;&amp; 
 +            baseurl_ == XML_TEXT_NODE) { 
 +            text = xmlNodeGetContent(baseurl_node 
 +            if (text) { 
 +                 = av_mallocz(MAX_URL_SIZE); 
 +                 (!tmp_str_2) { 
 +                 
 +                 NULL; 
 +                 
 +                 MAX_URL_SIZE, tmp_str, text); 
 +                 
 +                 = tmp_str_2; 
 +                 
 +            } 
 +        } 
 +    } 
 +    if (val) 
 +        av_strlcat(tmp_s (const char*)val, MAX_URL_SIZE); 
 + 
 +    if (rep_id_val) { 
 +        url = av_strireplace(tmp_str, &#34;$RepresentationID$&#34;, (const char*)rep_id_val); 
 +        av_free(tmp_str) 
 +        tmp_str = url; 
 +    } 
 +    if (rep_bandwidth_val &amp;&amp; tmp_str) 
 +        url = av_strireplace(tmp_str, &#34;$Bandwidth$&#34;, (const char*)rep_bandwidth_val); 
 +    if (tmp_str != url) 
 +        av_free(tmp_str) 
 +    return url; 
 +} 
 + 
 +static xmlChar *get_val_from_nodes_tab(xmlNod *nodes, const int n_nodes, const xmlChar *attrname) 
 +{ 
 +    int i; 
 +    xmlChar *val; 
 + 
 +    for (i = 0; i &lt; n_nodes; ++i) { 
 +        if (nodes[i]) { 
 +            val = xmlGetProp(nodes[i], attrname); 
 +            if (val) 
 +                 val; 
 +        } 
 +    } 
 + 
 +    return NULL; 
 +} 
 + 
 +static xmlNodePtr find_child_node_by_name(xmlNod rootnode, const xmlChar *nodename) 
 +{ 
 +    xmlNodePtr node = rootnode; 
 +    if (!node) { 
 +        return NULL; 
 +    } 
 + 
 +    node = xmlFirstElementChild(node); 
 +    while (node) { 
 +        if (!xmlStrcmp(node-&gt;name, nodename)) { 
 +            return node; 
 +        } 
 +        node = xmlNextElementSibling(node); 
 +    } 
 +    return NULL; 
 +} 
 + 
 +static int get_repl_pattern_and_format(co char *i_url, const char *i_marker, char **o_pattern, char **o_format) 
 +{ 
 +    int ret = -1; 
 +    int marker_len = 0; 
 +    int format_len = 0; 
 +    char *prefix = NULL; 
 +    char *start = NULL; 
 +    char *end = NULL; 
 + 
 + 
 +    if (av_stristr(i_url, i_marker)) { 
 +        *o_pattern = av_strdup(i_marker); 
 +        *o_format = av_strdup(&#34;%&#34;PRId64); 
 +        ret = 0; 
 +    } else { 
 +        prefix = av_strdup(i_marker); 
 +        marker_len = strlen(prefix)-1; 
 +        prefix[marker_le = &#39;\0&#39;; 
 +        start = av_stristr(i_url, prefix); 
 +        if (!start) 
 +            goto finish; 
 + 
 +        end = strchr(start + 1, &#39;$&#39;); 
 +        if (!end) 
 +            goto finish; 
 + 
 +        if (start[marker_len] != &#39;%&#39;) 
 +            goto finish; 
 + 
 +        if (end[-1] != &#39;d&#39;) 
 +            goto finish; 
 + 
 +        format_len = end - start - marker_len - 1 + strlen(PRId64); 
 +        *o_format = av_mallocz(format_len+1); 
 +        av_strlcpy(*o_fo start + marker_len, end - start - marker_len -1); 
 +        av_strlcat(*o_fo PRId64, strlen(*o_format) + strlen(PRId64)); 
 +        *o_pattern = av_mallocz(end - start + 2); 
 +        if (*o_pattern) { 
 +            ret = AVERROR(EINVAL); 
 +            goto finish; 
 +        } 
 +        av_strlcpy(*o_pa start, end - start + 1); 
 +        ret = 0; 
 + 
 +finish: 
 +        av_free(prefix); 
 +    } 
 + 
 +    return ret; 
 +} 
 + 
 +static enum AVMediaType get_content_type(xmlNodePtr node) 
 +{ 
 +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN; 
 +    int i = 0; 
 +    const char *attr; 
 +    xmlChar *val = NULL; 
 + 
 +    if (node) { 
 +        while (type == AVMEDIA_TYPE_UNKNOWN &amp;&amp; i &lt; 2) { 
 +            attr = (i) ? &#34;mimeType&#34; : &#34;contentType&#34;; 
 +            val = xmlGetProp(node, attr); 
 +            if (val) { 
 +                 (av_stristr((const char *)val, &#34;video&#34;)) { 
 +                 = AVMEDIA_TYPE_VIDEO; 
 +                 else if (av_stristr((const char *)val, &#34;audio&#34;)) { 
 +                 = AVMEDIA_TYPE_AUDIO; 
 +                 
 +                 
 +            } 
 +            i++; 
 +        } 
 +    } 
 +    return type; 
 +} 
 + 
 +static int parse_manifest_segmenturlnode( *s, struct representation *rep, 
 +                 fragmenturl_node, 
 +                 *baseurl_nodes, 
 +                 *rep_id_val, 
 +                 *rep_bandwidth_val) 
 +{ 
 +    xmlChar *initialization_val = NULL; 
 +    xmlChar *media_val = NULL; 
 + 
 +    if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;Initialization&#34;)) { 
 +        initialization_v = xmlGetProp(fragmenturl_node, &#34;sourceURL&#34;); 
 +        if (initialization_val) { 
 +            rep-&gt;ini = av_mallocz(sizeof(struct fragment)); 
 +            if (!rep-&gt;init_section) { 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            rep-&gt;ini = get_content_url(baseurl_nodes, 4, 
 +                 
 +                 
 +                 
 +            if (!rep-&gt;init_section-&gt;url) { 
 +                 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            rep-&gt;ini = -1; 
 +            xmlFree( 
 +        } 
 +    } else if (!xmlStrcmp(fragmenturl_node-&gt; (const xmlChar *)&#34;SegmentURL&#34;)) { 
 +        media_val = xmlGetProp(fragmenturl_node, &#34;media&#34;); 
 +        if (media_val) { 
 +            struct fragment *seg = av_mallocz(sizeof(struct fragment)); 
 +            if (!seg) { 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            seg-&gt;url = get_content_url(baseurl_nodes, 4, 
 +                 
 +                 
 +                 
 +            if (!seg-&gt;url) { 
 +                 
 +                 
 +                 AVERROR(ENOMEM); 
 +            } 
 +            seg-&gt;siz = -1; 
 +            dynarray &amp;rep-&gt;n_fragments, seg); 
 +            xmlFree( 
 +        } 
 +    } 
 + 
 +    return 0; 
 +} 
 + 
 +static int parse_manifest_segmenttimeline *s, struct representation *rep, 
 +                 fragment_timeline_node) 
 +{ 
 +    xmlAttrPtr attr = NULL; 
 +    xmlChar *val  = NULL; 
 + 
 +    if (!xmlStrcmp(fragment_timeline_ (const xmlChar *)&#34;S&#34;)) { 
 +        struct timeline *tml = av_mallocz(sizeof(struct timeline)); 
 +        if (!tml) { 
 +            return AVERROR(ENOMEM); 
 +        } 
 +        attr = fragment_timeline_node-&gt;proper 
 +        while (attr) { 
 +            val = xmlGetProp(fragment_timeline_n attr-&gt;name); 
 + 
 +            if (!val) { 
 +                 AV_LOG_WARNING, &#34;parse_manifest_segmenttimelin attr-&gt;name = %s val is NULL\n&#34;, attr-&gt;name); 
 +                 
 +            } 
 + 
 +            if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;t&#34;)) { 
 +                 = (int64_t)strtoll(val, NULL, 10); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;r&#34;)) { 
 +                 =(int64_t) strtoll(val, NULL, 10); 
 +            } else if (!xmlStrcmp(attr-&gt;name, (const xmlChar *)&#34;d&#34;)) { 
 +                 = (int64_t)strtoll(val, NULL, 10); 
 +//                 = (int64_t) strtoll(val, NULL, 10); 
 +            } 
 +            attr = attr-&gt;next; 
 +            xmlFree( 
 +        } 
 +        dynarray_add(&amp;re &amp;rep-&gt;n_timelines, tml); 
 +    } 
 + 
 +    return 0; 
 +} 
 + 
 +static int parse_manifest_representation( *s, const char *url, 
 +                 node, 
 +                 adaptionset_node, 
 +                 mpd_baseurl_node, 
 +                 period_baseurl_node, 
 +                 fragment_template_node, 
 +                 content_component_node, 
 +                 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-&gt;priv_data; 
 +    struct representation *rep = NULL; 
 +    struct fragment *seg = NULL; 
 +    xmlNodePtr representation_segmenttemplate = NULL; 
 +    xmlNodePtr representation_baseurl_node = NULL; 
 +    xmlNodePtr representation_segmentlist_nod = NULL; 
 +    xmlNodePtr fragment_timeline_node = NULL; 
 +    xmlNodePtr fragment_templates_tab[2]; 
 +    xmlChar *duration_val = NULL; 
 +    xmlChar *presentation_timeoffset_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 &#34;id&#34;); 
 +    xmlChar *rep_bandwidth_val = xmlGetProp(representation_node &#34;bandwidth&#34;); 
 +    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN; 
 + 
 +    // try get information from representation 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(representatio 
 +    // try get information from contentComponen 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(content_compo 
 +    // try get information from adaption set 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) 
 +        type = get_content_type(adaptionset_n 
 +    if (type == AVMEDIA_TYPE_UNKNOWN) { 
 +        av_log(s, AV_LOG_VERBOSE, &#34;Parsing &#39;%s&#39; - skipp not supported representation type\n&#34;, url); 
 +    } else if ((type == AVMEDIA_TYPE_VIDEO &amp;&amp; !c-&gt;cur_video) || (type == AVMEDIA_TYPE_AUDIO &amp;&amp; !c-&gt;cur_audio)) { 
 +        // convert selected representation to our internal struct 
 +        rep = av_mallocz(sizeof(struct representation)); 
 +        if (!rep) { 
 +            ret = AVERROR(ENOMEM); 
 +            goto end; 
 +        } 
 +        representation_s = find_child_node_by_name(repres &#34;SegmentTemplate&#34;); 
 +        representation_b = find_child_node_by_name(repres &#34;BaseURL&#34;); 
 +        representation_s = find_child_node_by_name(repres &#34;SegmentList&#34;); 
 + 
 +        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_segmenttemplat || fragment_template_node) { 
 +            fragment = NULL; 
 +            fragment = representation_segmenttemplate 
 +            fragment = fragment_template_node; 
 + 
 +            presenta = get_val_from_nodes_tab(fragmen 2, &#34;presentationTimeOffset&#34;); 
 +            duration = get_val_from_nodes_tab(fragmen 2, &#34;duration&#34;); 
 +            startnum = get_val_from_nodes_tab(fragmen 2, &#34;startNumber&#34;); 
 +            timescal = get_val_from_nodes_tab(fragmen 2, &#34;timescale&#34;); 
 +            initiali = get_val_from_nodes_tab(fragmen 2, &#34;initialization&#34;); 
 +            media_va = get_val_from_nodes_tab(fragmen 2, &#34;media&#34;); 
 + 
 +            if (initialization_val) { 
 +                 = av_mallocz(sizeof(struct fragment)); 
 +                 (!rep-&gt;init_section) { 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +                 
 +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val); 
 +                 (!rep-&gt;init_section-&gt;url) { 
 +                 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +                 
 +                 = -1; 
 +                 
 +            } 
 + 
 +            if (media_val) { 
 +                 = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val); 
 +                 = rep-&gt;url_template; 
 +                 (temp_string) { 
 +                 (av_stristr(temp_string, &#34;$Number&#34;)) { 
 +                 &#34;$Number$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format)); 
 +                 = TMP_URL_TYPE_NUMBER;  /* Number-Based. */ 
 +                 else if (av_stristr(temp_string, &#34;$Time&#34;)) { 
 +                 &#34;$Time$&#34;, &amp;(rep-&gt;url_template_pattern), &amp;(rep-&gt;url_template_format)); 
 +                 = TMP_URL_TYPE_TIME; /* Time-Based. */ 
 +                 else { 
 +                 = NULL; 
 +                 
 +                 
 +                 
 +            } 
 + 
 +            if (presentation_timeoffset_val) { 
 +                 = (int64_t) strtoll(presentation_timeoffse NULL, 10); 
 +                 
 +            } 
 +            if (duration_val) { 
 +                 = (int64_t) strtoll(duration_val, NULL, 10); 
 +                 
 +            } 
 +            if (timescale_val) { 
 +                 = (int64_t) strtoll(timescale_val, NULL, 10); 
 +                 
 +            } 
 +            if (startnumber_val) { 
 +                 = (int64_t) strtoll(startnumber_val, NULL, 10); 
 +                 
 +            } 
 + 
 +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;); 
 + 
 +            if (!fragment_timeline_node) 
 +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;); 
 +            if (fragment_timeline_node) { 
 +                 = xmlFirstElementChild(fragment_ 
 +                 (fragment_timeline_node) { 
 +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node); 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +                 = xmlNextElementSibling(fragment 
 +                 
 +            } 
 +        } else if (representation_baseurl_node &amp;&amp; !representation_segmentlist_no { 
 +            seg = av_mallocz(sizeof(struct fragment)); 
 +            if (!seg) { 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +            } 
 +            seg-&gt;url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL); 
 +            if (!seg-&gt;url) { 
 +                 
 +                 = AVERROR(ENOMEM); 
 +                 end; 
 +            } 
 +            seg-&gt;siz = -1; 
 +            dynarray &amp;rep-&gt;n_fragments, seg); 
 +        } else if (representation_segmentlist_no { 
 +            // TODO:  www.brendanlong.com www.brendanlong.com 
 +            //  www-itec.uni-klu.ac.at www-itec.uni-klu.ac.at 
 +            xmlNodeP fragmenturl_node = NULL; 
 +            duration = xmlGetProp(representation_segm &#34;duration&#34;); 
 +            timescal = xmlGetProp(representation_segm &#34;timescale&#34;); 
 +            if (duration_val) { 
 +                 = (int64_t) strtoll(duration_val, NULL, 10); 
 +                 
 +            } 
 +            if (timescale_val) { 
 +                 = (int64_t) strtoll(timescale_val, NULL, 10); 
 +                 
 +            } 
 +            fragment = xmlFirstElementChild(represent 
 +            while (fragmenturl_node) { 
 +                 = parse_manifest_segmenturlnode( rep, fragmenturl_node, 
 +                 
 +                 
 +                 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +                 = xmlNextElementSibling(fragment 
 +            } 
 + 
 +            fragment = find_child_node_by_name(repres &#34;SegmentTimeline&#34;); 
 + 
 +            if (!fragment_timeline_node) 
 +                 = find_child_node_by_name(fragme &#34;SegmentTimeline&#34;); 
 +            if (fragment_timeline_node) { 
 +                 = xmlFirstElementChild(fragment_ 
 +                 (fragment_timeline_node) { 
 +                 = parse_manifest_segmenttimeline rep, fragment_timeline_node); 
 +                 (ret &lt; 0) { 
 +                 ret; 
 +                 
 +