]> git.webhop.me Git - bs-cst-neutrino-hd.git/commitdiff
add patches ffmpeg-3.3.9
authorMarkham <markham001@gmx.de>
Tue, 14 Feb 2023 20:04:58 +0000 (21:04 +0100)
committerMarkham <markham001@gmx.de>
Tue, 14 Feb 2023 20:04:58 +0000 (21:04 +0100)
14 files changed:
archive-patches/ffmpeg-cst/3.3.9/0001-ffmpeg-hds-libroxml-3.x.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0002-ffmpeg-aac-3.x.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0003-ffmpeg-increase-IO-BUFFER-SIZE-to-256k.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0004-ffmpeg-revert-libavformat-aviobuf-keep-track-of-the-original-buffer-size.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0005-ffmpeg-devel-v14-avformat-dashdec-add-dash-demuxer-base-version.patch [new file with mode: 0755]
archive-patches/ffmpeg-cst/3.3.9/0006-ffmpeg-reset-compressed-header-flag.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0007-ffmpeg-CST-fix-pts.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0008-ffmpeg-CST-fix-sps-pps.patch [new file with mode: 0755]
archive-patches/ffmpeg-cst/3.3.9/0009-ffmpeg-CST-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0010-ffmpeg-CST-allow-to-choose-rtmp-impl-at-runtime.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0011-ffmpeg-discont.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0012-ffmpeg-nolog.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3.9/0013-ffmpeg-disable-whitelist.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/3.3/ffmpeg34-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch [deleted file]

diff --git a/archive-patches/ffmpeg-cst/3.3.9/0001-ffmpeg-hds-libroxml-3.x.patch b/archive-patches/ffmpeg-cst/3.3.9/0001-ffmpeg-hds-libroxml-3.x.patch
new file mode 100644 (file)
index 0000000..e3dfdc7
--- /dev/null
@@ -0,0 +1,2703 @@
+--- a/configure        2018-11-18 01:57:59.000000000 +0100
++++ b/configure        2023-02-14 17:31:43.422907713 +0100
+@@ -293,6 +293,7 @@ External library support:
+                            on OSX if openssl and gnutls are not used [autodetect]
+   --disable-xlib           disable xlib [autodetect]
+   --disable-zlib           disable zlib [autodetect]
++  --disable-libroxml       disable XML parsing using the C library libroxml [autodetect]
+   The following libraries provide various hardware acceleration features:
+   --disable-audiotoolbox   disable Apple AudioToolbox code [autodetect]
+@@ -1565,6 +1566,7 @@ EXTERNAL_LIBRARY_LIST="
+     libopenmpt
+     libopus
+     libpulse
++    libroxml
+     librtmp
+     libschroedinger
+     libshine
+@@ -2926,6 +2928,7 @@ eac3_demuxer_select="ac3_parser"
+ f4v_muxer_select="mov_muxer"
+ fifo_muxer_deps="threads"
+ flac_demuxer_select="flac_parser"
++hds_demuxer_select="libroxml"
+ hds_muxer_select="flv_muxer"
+ hls_muxer_select="mpegts_muxer"
+ image2_alias_pix_demuxer_select="image2_demuxer"
+@@ -5735,6 +5738,8 @@ disabled  zlib || check_lib  zlib.h
+ disabled bzlib || check_lib bzlib.h BZ2_bzlibVersion    -lbz2  || disable bzlib
+ disabled  lzma || check_lib  lzma.h lzma_version_number -llzma || disable lzma
++disabled libroxml || check_lib roxml roxml.h roxml_load_buf -lroxml || disable libroxml
++
+ check_lib math.h sin -lm && LIBM="-lm"
+ disabled crystalhd || check_lib "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd || disable crystalhd
+--- a/libavformat/allformats.c 2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/allformats.c 2023-02-14 17:27:17.200274887 +0100
+@@ -142,7 +142,7 @@ static void register_all(void)
+     REGISTER_MUXDEMUX(H263,             h263);
+     REGISTER_MUXDEMUX(H264,             h264);
+     REGISTER_MUXER   (HASH,             hash);
+-    REGISTER_MUXER   (HDS,              hds);
++    REGISTER_MUXDEMUX(HDS,              hds);
+     REGISTER_MUXDEMUX(HEVC,             hevc);
+     REGISTER_MUXDEMUX(HLS,              hls);
+     REGISTER_DEMUXER (HNM,              hnm);
+--- a/libavformat/amfmetadata.c        1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/amfmetadata.c        2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,290 @@
++/*
++ * Adobe Action Message Format Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Action Message Format Parser
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ * @see http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf
++ */
++
++#include "amfmetadata.h"
++#include "avio_internal.h"
++#include "flv.h"
++#include "libavutil/avstring.h"
++#include "libavutil/intfloat.h"
++#include "libavformat/avformat.h"
++
++static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata *metadata, const char *name);
++
++static int amf_metadata_read_string_value(AVIOContext *in, char *str, int str_size)
++{
++    uint8_t type;
++
++    type = avio_r8(in);
++    if(type != 0x02) {
++      av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected type 2, type = %d \n", type);
++      return -1;
++    }
++
++    return flv_amf_get_string(in, str, str_size);
++}
++
++static void amf_metadata_assign_property_number(AMFMetadata *metadata,
++    const char *name, double value)
++{
++    if(!av_strcasecmp("width", name)) {
++      metadata->width = (int)value;
++    }
++    else
++    if(!av_strcasecmp("height", name)) {
++      metadata->height = (int)value;
++    }
++    else
++    if(!av_strcasecmp("framerate", name)) {
++      metadata->frame_rate = (int)value;
++    }
++    else
++    if(!av_strcasecmp("videodatarate", name)) {
++      metadata->video_data_rate = (int)value;
++    }
++    else
++    if(!av_strcasecmp("audiosamplerate", name)) {
++      metadata->audio_sample_rate = (int)value;
++    }
++    else
++    if(!av_strcasecmp("audiosamplesize", name)) {
++      metadata->audio_sample_size = (int)value;
++    }
++    else
++    if(!av_strcasecmp("audiochannels", name)) {
++      metadata->nb_audio_channels = (int)value;
++    }
++    else
++    if(!av_strcasecmp("stereo", name)) {
++      metadata->nb_audio_channels = ((int)value) ? 2 : 1;
++    }
++    else
++    if(!av_strcasecmp("audiodatarate", name)) {
++      metadata->audio_data_rate = (int)value;
++    }
++    else
++    if(!av_strcasecmp("audiocodecid", name)) {
++      switch(FLV_AUDIO_CODECID_MASK & ((int)value << FLV_AUDIO_CODECID_OFFSET)) {
++      case FLV_CODECID_PCM:
++          metadata->audio_codec_id = metadata->audio_sample_size == 8
++                                     ? AV_CODEC_ID_PCM_U8
++#if HAVE_BIGENDIAN
++                                     : AV_CODEC_ID_PCM_S16BE;
++#else
++                                     : AV_CODEC_ID_PCM_S16LE;
++#endif
++          break;
++      case FLV_CODECID_ADPCM:
++          metadata->audio_codec_id = AV_CODEC_ID_ADPCM_SWF;
++          break;
++      case FLV_CODECID_MP3:
++          metadata->audio_stream_need_parsing = AVSTREAM_PARSE_FULL;
++          metadata->audio_codec_id = AV_CODEC_ID_MP3;
++          break;
++      case FLV_CODECID_PCM_LE:
++          metadata->audio_codec_id = metadata->audio_sample_size == 8
++                                     ? AV_CODEC_ID_PCM_U8
++                                     : AV_CODEC_ID_PCM_S16LE;
++          break;
++      case FLV_CODECID_NELLYMOSER_16KHZ_MONO:
++          if (!metadata->audio_sample_rate)
++              metadata->audio_sample_rate = 16000;
++          metadata->nb_audio_channels = 1;
++          metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER;
++          break;
++      case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
++          if (!metadata->audio_sample_rate)
++              metadata->audio_sample_rate = 8000;
++          metadata->nb_audio_channels = 1;
++          metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER;
++          break;
++      case FLV_CODECID_NELLYMOSER:
++          metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER;
++          break;
++      case FLV_CODECID_PCM_ALAW:
++          metadata->audio_sample_rate = 8000;
++          metadata->audio_codec_id = AV_CODEC_ID_PCM_ALAW;
++          break;
++      case FLV_CODECID_PCM_MULAW:
++          metadata->audio_sample_rate = 8000;
++          metadata->audio_codec_id = AV_CODEC_ID_PCM_MULAW;
++          break;
++      case FLV_CODECID_AAC:
++          metadata->audio_codec_id = AV_CODEC_ID_AAC;
++          break;
++      case FLV_CODECID_SPEEX:
++          metadata->audio_sample_rate = 16000;
++          metadata->audio_codec_id = AV_CODEC_ID_SPEEX;
++          break;
++      default:
++          break;
++      }
++    }
++    else
++    if(!av_strcasecmp("videocodecid", name)) {
++      if((int)value == 7)
++          metadata->video_codec_id = AV_CODEC_ID_H264;
++    }
++}
++
++static void amf_metadata_assign_property_string(AMFMetadata *metadata,
++    const char *name, const char *value)
++{
++    if(!av_strcasecmp("audiocodecid", name)) {
++      if(!av_strcasecmp("mp4a", value))
++          metadata->audio_codec_id = AV_CODEC_ID_AAC;
++      else if(!av_strcasecmp(".mp3", value)) {
++          metadata->audio_stream_need_parsing = AVSTREAM_PARSE_FULL;
++          metadata->audio_codec_id = AV_CODEC_ID_MP3;
++      }
++      else
++          av_log(NULL, AV_LOG_ERROR, "amfmetadata audiocodecid=%s unhandled\n", value);
++    }
++    else
++    if(!av_strcasecmp("videocodecid", name)) {
++      if(!av_strcasecmp("avc1", value))
++          metadata->video_codec_id = AV_CODEC_ID_H264;
++      else
++          av_log(NULL, AV_LOG_ERROR, "amfmetadata videocodecid=%s unhandled\n", value);
++    }
++}
++
++static int amf_metadata_parse_object_property(AVIOContext *in, AMFMetadata *metadata)
++{
++    char name[INT16_MAX];
++    int ret;
++
++    if((ret = flv_amf_get_string(in, name, sizeof(name))) < 0)
++      return ret;
++
++    return amf_metadata_parse_value(in, metadata, name);
++}
++
++static int amf_metadata_parse_object(AVIOContext *in, AMFMetadata *metadata)
++{
++    int ret;
++
++    while(!avio_feof(in))
++      if((ret = amf_metadata_parse_object_property(in, metadata)) < 0)
++          break;
++
++    return 0;
++}
++
++static int amf_metadata_parse_strict_array(AVIOContext *in, AMFMetadata *metadata)
++{
++    int length;
++    int ret;
++
++    length = avio_rb32(in);
++    while(!avio_feof(in) && length > 0) {
++      if((ret = amf_metadata_parse_value(in, metadata, NULL)) < 0)
++          return ret;
++      length--;
++    }
++
++    return 0;
++}
++
++static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata *metadata, const char *name)
++{
++    uint8_t type;
++    char value_str[INT16_MAX];
++    double value_number;
++    int ret = 0;
++
++    type = avio_r8(in);
++
++    if(type == AMF_DATA_TYPE_NUMBER) {
++      value_number = av_int2double(avio_rb64(in));
++      amf_metadata_assign_property_number(metadata, name, value_number);
++    }
++    else if(type == AMF_DATA_TYPE_BOOL) {
++      value_number = avio_r8(in);
++      amf_metadata_assign_property_number(metadata, name, value_number);
++    }
++    else if(type == AMF_DATA_TYPE_STRING) {
++      if((ret = flv_amf_get_string(in, value_str, sizeof(value_str))) < 0)
++          return ret;
++      amf_metadata_assign_property_string(metadata, name, value_str);
++    }
++    else if(type == AMF_DATA_TYPE_OBJECT) {
++      ret = amf_metadata_parse_object(in, metadata);
++    }
++    else if(type == AMF_DATA_TYPE_MIXEDARRAY) {
++        avio_skip(in, 4);
++        ret = amf_metadata_parse_object(in, metadata);
++    }
++    else if(type == AMF_DATA_TYPE_ARRAY) {
++        ret = amf_metadata_parse_strict_array(in, metadata);
++    }
++    else if(type == AMF_END_OF_OBJECT) {
++        ret = -1; // EOF
++    }
++
++    return ret;
++}
++
++static int amf_metadata_parse(AVIOContext *in, AMFMetadata *metadata)
++{
++    char name[INT16_MAX];
++    int ret;
++
++    if((ret = amf_metadata_read_string_value(in, name, sizeof(name))) < 0) {
++        av_log(NULL, AV_LOG_ERROR, "amfmetadata Failed to read onMetadata string, ret: %d \n", ret);
++        return ret;
++    }
++
++    if(av_strcasecmp(name, "onMetaData")) {
++        av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected onMetadata, str = %s \n", name);
++        return -1;
++    }
++
++    return amf_metadata_parse_value(in, metadata, name);
++}
++
++int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata *metadata)
++{
++    AVIOContext *in;
++    int ret;
++
++    if(!buffer)
++        return 0;
++    if(buffer_size <= 0)
++        return 0;
++
++    in = avio_alloc_context(buffer, buffer_size,
++        0, NULL, NULL, NULL, NULL);
++    if(!in)
++        return AVERROR(ENOMEM);
++
++    ret = amf_metadata_parse(in, metadata);
++    av_free(in);
++
++    return ret;
++}
+--- a/libavformat/amfmetadata.h        1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/amfmetadata.h        2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,47 @@
++/*
++ * Adobe Action Message Format Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Action Message Format Parser
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ * @see http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf
++ */
++
++#include "libavcodec/avcodec.h"
++
++typedef struct AMFMetadata {
++    int width;
++    int height;
++    int frame_rate;
++    int audio_sample_rate;
++    int audio_sample_size;
++    int nb_audio_channels;
++    int audio_data_rate;
++    int video_data_rate;
++    int audio_stream_need_parsing;
++
++    enum AVCodecID audio_codec_id;
++    enum AVCodecID video_codec_id;
++} AMFMetadata;
++
++int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata *metadata);
+--- a/libavformat/f4fbox.c     1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/f4fbox.c     2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,345 @@
++/*
++ * Adobe Fragmented F4V File (F4F) Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Fragmented F4V File (F4F) Parser for Adobe HDS
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ */
++
++#include "f4fbox.h"
++#include "avformat.h"
++
++static int f4fbox_parse_single_box(AVIOContext *in, void *opague);
++
++static int f4fbox_parse_asrt(AVIOContext *in, int64_t data_size, void *opague)
++{
++    F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opague;
++    F4FSegmentRunTableBox *asrt;
++    F4FSegmentRunEntry *entry;
++    uint32_t segment_run_entry_count;
++    char url[1024];
++    int i;
++
++    asrt = av_mallocz(sizeof(F4FSegmentRunTableBox));
++    if(!asrt)
++        return AVERROR(ENOMEM);
++
++    parent->segment_run_table_boxes[parent->nb_segment_run_table_boxes] = asrt;
++    parent->nb_segment_run_table_boxes++;
++
++    asrt->version = avio_r8(in);
++    asrt->flags = avio_rb24(in);
++
++    asrt->nb_quality_entries = avio_r8(in);
++    if (asrt->nb_quality_entries)
++        asrt->quality_entries = (char **) av_malloc(asrt->nb_quality_entries * sizeof(char *));
++    for(i = 0; i < asrt->nb_quality_entries; i++) {
++        avio_get_str(in, sizeof(url), url, sizeof(url));
++        av_log(NULL, AV_LOG_DEBUG, "f4fbox asrt quality[%d]=%s\n", i, url);
++        asrt->quality_entries[i] = av_strdup(url);
++    }
++
++    segment_run_entry_count = avio_rb32(in);
++    if(segment_run_entry_count > MAX_NB_SEGMENT_RUN_ENTRIES) {
++        av_log(NULL, AV_LOG_ERROR, "f4fbox segment run entry count exceeds maximum, %d > %d \n",
++            segment_run_entry_count, MAX_NB_SEGMENT_RUN_ENTRIES);
++        segment_run_entry_count = MAX_NB_SEGMENT_RUN_ENTRIES;
++    }
++
++    for(i = 0; i < segment_run_entry_count; i++) {
++        entry = av_mallocz(sizeof(F4FSegmentRunEntry));
++        if(!entry)
++            return AVERROR(ENOMEM);
++
++        asrt->segment_run_entries[asrt->nb_segment_run_entries] = entry;
++        asrt->nb_segment_run_entries++;
++
++        entry->first_segment = avio_rb32(in);
++        entry->fragments_per_segment = avio_rb32(in);
++    }
++
++    return 0;
++}
++
++static int f4fbox_parse_afrt(AVIOContext *in, int64_t data_size, void *opague)
++{
++    F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opague;
++    F4FFragmentRunTableBox *afrt;
++    F4FFragmentRunEntry *entry;
++    uint32_t fragment_run_entry_count;
++    char url[1024];
++    int i;
++
++    afrt = av_mallocz(sizeof(F4FFragmentRunTableBox));
++    if(!afrt)
++        return AVERROR(ENOMEM);
++
++    parent->fragment_run_table_boxes[parent->nb_fragment_run_table_boxes] = afrt;
++    parent->nb_fragment_run_table_boxes++;
++
++    afrt->version = avio_r8(in);
++    afrt->flags = avio_rb24(in);
++
++    afrt->timescale = avio_rb32(in);
++
++    afrt->nb_quality_entries = avio_r8(in);
++    if (afrt->nb_quality_entries)
++        afrt->quality_entries = (char **) av_malloc(afrt->nb_quality_entries * sizeof(char *));
++    for(i = 0; i < afrt->nb_quality_entries; i++) {
++        avio_get_str(in, sizeof(url), url, sizeof(url));
++        av_log(NULL, AV_LOG_DEBUG, "f4fbox afrt quality[%d]=%s\n", i, url);
++        afrt->quality_entries[i] = av_strdup(url);
++    }
++
++    fragment_run_entry_count = avio_rb32(in);
++    if(fragment_run_entry_count > MAX_NB_FRAGMENT_RUN_ENTRIES) {
++        av_log(NULL, AV_LOG_ERROR, "f4fbox fragment run entry count exceeds maximum, %d > %d \n",
++            fragment_run_entry_count, MAX_NB_FRAGMENT_RUN_ENTRIES);
++        fragment_run_entry_count = MAX_NB_FRAGMENT_RUN_ENTRIES;
++    }
++
++    for(i = 0; i < fragment_run_entry_count; i++) {
++        entry = av_mallocz(sizeof(F4FFragmentRunEntry));
++        if(!entry)
++            return AVERROR(ENOMEM);
++
++        afrt->fragment_run_entries[afrt->nb_fragment_run_entries] = entry;
++        afrt->nb_fragment_run_entries++;
++
++        entry->first_fragment = avio_rb32(in);
++        entry->first_fragment_time_stamp = avio_rb64(in);
++        entry->fragment_duration = avio_rb32(in);
++        if(entry->fragment_duration == 0) {
++            entry->discontinuity_indicator = avio_r8(in);
++        }
++    }
++
++    return 0;
++}
++
++
++static int f4fbox_parse_abst(AVIOContext *in, int64_t data_size, void *opague)
++{
++    F4FBox *parent = (F4FBox*)opague;
++    F4FBootstrapInfoBox *abst = &(parent->abst);
++    uint8_t server_entry_count;
++    uint8_t segment_run_table_count, fragment_run_table_count;
++    uint8_t byte;
++    char url[1024];
++    int i, ret;
++
++    abst->version = avio_r8(in);
++    abst->flags = avio_rb24(in);
++    abst->bootstrap_info_version = avio_rb32(in);
++
++    byte = avio_r8(in);
++    abst->profile = (byte >> 6) & 0x03;
++    abst->is_live = (byte >> 5) & 0x01;
++    abst->is_update = (byte >> 4) & 0x01;
++
++    abst->timescale = avio_rb32(in);
++    abst->current_media_time = avio_rb64(in);
++    abst->smpte_time_code_offset = avio_rb64(in);
++
++    avio_get_str(in, sizeof(abst->movie_id), abst->movie_id, sizeof(abst->movie_id));
++
++    server_entry_count = avio_r8(in);
++    for(i = 0; i < server_entry_count; i++) {
++        avio_get_str(in, sizeof(url), url, sizeof(url));
++    }
++
++    abst->nb_quality_entries = avio_r8(in);
++    if (abst->nb_quality_entries)
++        abst->quality_entries = (char **) av_malloc(abst->nb_quality_entries * sizeof(char *));
++    for(i = 0; i < abst->nb_quality_entries; i++) {
++        avio_get_str(in, sizeof(url), url, sizeof(url));
++        av_log(NULL, AV_LOG_DEBUG, "f4fbox abst quality[%d]=%s\n", i, url);
++        abst->quality_entries[i] = av_strdup(url);
++    }
++
++    avio_get_str(in, sizeof(abst->drm_data), abst->drm_data, sizeof(abst->drm_data));
++    avio_get_str(in, sizeof(abst->metadata), abst->metadata, sizeof(abst->metadata));
++
++    segment_run_table_count = avio_r8(in);
++    for(i = 0; i < segment_run_table_count; i++) {
++        if((ret = f4fbox_parse_single_box(in, abst)) < 0) {
++            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse asrt box, ret: %d \n", ret);
++            return ret;
++        }
++    }
++
++    fragment_run_table_count = avio_r8(in);
++    for(i = 0; i < fragment_run_table_count; i++) {
++        if((ret = f4fbox_parse_single_box(in, abst)) < 0) {
++            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse afrt box, ret: %d \n", ret);
++            return ret;
++        }
++    }
++
++    return 0;
++}
++
++static int f4fbox_parse_mdat(AVIOContext *in, int64_t data_size, void *opague)
++{
++    F4FBox *parent = (F4FBox*)opague;
++    F4FMediaDataBox *mdat = &(parent->mdat);
++
++    mdat->data = av_mallocz(sizeof(uint8_t)*data_size);
++    if(!mdat->data)
++        return AVERROR(ENOMEM);
++
++    mdat->size = data_size;
++    avio_read(in, mdat->data, mdat->size);
++
++    return 0;
++}
++
++static int f4fbox_parse_single_box(AVIOContext *in, void *opague)
++{
++    int64_t bytes_read, bytes_left, start_pos, end_pos;
++    uint64_t size;
++    uint32_t type;
++    int ret = 0;
++
++    bytes_read = 0;
++    start_pos = avio_tell(in);
++
++    size = avio_rb32(in);
++    type = avio_rl32(in);
++    bytes_read += 8;
++
++    if(size == 1) {/* 64 bit extended size */
++        size = avio_rb64(in) - 8;
++        bytes_read += 8;
++    }
++
++    if(size == 0)
++        return -1;
++
++    if(type == MKTAG('a', 'b', 's', 't')) {
++        ret = f4fbox_parse_abst(in, size, opague);
++    }
++    if(type == MKTAG('a', 's', 'r', 't')) {
++        ret = f4fbox_parse_asrt(in, size, opague);
++    }
++    if(type == MKTAG('a', 'f', 'r', 't')) {
++        ret = f4fbox_parse_afrt(in, size, opague);
++    }
++    if(type == MKTAG('m', 'd', 'a', 't')) {
++        ret = f4fbox_parse_mdat(in, size, opague);
++    }
++
++    if(ret < 0)
++        return ret;
++
++    end_pos = avio_tell(in);
++    bytes_left = size - (end_pos - start_pos);
++    if(bytes_left > 0)
++        avio_skip(in, bytes_left);
++
++    bytes_read += size;
++
++    return bytes_read;
++}
++
++static int f4fbox_parse(AVIOContext *in, int64_t data_size, void *opague)
++{
++    int64_t bytes_read = 0;
++    int ret;
++
++    while(!avio_feof(in) && bytes_read + 8 < data_size) {
++        if((ret = f4fbox_parse_single_box(in, opague)) < 0) {
++            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse box, ret: %d \n", ret);
++            return ret;
++        }
++        bytes_read += ret;
++    }
++
++    return 0;
++}
++
++int ff_parse_f4f_box(uint8_t *buffer, int buffer_size, F4FBox *box)
++{
++    AVIOContext *in;
++    int ret;
++
++    in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL, NULL);
++    if(!in)
++        return AVERROR(ENOMEM);
++
++    ret = f4fbox_parse(in, buffer_size, box);
++    av_free(in);
++
++    return ret;
++}
++
++int ff_free_f4f_box(F4FBox *box)
++{
++    F4FBootstrapInfoBox *abst;
++    F4FSegmentRunTableBox *asrt;
++    F4FSegmentRunEntry *sre;
++    F4FFragmentRunTableBox *afrt;
++    F4FFragmentRunEntry *fre;
++    F4FMediaDataBox *mdat;
++    int i, j;
++
++    abst = &(box->abst);
++    for(j = 0; j < abst->nb_quality_entries; j++)
++        av_freep(&abst->quality_entries[j]);
++    if (abst->quality_entries)
++        av_freep(&abst->quality_entries);
++
++    for(i = 0; i < abst->nb_segment_run_table_boxes; i++) {
++        asrt = abst->segment_run_table_boxes[i];
++        for(j = 0; j < asrt->nb_quality_entries; j++)
++            av_freep(&asrt->quality_entries[j]);
++        if (asrt->quality_entries)
++            av_freep(&asrt->quality_entries);
++        for(j = 0; j < asrt->nb_segment_run_entries; j++) {
++            sre = asrt->segment_run_entries[j];
++            av_freep(&sre);
++        }
++        av_freep(&asrt);
++    }
++
++    for(i = 0; i < abst->nb_fragment_run_table_boxes; i++) {
++        afrt = abst->fragment_run_table_boxes[i];
++        for(j = 0; j < afrt->nb_quality_entries; j++)
++            av_freep(&afrt->quality_entries[j]);
++        if (afrt->quality_entries)
++            av_freep(&afrt->quality_entries);
++        for(j = 0; j < afrt->nb_fragment_run_entries; j++) {
++            fre = afrt->fragment_run_entries[j];
++            av_freep(&fre);
++        }
++        av_freep(&afrt);
++    }
++
++    mdat = &(box->mdat);
++    if(mdat->size > 0)
++        av_freep(&mdat->data);
++
++    memset(box, 0x00, sizeof(F4FBox));
++
++    return 0;
++}
+--- a/libavformat/f4fbox.h     1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/f4fbox.h     2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,110 @@
++/*
++ * Adobe Fragmented F4V File (F4F) Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Fragmented F4V File (F4F) Parser for Adobe HDS
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ */
++
++#include "avio_internal.h"
++
++#define MAX_NB_SEGMENT_RUN_TABLE_BOXES 256
++#define MAX_NB_FRAGMENT_RUN_TABLE_BOXES 256
++
++#define MAX_NB_SEGMENT_RUN_ENTRIES 1024
++#define MAX_NB_FRAGMENT_RUN_ENTRIES 1024
++
++typedef struct F4FFragmentRunEntry {
++    uint32_t first_fragment;
++    uint64_t first_fragment_time_stamp;
++    uint32_t fragment_duration;
++    uint8_t discontinuity_indicator;
++} F4FFragmentRunEntry;
++
++typedef struct F4FFragmentRunTableBox {
++    uint8_t version;
++    uint32_t flags;
++    uint32_t timescale;
++
++    uint8_t nb_quality_entries;
++    char **quality_entries;
++
++    uint32_t nb_fragment_run_entries;
++    F4FFragmentRunEntry *fragment_run_entries[MAX_NB_FRAGMENT_RUN_ENTRIES];
++} F4FFragmentRunTableBox;
++
++typedef struct F4FSegmentRunEntry {
++    uint32_t first_segment;
++    uint32_t fragments_per_segment;
++} F4FSegmentRunEntry;
++
++typedef struct F4FSegmentRunTableBox {
++    uint8_t version;
++    uint32_t flags;
++
++    uint8_t nb_quality_entries;
++    char **quality_entries;
++
++    uint32_t nb_segment_run_entries;
++    F4FSegmentRunEntry *segment_run_entries[MAX_NB_SEGMENT_RUN_ENTRIES];
++} F4FSegmentRunTableBox;
++
++typedef struct F4FBootstrapInfoBox {
++    uint8_t version;
++    uint32_t flags;
++    uint32_t bootstrap_info_version;
++
++    uint8_t profile;
++    uint8_t is_live;
++    uint8_t is_update;
++
++    uint32_t timescale;
++    uint64_t current_media_time;
++    uint64_t smpte_time_code_offset;
++
++    char movie_id[1024];
++    char drm_data[1024];
++    char metadata[1024];
++
++    uint8_t nb_quality_entries;
++    char **quality_entries;
++
++    uint8_t nb_segment_run_table_boxes;
++    F4FSegmentRunTableBox *segment_run_table_boxes[MAX_NB_SEGMENT_RUN_TABLE_BOXES];
++
++    uint8_t nb_fragment_run_table_boxes;
++    F4FFragmentRunTableBox *fragment_run_table_boxes[MAX_NB_FRAGMENT_RUN_TABLE_BOXES];
++} F4FBootstrapInfoBox;
++
++typedef struct F4FMediaDataBox {
++    uint32_t size;
++    uint8_t *data;
++} F4FMediaDataBox;
++
++typedef struct F4FBox {
++    F4FBootstrapInfoBox abst;
++    F4FMediaDataBox mdat;
++} F4FBox;
++
++int ff_parse_f4f_box(uint8_t *buffer, int buffer_size, F4FBox *box);
++int ff_free_f4f_box(F4FBox *box);
+--- a/libavformat/f4mmanifest.c        1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/f4mmanifest.c        2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,256 @@
++/*
++ * Adobe Media Manifest (F4M) File Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Media Manifest (F4M) File Parser
++ * @author Cory McCarthy
++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf
++ */
++
++#include "f4mmanifest.h"
++#include "libavutil/avstring.h"
++#include "libavutil/base64.h"
++#include <roxml.h>
++
++static int f4m_parse_bootstrap_info_node(node_t * node, F4MBootstrapInfo *bootstrap_info)
++{
++    const char *p;
++    uint8_t *dst;
++    int ret;
++    node_t *attr;
++
++    attr = roxml_get_attr(node, "id", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        av_strlcpy(bootstrap_info->id, p, sizeof(bootstrap_info->id));
++    }
++
++    attr = roxml_get_attr(node, "url", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        av_strlcpy(bootstrap_info->url, p, sizeof(bootstrap_info->url));
++    }
++
++    attr = roxml_get_attr(node, "profile", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        av_strlcpy(bootstrap_info->profile, p, sizeof(bootstrap_info->profile));
++    }
++
++    p = roxml_get_content(node, NULL, 0, NULL);
++    if(p) {
++        dst = av_mallocz(sizeof(uint8_t)*strlen(p));
++        if(!dst)
++            return AVERROR(ENOMEM);
++
++        if((ret = av_base64_decode(dst, p, strlen(p))) < 0) {
++            av_log(NULL, AV_LOG_ERROR, "f4mmanifest Failed to decode bootstrap node base64 metadata, ret: %d \n", ret);
++            av_free(dst);
++            return ret;
++        }
++
++        bootstrap_info->metadata = av_mallocz(sizeof(uint8_t)*ret);
++        if(!bootstrap_info->metadata)
++            return AVERROR(ENOMEM);
++
++        bootstrap_info->metadata_size = ret;
++        memcpy(bootstrap_info->metadata, dst, ret);
++
++        av_free(dst);
++    }
++
++    return 0;
++}
++
++static int f4m_parse_metadata_node(node_t * node, F4MMedia *media)
++{
++    const char  *p = NULL;
++    uint8_t *dst;
++    int ret;
++
++    node_t * metadata_node = roxml_get_chld(node, NULL, 0);
++    while(metadata_node) {
++        if(!strcmp(roxml_get_name(metadata_node, NULL, 0), "metadata")) {
++            p = roxml_get_content(metadata_node, NULL, 0, NULL);
++            break;
++        }
++        metadata_node = roxml_get_next_sibling(metadata_node);
++    }
++
++    if(!p)
++        return 0;
++
++    dst = av_mallocz(sizeof(uint8_t)*strlen(p));
++    if(!dst)
++        return AVERROR(ENOMEM);
++
++    if((ret = av_base64_decode(dst, p, strlen(p))) < 0) {
++        av_log(NULL, AV_LOG_ERROR, "f4mmanifest Failed to decode base64 metadata, ret: %d \n", ret);
++        av_free(dst);
++        return ret;
++    }
++
++    media->metadata = av_mallocz(sizeof(uint8_t)*ret);
++    if(!media->metadata)
++        return AVERROR(ENOMEM);
++
++    media->metadata_size = ret;
++    memcpy(media->metadata, dst, ret);
++
++    av_free(dst);
++
++    return 0;
++}
++
++static int f4m_parse_media_node(node_t * node, F4MMedia *media)
++{
++    const char  *p;
++    int ret;
++    node_t * attr;
++
++    attr = roxml_get_attr(node, "bitrate", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        media->bitrate = strtoul(p, NULL, 10);
++    }
++
++    attr = roxml_get_attr(node, "url", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        av_strlcpy(media->url, p, sizeof(media->url));
++    }
++
++    attr = roxml_get_attr(node, "bootstrapInfoId", 0);
++    p =roxml_get_content(attr,NULL,0,NULL);
++    if(p) {
++        av_strlcpy(media->bootstrap_info_id, p, sizeof(media->bootstrap_info_id));
++    }
++
++    if((ret = f4m_parse_metadata_node(node, media)) < 0) {
++        return ret;
++    }
++
++    return 0;
++}
++
++static int f4m_parse_manifest_node(node_t * root_node, F4MManifest *manifest)
++{
++    F4MBootstrapInfo *bootstrap_info;
++    F4MMedia *media;
++    node_t * node;
++    const char  *node_content;
++    int ret = 0,chld_idx=0;
++
++    for (chld_idx=0; chld_idx<roxml_get_chld_nb(root_node); chld_idx++){
++      node = roxml_get_chld(root_node, NULL, chld_idx);
++      const char * node_name = roxml_get_name(node, NULL, 0);
++        if(!strcmp(node_name, "text"))
++            continue;
++
++      node_content = roxml_get_content(node, NULL, 0, NULL);
++
++        if(!strcmp(node_name, "id") && node_content) {
++            av_strlcpy(manifest->id, node_content, sizeof(manifest->id));
++        } else if(!strcmp(node_name, "streamType") && node_content) {
++            av_strlcpy(manifest->stream_type, node_content, sizeof(manifest->stream_type));
++        } else if(!strcmp(node_name, "bootstrapInfo")) {
++            bootstrap_info = av_mallocz(sizeof(F4MBootstrapInfo));
++            if(!bootstrap_info)
++                return AVERROR(ENOMEM);
++            manifest->bootstraps[manifest->nb_bootstraps++] = bootstrap_info;
++            ret = f4m_parse_bootstrap_info_node(node, bootstrap_info);
++        } else if(!strcmp(node_name, "media")) {
++            media = av_mallocz(sizeof(F4MMedia));
++            if(!media)
++                return AVERROR(ENOMEM);
++            manifest->media[manifest->nb_media++] = media;
++            ret = f4m_parse_media_node(node, media);
++        } else if (!strcmp(node_name, "duration")) {
++          double duration = strtod(node_content, NULL);
++          manifest->duration = duration * AV_TIME_BASE;
++      }
++
++        if(ret < 0)
++            return ret;
++    }
++
++    return 0;
++}
++
++static int f4m_parse_xml_file(uint8_t *buffer, int size, F4MManifest *manifest)
++{
++    node_t * doc;
++    node_t * root_node;
++    int ret;
++
++    doc = roxml_load_buf(buffer);
++    if(!doc) {
++        return -1;
++    }
++
++    doc = roxml_get_root(doc);
++    root_node = roxml_get_chld(doc, NULL, 0);
++    if(!root_node) {
++        av_log(NULL, AV_LOG_ERROR, "f4mmanifest Root element not found \n");
++        roxml_close(doc);
++        return -1;
++    }
++    const char * root_node_name = roxml_get_name(root_node, NULL, 0);
++    if(strcmp(root_node_name, "manifest")) {
++        av_log(NULL, AV_LOG_ERROR, "f4mmanifest Root element is not named manifest, name = %s \n", root_node_name);
++        roxml_close(doc);
++        return -1;
++    }
++
++    ret = f4m_parse_manifest_node(root_node, manifest);
++    roxml_close(doc);
++
++    return ret;
++}
++
++int ff_parse_f4m_manifest(uint8_t *buffer, int size, F4MManifest *manifest)
++{
++    return f4m_parse_xml_file(buffer, size, manifest);
++}
++
++int ff_free_manifest(F4MManifest *manifest)
++{
++    F4MBootstrapInfo *bootstrap_info;
++    F4MMedia *media;
++    int i;
++
++    for(i = 0; i < manifest->nb_bootstraps; i++) {
++        bootstrap_info = manifest->bootstraps[i];
++        av_freep(&bootstrap_info->metadata);
++        av_freep(&bootstrap_info);
++    }
++
++    for(i = 0; i < manifest->nb_media; i++) {
++        media = manifest->media[i];
++        av_freep(&media->metadata);
++        av_freep(&media);
++    }
++
++    memset(manifest, 0x00, sizeof(F4MManifest));
++
++    return 0;
++}
+--- a/libavformat/f4mmanifest.h        1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/f4mmanifest.h        2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,65 @@
++/*
++ * Adobe Media Manifest (F4M) File Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe Media Manifest (F4M) File Parser
++ * @author Cory McCarthy
++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf
++ */
++
++#include "internal.h"
++
++#define MAX_NB_BOOTSTRAPS 32
++#define MAX_NB_MEDIA 32
++
++typedef struct F4MBootstrapInfo {
++    char id[MAX_URL_SIZE];
++    char url[MAX_URL_SIZE];
++    char profile[MAX_URL_SIZE];
++
++    int metadata_size;
++    uint8_t *metadata;
++} F4MBootstrapInfo;
++
++typedef struct F4MMedia {
++    int bitrate;
++    char url[MAX_URL_SIZE];
++    char bootstrap_info_id[MAX_URL_SIZE];
++
++    int metadata_size;
++    uint8_t *metadata;
++} F4MMedia;
++
++typedef struct F4MManifest {
++    char id[MAX_URL_SIZE];
++    char stream_type[MAX_URL_SIZE];
++    int64_t duration;
++
++    int nb_bootstraps;
++    F4MBootstrapInfo *bootstraps[MAX_NB_BOOTSTRAPS];
++
++    int nb_media;
++    F4MMedia *media[MAX_NB_MEDIA];
++} F4MManifest;
++
++int ff_parse_f4m_manifest(uint8_t *buffer, int size, F4MManifest *manifest);
++int ff_free_manifest(F4MManifest *manifest);
+--- a/libavformat/flvdec.c     2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/flvdec.c     2023-02-14 17:27:17.200274887 +0100
+@@ -368,6 +368,11 @@ static int amf_get_string(AVIOContext *i
+     return length;
+ }
++int flv_amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
++{
++    return amf_get_string(ioc, buffer, buffsize);
++}
++
+ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t max_pos)
+ {
+     FLVContext *flv       = s->priv_data;
+--- a/libavformat/flv.h        2017-12-31 23:35:49.000000000 +0100
++++ b/libavformat/flv.h        2023-02-14 17:27:17.200274887 +0100
+@@ -135,4 +135,6 @@ typedef enum {
+     AMF_DATA_TYPE_UNSUPPORTED = 0x0d,
+ } AMFDataType;
++int flv_amf_get_string(AVIOContext *ioc, char *buffer, int buffsize);
++
+ #endif /* AVFORMAT_FLV_H */
+--- a/libavformat/flvtag.c     1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/flvtag.c     2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,408 @@
++/*
++ * Adobe FLV Tag Parser
++ * Copyright (c) 2013 Cory McCarthy
++ * Copyright (c) 2014 martii
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief FLV Tag Parser for Adobe HDS F4F Files
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ */
++
++#include "flvtag.h"
++#include "libavformat/avio.h"
++#include "libavcodec/get_bits.h"
++#include "libavutil/channel_layout.h"
++#include "flv.h"
++
++typedef struct FLVTagAudioHeader {
++    uint8_t sound_format;
++    uint8_t sound_rate;
++    uint8_t sound_size;
++    uint8_t sound_type;
++
++    uint8_t aac_packet_type;
++} FLVTagAudioHeader;
++
++typedef struct FLVTagAudioBody {
++    uint8_t sound_format;
++    uint8_t sound_rate;
++    uint8_t sound_size;
++    uint8_t sound_type;
++
++    uint8_t aac_packet_type;
++} FLVTagAudioBody;
++
++typedef struct FLVTagVideoHeader {
++    uint8_t frame_type;
++    uint8_t codec_id;
++
++    uint8_t avc_packet_type;
++    int32_t cts;
++} FLVTagVideoHeader;
++
++typedef struct FLVTagVideoBody {
++    //AVCDecoderConfigurationRecord
++    uint8_t configuration_version;
++    uint8_t avc_profile_indication;
++    uint8_t profile_compatibility;
++    uint8_t avc_level_indication;
++
++    uint8_t length_size_minus_one;
++
++    uint8_t *sps_data;
++    int sps_data_size;
++
++    uint8_t *pps_data;
++    int pps_data_size;
++} FLVTagVideoBody;
++
++static int flv_tag_parse_audio_header(AVIOContext *in,
++    FLVTagAudioHeader *header)
++{
++    int ret = 0;
++    uint8_t byte;
++
++    byte = avio_r8(in);
++    ret++;
++
++    header->sound_format = byte & FLV_AUDIO_CODECID_MASK;
++    header->sound_rate   = (byte >> FLV_AUDIO_SAMPLERATE_OFFSET) & 0x03;
++    header->sound_size   = (byte >> FLV_AUDIO_SAMPLESSIZE_OFFSET) & 0x01;
++    header->sound_type   = byte & 0x01;
++
++    if(header->sound_format == FLV_CODECID_AAC) {
++      header->aac_packet_type = avio_r8(in);
++      ret++;
++    }
++
++    return ret;
++}
++
++static int flv_tag_parse_audio_body(AVIOContext *in, uint32_t data_size,
++    FLVTagAudioHeader *header, FLVTagAudioBody *body, FLVMediaSample **sample_out)
++{
++    FLVMediaSample *sample;
++
++    if(header->sound_format != FLV_CODECID_AAC) {
++      av_log(NULL, AV_LOG_ERROR, "flvtag Unhandled sound format %d\n", header->sound_format >> FLV_AUDIO_CODECID_OFFSET);
++      return 0;
++    }
++
++    if(header->aac_packet_type == 0) {
++      av_log(NULL, AV_LOG_DEBUG, "flvtag skipped aac_packet_type %d\n", header->aac_packet_type);
++      return 0;
++    }
++
++    if(header->aac_packet_type != 1) {
++      av_log(NULL, AV_LOG_ERROR, "flvtag Unhandled aac_packet_type %d\n", header->aac_packet_type);
++      return 0;
++    }
++
++    sample = av_mallocz(sizeof(FLVMediaSample));
++    if(!sample)
++      return AVERROR(ENOMEM);
++
++    sample->type = AVMEDIA_TYPE_AUDIO;
++
++    sample->data = av_mallocz(sizeof(uint8_t) * data_size);
++    if(!sample->data) {
++      av_free(sample);
++      return AVERROR(ENOMEM);
++    }
++
++    sample->data_size = data_size;
++    avio_read(in, sample->data, sample->data_size);
++
++    if(sample_out)
++      *sample_out = sample;
++
++    return data_size;
++}
++
++static int flv_tag_parse_video_header(AVIOContext *in,
++    FLVTagVideoHeader *header)
++{
++    int ret = 0;
++    uint8_t byte;
++
++    byte = avio_r8(in);
++    ret++;
++
++    header->frame_type = byte >> 4;
++    header->codec_id = byte & 0x0F;
++
++    if(header->codec_id == 0x07) {
++      header->avc_packet_type = avio_r8(in);
++      header->cts = (avio_rb24(in) + 0xff800000) ^ 0xff800000;
++      ret += 4;
++    }
++
++    return ret;
++}
++
++static int flv_tag_parse_video_body(AVIOContext *in, uint32_t data_size,
++    FLVTagVideoHeader *header, FLVTagVideoBody *body, FLVMediaSample **sample_out)
++{
++    FLVMediaSample *sample = NULL;
++    uint8_t *p;
++    uint8_t nb_sps, nb_pps;
++    uint16_t sps_length, pps_length;
++    int i, ret = 0;
++
++    if(header->frame_type == 0x05) {
++      avio_r8(in);
++      return 1;
++    }
++
++    if(header->codec_id != FLV_CODECID_H264) {
++      av_log(NULL, AV_LOG_ERROR, "flvtag Unhandled video codec id, id: %d \n", header->codec_id);
++      return 0;
++    }
++
++    if(header->avc_packet_type == 0x00) {
++      body->configuration_version = avio_r8(in);
++      body->avc_profile_indication = avio_r8(in);
++      body->profile_compatibility = avio_r8(in);
++      body->avc_level_indication = avio_r8(in);
++      ret += 4;
++
++      body->length_size_minus_one = avio_r8(in) & 0x03;
++      ret++;
++
++      if(body->sps_data_size > 0)
++          av_freep(&body->sps_data);
++      if(body->pps_data_size > 0)
++          av_freep(&body->pps_data);
++
++      nb_sps = avio_r8(in) & 0x1F;
++      ret++;
++
++      for(i = 0; i < nb_sps; i++) {
++          sps_length = avio_rb16(in);
++          ret += 2;
++
++          body->sps_data = av_realloc(body->sps_data,
++              body->sps_data_size + sps_length + 4);
++          if(!body->sps_data)
++              return AVERROR(ENOMEM);
++
++          p = body->sps_data + body->sps_data_size;
++
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x01;
++          body->sps_data_size += 4;
++
++          avio_read(in, p, sps_length);
++          body->sps_data_size += sps_length;
++
++          ret += sps_length;
++      }
++
++      nb_pps = avio_r8(in);
++      ret++;
++
++      for(i = 0; i < nb_pps; i++) {
++          pps_length = avio_rb16(in);
++          ret += 2;
++
++          body->pps_data = av_realloc(body->pps_data,
++              body->pps_data_size + pps_length + 4);
++          if(!body->pps_data)
++              return AVERROR(ENOMEM);
++
++          p = body->pps_data + body->pps_data_size;
++
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x01;
++          body->pps_data_size += 4;
++
++          avio_read(in, p, pps_length);
++          body->pps_data_size += pps_length;
++
++          ret += pps_length;
++      }
++    }
++    else if(header->avc_packet_type == 0x01) { // AVC NALU
++      sample = av_mallocz(sizeof(FLVMediaSample));
++      if(!sample)
++          return AVERROR(ENOMEM);
++
++      sample->type = AVMEDIA_TYPE_VIDEO;
++
++      sample->data_size = body->sps_data_size + body->pps_data_size;
++      sample->data_size += 4 + data_size;
++
++      sample->data = av_mallocz(sizeof(uint8_t) * sample->data_size);
++      if(!sample->data) {
++          av_free(sample);
++          return AVERROR(ENOMEM);
++      }
++
++      p = sample->data;
++
++      memcpy(p, body->sps_data, body->sps_data_size);
++      p += body->sps_data_size;
++
++      memcpy(p, body->pps_data, body->pps_data_size);
++      p += body->pps_data_size;
++
++      while(ret < data_size) {
++          int length_size = body->length_size_minus_one + 1;
++          uint32_t nal_size = 0;
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x00;
++          *p++ = 0x01;
++
++          while (length_size) {
++              nal_size <<= 8;
++              nal_size |= avio_r8(in);
++              length_size--;
++              ret++;
++          }
++
++          avio_read(in, p, nal_size);
++          p += nal_size;
++          ret += nal_size;
++      }
++    }
++
++    if(sample_out)
++      *sample_out = sample;
++
++    return ret;
++}
++
++static int flv_tag_decode_body(uint8_t *buffer, int buffer_size,
++    FLVMediaSample **samples, int *nb_samples_out)
++{
++    FLVMediaSample *sample;
++    AVIOContext *in;
++    FLVTagAudioHeader audio_header;
++    FLVTagAudioBody audio_body;
++    FLVTagVideoHeader video_header;
++    FLVTagVideoBody video_body;
++    uint8_t byte, filter, tag_type;
++    uint32_t data_size, dts, stream_id;
++    int nb_samples = 0;
++    int ret;
++
++    memset(&audio_header, 0x00, sizeof(FLVTagAudioHeader));
++    memset(&audio_body, 0x00, sizeof(FLVTagAudioBody));
++    memset(&video_header, 0x00, sizeof(FLVTagVideoHeader));
++    memset(&video_body, 0x00, sizeof(FLVTagVideoBody));
++
++    in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL, NULL);
++    if(!in)
++      return AVERROR(ENOMEM);
++
++    while(!avio_feof(in)) {
++      byte = avio_r8(in);
++      filter = byte & 0x20;
++      tag_type = (byte & 0x01F);
++
++      data_size = avio_rb24(in);
++
++      dts = avio_rb24(in);
++      dts |= avio_r8(in) << 24;
++
++      stream_id = avio_rb24(in);
++      if(stream_id != 0) {
++          av_log(NULL, AV_LOG_ERROR, "flvtag Invalid stream_id %d \n", stream_id);
++          return -1;
++      }
++
++      if(tag_type == FLV_TAG_TYPE_AUDIO) {
++          data_size -= flv_tag_parse_audio_header(in, &audio_header);
++      } else if(tag_type == FLV_TAG_TYPE_VIDEO) {
++          data_size -= flv_tag_parse_video_header(in, &video_header);
++      }
++
++#if 0
++      if(filter) {
++          //EncryptionTagHeader
++          //FilterParams
++      }
++#endif
++
++      sample = NULL;
++
++      if(tag_type == FLV_TAG_TYPE_AUDIO) {
++          if((ret = flv_tag_parse_audio_body(in, data_size,
++                  &audio_header, &audio_body, &sample)) < 0) {
++
++              av_free(in);
++              return ret;
++          }
++          data_size -= ret;
++      } else if(tag_type == FLV_TAG_TYPE_VIDEO) {
++          if((ret = flv_tag_parse_video_body(in, data_size,
++                  &video_header, &video_body, &sample)) < 0) {
++
++              av_free(in);
++              return ret;
++          }
++          data_size -= ret;
++      }
++#if 0
++      else if(tag_type == 18) {
++          //ScriptData
++      }
++#endif
++
++      if(sample) {
++          sample->dts = dts;
++          if (tag_type == FLV_TAG_TYPE_VIDEO)
++                  sample->pts = dts + video_header.cts;
++          else
++                  sample->pts = AV_NOPTS_VALUE;
++          samples[nb_samples++] = sample;
++      }
++
++      if(data_size) {
++          avio_skip(in, data_size);
++      }
++      avio_rb32(in);
++    }
++
++    av_free(in);
++
++    if(video_body.sps_data_size > 0)
++      av_free(video_body.sps_data);
++    if(video_body.pps_data_size > 0)
++      av_free(video_body.pps_data);
++
++    if(nb_samples_out)
++      *nb_samples_out = nb_samples;
++
++    return 0;
++}
++
++int ff_decode_flv_body(uint8_t *buffer, int buffer_size,
++    FLVMediaSample **samples, int *nb_samples_out)
++{
++    return flv_tag_decode_body(buffer, buffer_size, samples, nb_samples_out);
++}
+--- a/libavformat/flvtag.h     1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/flvtag.h     2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,40 @@
++/*
++ * Adobe FLV Tag Parser
++ * Copyright (c) 2013 Cory McCarthy
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe FLV Tag Parser for Adobe HDS F4F Files
++ * @author Cory McCarthy
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ */
++
++#include "libavutil/avutil.h"
++
++typedef struct FLVMediaSample {
++    enum AVMediaType type;
++    uint64_t dts;
++    uint64_t pts;
++    int data_size;
++    uint8_t *data;
++} FLVMediaSample;
++
++int ff_decode_flv_body(uint8_t *buffer, int buffer_size,
++    FLVMediaSample **samples, int *nb_samples_out);
+--- a/libavformat/hdsdec.c     1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/hdsdec.c     2023-02-14 17:27:17.200274887 +0100
+@@ -0,0 +1,1036 @@
++/*
++ * Adobe HTTP Dynamic Streaming (HDS) demuxer
++ * Copyright (c) 2013 Cory McCarthy
++ * Copyright (c) 2014 martii
++ *
++ * 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
++ */
++
++/**
++ * @file
++ * @brief Adobe HTTP Dynamic Streaming (HDS) demuxer
++ * @author Cory McCarthy
++ * @see http://www.adobe.com/devnet/hds.html
++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-hds-specification.pdf
++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf
++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
++ *
++ * @note Link for a HDS test player below:
++ * @see http://mediapm.edgesuite.net/edgeflash/public/zeri/debug/Main.html
++ *
++ * @note Test streams are below:
++ * @test http://multiplatform-f.akamaihd.net/z/multi/april11/hdworld/hdworld_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/april11/cctv/cctv_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/april11/sintel/sintel-hd_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/akamai10year/Akamai_10_Year_,200,300,600,800,1000,1500,2500,4000,k.mp4.csmil/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/seeker/LegendofSeeker_16x9_24fps_H264_,400K,650K,1Mbps,1.4Mbps,1.8Mbps,2.5Mbps,.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,1280x720_2000,1280x720_3000,.f4v.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/companion/nba_game/nba_game.mov_,300,600,800,1000,2500,4000,9000,k.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/companion/big_bang_theory/big_bang_theory.mov_,300,600,800,1000,2500,4000,9000,k.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/shuttle/shuttle_,300,600,800,1000,k.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiplatform-f.akamaihd.net/z/multi/up_trailer/up_trailer_720p_,300,600,800,1000,k.mp4.csmil/manifest.f4m?hdcore
++ * @test http://multiformatlive-f.akamaihd.net/z/demostream_1@2131/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/darkknight/darkknight.smil/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/amours/amours.smil/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/robinhood/robinhood.smil/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/wallstreet/wallstreet.smil/manifest.f4m?hdcore
++ * @test http://zerihdndemo-f.akamaihd.net/z/h264/rockandroll/rockandroll.smil/manifest.f4m?hdcore
++ * @test http://184.72.239.149/vod/smil:bigbuckbunny.smil/manifest.f4m
++ */
++
++#include "avformat.h"
++#include "internal.h"
++#include "url.h"
++#include "avio_internal.h"
++#include "libavutil/avstring.h"
++#include "libavutil/parseutils.h"
++#include "libavutil/opt.h"
++#include "libavutil/dict.h"
++#include "libavutil/time.h"
++
++#include "amfmetadata.h"
++#include "f4mmanifest.h"
++#include "f4fbox.h"
++#include "flvtag.h"
++
++#include <unistd.h>
++#include <pthread.h>
++#include <semaphore.h>
++#include <alloca.h>
++
++#define MAX_NB_SAMPLES 1024
++
++struct HDSDownloadThreadData
++{
++    pthread_mutex_t mutex;                    // protects access to this struct
++    pthread_t thread;                         // download thread id
++    sem_t *to_thread;                         // send signal to thread
++    sem_t *to_caller;                         // send signal to caller
++    sem_t _to_thread;                         // send signal to thread
++    sem_t _to_caller;                         // send signal to caller
++    char *cookies;                            // cookies, if any
++    char *url;                                        // current url
++    int run;                                  // thread will run until == 0
++    int abort;                                        // setting this to !=0 aborts download
++    uint8_t *buffer;                          // set by thread, unset by thread or caller
++    uint32_t buflen;                          // set by thread, unset by thread or caller
++    AVIOInterruptCB *interrupt_callback;
++    AVIOInterruptCB *abort_callback;
++    AVIOInterruptCB download_abort_callback;
++};
++
++typedef struct HDSBootstrapInfo {
++    char id[MAX_URL_SIZE];
++    char url[MAX_URL_SIZE];
++    char profile[MAX_URL_SIZE];
++    char *quality;
++
++    F4FBox box;
++} HDSBootstrapInfo;
++
++typedef struct HDSMedia {
++    int media_index;
++    int bitrate;
++    char url[MAX_URL_SIZE];
++    HDSBootstrapInfo *bootstrap_info;
++
++    AVStream *audio_stream;
++    AVStream *video_stream;
++
++    int nb_samples;
++    FLVMediaSample *samples[MAX_NB_SAMPLES];
++    int sample_index;
++
++    unsigned int nb_fragments_read;
++
++    struct HDSDownloadThreadData download_data;
++} HDSMedia;
++
++typedef struct HDSContext {
++    char id[MAX_URL_SIZE];
++    char base_url[MAX_URL_SIZE];
++    int is_live;
++    int last_media_index;
++
++    int nb_bootstraps;
++    HDSBootstrapInfo *bootstrap_info[MAX_NB_BOOTSTRAPS];
++
++    int nb_media;
++    HDSMedia *media[MAX_NB_MEDIA];
++
++    int64_t seek_timestamp;
++    char *cookies;
++} HDSContext;
++
++static void construct_bootstrap_url(const char *base_url, const char *bootstrap_url,
++    const char *suffix, char *url_out, size_t url_size)
++{
++    char *p;
++
++    p = url_out;
++    p += av_strlcat(p, base_url, url_size);
++    p += av_strlcat(p, bootstrap_url, url_size);
++    p += av_strlcat(p, suffix, url_size);
++}
++
++static int download_bootstrap(AVFormatContext *s, HDSBootstrapInfo *bootstrap,
++    uint8_t **buffer_out, int *buffer_size_out)
++{
++    HDSContext *c = s->priv_data;
++    URLContext *puc = NULL;
++    AVDictionary *opts = NULL;
++    char url[MAX_URL_SIZE];
++    uint8_t *buffer;
++    int buffer_size;
++    int ret;
++
++    memset(url, 0x00, sizeof(url));
++
++    if(!av_stristr(bootstrap->url, "?") && av_stristr(s->filename, "?")) {
++      construct_bootstrap_url(c->base_url, bootstrap->url, av_stristr(s->filename, "?"), url, MAX_URL_SIZE);
++    } else {
++      construct_bootstrap_url(c->base_url, bootstrap->url, "", url, MAX_URL_SIZE);
++    }
++
++    av_dict_set(&opts, "cookies", c->cookies, 0);
++    ret = ffurl_open(&puc, url, AVIO_FLAG_READ, &s->interrupt_callback, &opts);
++    av_dict_free(&opts);
++
++    if(ret < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to start downloading bootstrap, ret: %d\n", ret);
++      return ret;
++    }
++
++    buffer_size = ffurl_size(puc);
++    buffer = av_mallocz(buffer_size+AV_INPUT_BUFFER_PADDING_SIZE);
++    if(!buffer)
++      return AVERROR(ENOMEM);
++
++    if((ret = ffurl_read_complete(puc, buffer, buffer_size)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to download bootstrap, ret: %d\n", ret);
++      av_free(buffer);
++      return ret;
++    }
++
++    if (c->cookies)
++      av_freep(&c->cookies);
++    av_opt_get(puc->priv_data, "cookies", 0, (uint8_t **) &c->cookies);
++    if (c->cookies && !strlen(c->cookies))
++      av_freep(&c->cookies);
++
++    if((ret = ffurl_close(puc)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to finish downloading bootstrap, ret: %d\n", ret);
++      av_free(buffer);
++      return ret;
++    }
++
++    if(buffer_out)
++      *buffer_out = buffer;
++    else
++      av_free(buffer);
++
++    if(buffer_size_out)
++      *buffer_size_out = buffer_size;
++
++    return 0;
++}
++
++static int create_bootstrap_info(AVFormatContext *s, F4MBootstrapInfo *f4m_bootstrap_info)
++{
++    HDSContext *c = s->priv_data;
++    HDSBootstrapInfo *bootstrap_info;
++    uint8_t *buffer;
++    int buffer_size, ret;
++
++    bootstrap_info = av_mallocz(sizeof(HDSBootstrapInfo));
++    if(!bootstrap_info)
++      return AVERROR(ENOMEM);
++
++    c->bootstrap_info[c->nb_bootstraps++] = bootstrap_info;
++
++    memcpy(bootstrap_info->id, f4m_bootstrap_info->id, sizeof(bootstrap_info->id));
++    memcpy(bootstrap_info->url, f4m_bootstrap_info->url, sizeof(bootstrap_info->url));
++    memcpy(bootstrap_info->profile, f4m_bootstrap_info->profile, sizeof(bootstrap_info->profile));
++
++    buffer = f4m_bootstrap_info->metadata;
++    buffer_size = f4m_bootstrap_info->metadata_size;
++
++    if(buffer_size > 0) {
++      if((ret = ff_parse_f4f_box(buffer, buffer_size, &(bootstrap_info->box))) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to parse metadata bootstrap box, ret: %d\n", ret);
++          return ret;
++      }
++    } else {
++      if((ret = download_bootstrap(s, bootstrap_info, &buffer, &buffer_size)) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to download bootstrap, ret: %d\n", ret);
++          return ret;
++      }
++
++      if((ret = ff_parse_f4f_box(buffer, buffer_size, &(bootstrap_info->box))) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to parse downloaded bootstrap box, ret: %d\n", ret);
++          return ret;
++      }
++    }
++
++    return 0;
++}
++
++static int create_streams(AVFormatContext *s, HDSMedia *media, AMFMetadata *metadata, int i)
++{
++    if (metadata->video_codec_id) {
++      AVStream *st = avformat_new_stream(s, NULL);
++      if(!st)
++          return AVERROR(ENOMEM);
++
++      media->video_stream = st;
++      avpriv_set_pts_info(st, 32, 1, 1000);
++      st->discard = AVDISCARD_ALL;
++      st->id = 0 + 2 * i;
++      st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
++      st->codecpar->codec_id = metadata->video_codec_id;
++      st->codecpar->width = metadata->width;
++      st->codecpar->height = metadata->height;
++      st->codecpar->bit_rate = metadata->video_data_rate * 1000;
++    }
++
++    if (metadata->audio_codec_id) {
++      AVStream *st = avformat_new_stream(s, NULL);
++      if(!st)
++          return AVERROR(ENOMEM);
++
++      media->audio_stream = st;
++      avpriv_set_pts_info(st, 32, 1, 1000);
++      st->discard = AVDISCARD_ALL;
++      st->id = 1 + 2 * i;
++      st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
++      st->codecpar->codec_id = metadata->audio_codec_id;
++      st->codecpar->channels = metadata->nb_audio_channels;
++      st->codecpar->channel_layout = (st->codecpar->channels == 2) ?  AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
++      st->codecpar->sample_rate = metadata->audio_sample_rate;
++      st->codecpar->sample_fmt = AV_SAMPLE_FMT_S16;
++      st->codecpar->bit_rate = metadata->audio_data_rate * 1000;
++      st->need_parsing = metadata->audio_stream_need_parsing;
++    }
++
++    return 0;
++}
++
++static int create_media(AVFormatContext *s, F4MMedia *f4m_media, int i)
++{
++    HDSContext *c = s->priv_data;
++    HDSMedia *media;
++    AMFMetadata metadata;
++    int ret, j;
++
++    media = av_mallocz(sizeof(HDSMedia));
++    if(!media)
++      return AVERROR(ENOMEM);
++
++    c->media[c->nb_media++] = media;
++
++    media->media_index = i;
++    media->bitrate = f4m_media->bitrate;
++    memcpy(media->url, f4m_media->url, sizeof(media->url));
++
++    for(j = 0; j < c->nb_bootstraps; j++) {
++      if(!av_strcasecmp(f4m_media->bootstrap_info_id, c->bootstrap_info[j]->id))
++          continue;
++      media->bootstrap_info = c->bootstrap_info[j];
++      break;
++    }
++
++    memset(&metadata, 0x00, sizeof(AMFMetadata));
++    metadata.nb_audio_channels = 1;
++    if((ret = ff_parse_amf_metadata(f4m_media->metadata, f4m_media->metadata_size, &metadata)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to parse metadata, ret: %d\n", ret);
++      return ret;
++    }
++
++    return create_streams(s, media, &metadata, i);
++}
++
++static int create_pmt(AVFormatContext *s)
++{
++    HDSContext *c = s->priv_data;
++    HDSMedia *media;
++    AVProgram *p;
++    int i, j;
++
++    j = 0;
++    for(i = 0; i < c->nb_media; i++) {
++      media = c->media[i];
++
++      p = av_new_program(s, j++);
++      if(!p)
++          return AVERROR(ENOMEM);
++
++      av_dict_set(&p->metadata,"name",
++          av_asprintf("%d kbit/s", media->bitrate), 0);
++
++      if (media->video_stream)
++          av_program_add_stream_index(s, p->id, media->video_stream->index);
++      if (media->audio_stream)
++          av_program_add_stream_index(s, p->id, media->audio_stream->index);
++    }
++
++    return 0;
++}
++
++static void download_thread_start(AVFormatContext *s, HDSMedia *media);
++
++static int initialize_context(AVFormatContext *s, F4MManifest *manifest)
++{
++    HDSContext *c = s->priv_data;
++    F4MBootstrapInfo *f4m_bootstrap_info;
++    F4MMedia *f4m_media;
++    int i, ret;
++
++    for(i = 0; i < manifest->nb_bootstraps; i++) {
++      f4m_bootstrap_info = manifest->bootstraps[i];
++      if((ret = create_bootstrap_info(s, f4m_bootstrap_info)) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to create bootstrap_info, ret: %d\n", ret);
++          return ret;
++      }
++    }
++
++    for(i = 0; i < manifest->nb_media; i++) {
++      f4m_media = manifest->media[i];
++      if((ret = create_media(s, f4m_media, i)) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to create media, ret: %d\n", ret);
++          return ret;
++      }
++    }
++
++    if((ret = create_pmt(s)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to create PMT, ret: %d\n", ret);
++      return ret;
++    }
++
++    if(!av_strcasecmp(manifest->stream_type, "live"))
++      c->is_live = 1;
++
++    s->duration = manifest->duration;
++    c->seek_timestamp = AV_NOPTS_VALUE;
++
++    for(i = 0; i < c->nb_media; i++)
++      download_thread_start(s, c->media[i]);
++
++    return 0;
++}
++
++//#define HDS_ENABLE_LOG_CALLBACK
++#ifdef HDS_ENABLE_LOG_CALLBACK
++static void log_callback(void *ptr __attribute__ ((unused)), int lvl __attribute__ ((unused)), const char *format, va_list ap)
++{
++    vfprintf(stderr, format, ap);
++}
++#endif
++
++static int hds_read_header(AVFormatContext *s)
++{
++    HDSContext *c = s->priv_data;
++    AVIOContext *in = s->pb;
++    F4MManifest manifest;
++    int64_t filesize;
++    uint8_t *buf;
++    char *p;
++    int ret;
++#ifdef HDS_ENABLE_LOG_CALLBACK
++    av_log_set_callback(log_callback);
++#endif
++
++    p = av_stristr(s->filename, "manifest.f4m");
++    if(!p) {
++      av_log(NULL, AV_LOG_ERROR, "hds \"manifest.f4m\" is not a substring of \"%s\"\n", s->filename);
++      return -1;
++    }
++    av_strlcpy(c->base_url, s->filename, p - s->filename + 1);
++
++    filesize = avio_size(in);
++    if(filesize <= 0)
++      return -1;
++
++    buf = av_mallocz(filesize*sizeof(uint8_t));
++    if(!buf)
++      return AVERROR(ENOMEM);
++
++    avio_read(in, buf, filesize);
++
++    memset(&manifest, 0x00, sizeof(F4MManifest));
++    ret = ff_parse_f4m_manifest(buf, filesize, &manifest);
++
++    av_free(buf);
++
++    if (ret > -1)
++      ret = initialize_context(s, &manifest);
++
++    ff_free_manifest(&manifest);
++
++    return ret;
++}
++
++static void construct_fragment_url(const char *base_url, const char *media_url,
++    unsigned int segment, unsigned int fragment, const char *suffix, char *url_out, size_t url_size)
++{
++    char *p;
++    char *fragment_str;
++
++    p = url_out;
++    p += av_strlcat(p, base_url, url_size);
++    p += av_strlcat(p, media_url, url_size);
++
++    fragment_str = av_asprintf("Seg%u-Frag%u", segment, fragment);
++    p += av_strlcat(p, fragment_str, url_size);
++    av_free(fragment_str);
++
++    p += av_strlcat(p, suffix, url_size);
++}
++
++static int get_fragment_offset(HDSMedia *media, int64_t timestamp)
++{
++    F4FBootstrapInfoBox *abst = &(media->bootstrap_info->box.abst);
++    int fragments_max = 0;
++    int i, j;
++
++    for(i = 0; i < abst->nb_segment_run_table_boxes; i++) {
++      F4FSegmentRunTableBox *asrt = abst->segment_run_table_boxes[i];
++      int found = 0;
++      if (asrt->nb_quality_entries && media->bootstrap_info->quality)
++          for(j = 0; !found && j < asrt->nb_quality_entries; j++)
++              found = !strcmp(asrt->quality_entries[j], media->bootstrap_info->quality);
++      else
++          found = 1;
++
++      if (found) {
++          F4FSegmentRunEntry *segment_entry = asrt->segment_run_entries[asrt->nb_segment_run_entries - 1];
++          fragments_max = segment_entry->fragments_per_segment;
++          break;
++      }
++    }
++
++    for(i = 0; i < abst->nb_fragment_run_table_boxes; i++) {
++      int found = 0;
++      F4FFragmentRunTableBox *afrt = abst->fragment_run_table_boxes[i];
++      if (afrt->nb_quality_entries && media->bootstrap_info->quality)
++          for(j = 0; !found && j < afrt->nb_quality_entries; j++)
++              found = !strcmp(afrt->quality_entries[j], media->bootstrap_info->quality);
++      else
++          found = 1;
++
++      if (found) {
++          for(j = 0; j < afrt->nb_fragment_run_entries; j++) {
++              F4FFragmentRunEntry *fre = afrt->fragment_run_entries[j];
++              int fragcount;
++              if (j + 1 < afrt->nb_fragment_run_entries) {
++                  fragcount = afrt->fragment_run_entries[j + 1]->first_fragment - fre->first_fragment;
++                  fragments_max -= fragcount;
++              } else
++                  fragcount = fragments_max;
++
++              if (timestamp >= fre->first_fragment_time_stamp && timestamp <= fre->first_fragment_time_stamp + fragcount * fre->fragment_duration)
++                  return fre->first_fragment + (timestamp - fre->first_fragment_time_stamp)/fre->fragment_duration;
++          }
++          break;
++      }
++    }
++    return 0;
++}
++
++static int get_segment_fragment(int is_live, HDSMedia *media, unsigned int *segment_out, unsigned int *fragment_out)
++{
++    F4FBootstrapInfoBox *abst = &(media->bootstrap_info->box.abst);
++    unsigned int segment = ~0, fragment = ~0, fragments_max = 0;
++    int i, j, skip;
++
++    if (is_live) {
++      // FIXME. This is a crude hack.
++      *segment_out = 1;
++      *fragment_out = media->nb_fragments_read;
++      return 0;
++    }
++
++    skip = media->nb_fragments_read;
++    for(i = 0; segment == ~0 && i < abst->nb_segment_run_table_boxes; i++) {
++      F4FSegmentRunTableBox *asrt = abst->segment_run_table_boxes[i];
++      int found = 0;
++      if (asrt->nb_quality_entries && media->bootstrap_info->quality)
++          for(j = 0; !found && j < asrt->nb_quality_entries; j++)
++              found = !strcmp(asrt->quality_entries[j], media->bootstrap_info->quality);
++      else
++          found = 1;
++
++      if (found) {
++          for (j = 0; segment == ~0 && j < asrt->nb_segment_run_entries; j++) {
++              F4FSegmentRunEntry *segment_entry = asrt->segment_run_entries[j];
++
++              if (segment_entry->fragments_per_segment < skip) {
++                  skip -= segment_entry->fragments_per_segment;
++                  fragments_max = segment_entry->fragments_per_segment;
++              } else
++                  segment = segment_entry->first_segment;
++          }
++          break;
++      }
++    }
++
++    if(segment == ~0) {
++      av_log(NULL, AV_LOG_ERROR, "hds segment entry for next fragment (%u) not found, skip: %d\n", media->nb_fragments_read, skip);
++      return 0;
++    }
++
++    skip = media->nb_fragments_read;
++    for(i = 0; fragment == ~0 && i < abst->nb_fragment_run_table_boxes; i++) {
++      int found = 0;
++      F4FFragmentRunTableBox *afrt = abst->fragment_run_table_boxes[i];
++      if (afrt->nb_quality_entries && media->bootstrap_info->quality)
++          for(j = 0; !found && j < afrt->nb_quality_entries; j++)
++              found = !strcmp(afrt->quality_entries[j], media->bootstrap_info->quality);
++      else
++          found = 1;
++
++      if (found) {
++          for(j = 0; fragment == ~0 && j < afrt->nb_fragment_run_entries; j++) {
++              int fragcount;
++              F4FFragmentRunEntry *fre = afrt->fragment_run_entries[j];
++              if (j + 1 < afrt->nb_fragment_run_entries) {
++                  fragcount = afrt->fragment_run_entries[j + 1]->first_fragment - fre->first_fragment;
++                  fragments_max -= fragcount;
++              } else
++                  fragcount = fragments_max;
++
++              if (fragcount < skip)
++                  fragcount -= skip;
++              else {
++                  fragment = fre->first_fragment + skip;
++                  skip = 0;
++              }
++          }
++          break;
++      }
++    }
++
++    if (!is_live && skip > 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds fragment %d fragments beyond EOF\n", skip);
++      return AVERROR_EOF;
++    }
++
++    if(fragment == ~0) {
++      av_log(NULL, AV_LOG_ERROR, "hds fragment entry not found\n");
++      return -1;
++    }
++
++    if(segment_out)
++      *segment_out = segment;
++    if(fragment_out)
++      *fragment_out = fragment;
++
++    return 0;
++}
++
++static int download_abort_callback_function(void *opaque)
++{
++    HDSMedia *media = (HDSMedia *) opaque;
++    return media->download_data.abort || ff_check_interrupt(media->download_data.interrupt_callback);
++}
++
++static void *download_thread(void *opaque)
++{
++    HDSMedia *media = (HDSMedia *) opaque;
++
++    while (media->download_data.run && !download_abort_callback_function(media)) {
++      AVDictionary *opts = NULL;
++      URLContext *puc = NULL;
++      uint8_t *buffer = NULL;
++      int buffer_size = 0, ret;
++      char *url = NULL;
++      int url_len;
++      int tries_left = 15;
++
++      if (sem_wait(media->download_data.to_thread))
++          break;
++      if (!media->download_data.run)
++          continue;
++
++      pthread_mutex_lock(&media->download_data.mutex);
++      media->download_data.abort = 0;
++      if (media->download_data.buffer)
++          av_freep(&media->download_data.buffer);
++      media->download_data.buflen = 0;
++      // generate local copies from HDSDownloadThreadData
++      if (media->download_data.cookies)
++          av_dict_set(&opts, "cookies", media->download_data.cookies, 0);
++      url_len = strlen(media->download_data.url) + 1;
++      url = alloca(url_len);
++      strncpy(url, media->download_data.url, url_len);
++      pthread_mutex_unlock(&media->download_data.mutex);
++      //av_log(NULL, AV_LOG_DEBUG, "%s %d: downloading %s\n", __FILE__,__LINE__, url);
++
++      // initiate download
++      do {
++          ret = ffurl_open(&puc, url, AVIO_FLAG_READ, &media->download_data.download_abort_callback, &opts);
++          if (ret < 0) {
++              sleep(1);
++              if (download_abort_callback_function(media))
++                  break;
++              sleep(1);
++              tries_left--;
++          }
++      } while (ret < 0 && media->download_data.run && !download_abort_callback_function(media) && tries_left > 0);
++
++      if (opts)
++          av_dict_free(&opts);
++
++      if(ret < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to start downloading url:%s, ret:%d\n", url, ret);
++      } else {
++          buffer_size = ffurl_size(puc);
++          if (buffer_size > -1)
++              buffer = av_mallocz(buffer_size+AV_INPUT_BUFFER_PADDING_SIZE);
++          if(!buffer)
++              av_log(NULL, AV_LOG_DEBUG, "hds Failed to allocate %d bytes buffer\n", buffer_size);
++      }
++
++      if(buffer && (ret = ffurl_read_complete(puc, buffer, buffer_size)) < 0) {
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to download fragment, ret: %d\n", ret);
++          av_freep(&buffer);
++      }
++
++      pthread_mutex_lock(&media->download_data.mutex);
++      if (media->download_data.abort || !buffer) {
++          media->download_data.abort = 0;
++      } else {
++          //av_log(NULL, AV_LOG_DEBUG, "%s %d: downloaded  %s\n", __FILE__,__LINE__, media->download_data.url);
++          if (media->download_data.cookies)
++              av_freep(&media->download_data.cookies);
++          av_opt_get(puc->priv_data, "cookies", 0, (uint8_t **) &media->download_data.cookies);
++          if (media->download_data.cookies && !strlen(media->download_data.cookies))
++              av_freep(&media->download_data.cookies);
++          media->download_data.buffer = buffer;
++          media->download_data.buflen = buffer_size;
++      }
++      sem_post(media->download_data.to_caller);       // confirm download
++      pthread_mutex_unlock(&media->download_data.mutex);
++      if (puc)
++          ffurl_close(puc);
++    }
++    pthread_exit(NULL);
++}
++
++static void download_thread_start(AVFormatContext *s, HDSMedia *media)
++{
++#if defined(__APPLE__)
++    char buf[40];
++#endif
++    pthread_mutex_init(&media->download_data.mutex, NULL);
++#if defined(__APPLE__) // no unnamed semaphores on darwin
++    snprintf(buf, sizeof(buf), "sem_to_thread%d", media->media_index);
++    media->download_data.to_thread = sem_open(buf, O_CREAT, 0644, 0);
++    snprintf(buf, sizeof(buf), "sem_to_caller%d", media->media_index);
++    media->download_data.to_caller = sem_open(buf, O_CREAT, 0644, 0);
++#else
++    sem_init(&media->download_data._to_thread, 0, 0);
++    sem_init(&media->download_data._to_caller, 0, 0);
++    media->download_data.to_thread = &media->download_data._to_thread;
++    media->download_data.to_caller = &media->download_data._to_caller;
++#endif
++    media->download_data.thread = 0;
++    media->download_data.run = 1;
++    media->download_data.abort = 0;
++    media->download_data.url = NULL;
++    media->download_data.buffer = NULL;
++    media->download_data.buflen = 0;
++    media->download_data.cookies = NULL;
++    media->download_data.interrupt_callback = &s->interrupt_callback;
++    media->download_data.download_abort_callback.callback = download_abort_callback_function;
++    media->download_data.download_abort_callback.opaque = media;
++    media->download_data.abort_callback = &media->download_data.download_abort_callback;
++    if (pthread_create(&media->download_data.thread, NULL, download_thread, media))
++      av_log(NULL, AV_LOG_ERROR, "hds: creating download thread failed\n");
++}
++
++static void download_thread_stop(HDSMedia *media)
++{
++    if (media->download_data.thread) {
++#if defined(__APPLE__)
++      char buf[40];
++#endif
++      media->download_data.run = 0;
++      media->download_data.abort = 1;
++      sem_post(media->download_data.to_thread);
++      pthread_join(media->download_data.thread, NULL);
++      media->download_data.thread = 0;
++      if (media->download_data.url)
++              av_freep(&media->download_data.url);
++      if (media->download_data.cookies)
++              av_freep(&media->download_data.cookies);
++      if (media->download_data.buffer)
++              av_freep(&media->download_data.buffer);
++      media->download_data.buflen = 0;
++#if defined(__APPLE__)
++      sem_close(media->download_data.to_thread);
++      sem_close(media->download_data.to_caller);
++      snprintf(buf, sizeof(buf), "sem_to_thread%d", media->media_index);
++      sem_unlink(buf);
++      snprintf(buf, sizeof(buf), "sem_to_caller%d", media->media_index);
++      sem_unlink(buf);
++#else
++      sem_destroy(&media->download_data._to_thread);
++      sem_destroy(&media->download_data._to_caller);
++#endif
++      pthread_mutex_destroy(&media->download_data.mutex);
++    }
++}
++
++static int download_fragment(AVFormatContext *s, HDSMedia *media, uint8_t **buffer_out, int *buffer_size_out)
++{
++    HDSContext *c = s->priv_data;
++    char url[MAX_URL_SIZE];
++    unsigned int segment, fragment;
++    int ret;
++
++    if((ret = get_segment_fragment(c->is_live, media, &segment, &fragment)) < 0)
++      return ret;
++    memset(url, 0x00, sizeof(url));
++    if(!av_stristr(media->url, "?") && av_stristr(s->filename, "?"))
++      construct_fragment_url(c->base_url, media->url, segment, fragment, av_stristr(s->filename, "?"), url, MAX_URL_SIZE);
++    else
++      construct_fragment_url(c->base_url, media->url, segment, fragment, "", url, MAX_URL_SIZE);
++
++    pthread_mutex_lock(&media->download_data.mutex);
++
++    if (!media->download_data.cookies)
++      media->download_data.cookies = av_strdup(c->cookies);
++    if (media->download_data.url && strcmp(media->download_data.url, url)) {
++      // download in progress or finished, but not the wanted one. abort it.
++      media->download_data.abort = 1; // initiate abort
++      pthread_mutex_unlock(&media->download_data.mutex);
++      sem_wait(media->download_data.to_caller); // wait until current transfer has finished
++      pthread_mutex_lock(&media->download_data.mutex);
++      av_freep(&media->download_data.url);
++    }
++    if (!media->download_data.url) {
++      // queue retrieval of wanted url
++      media->download_data.url = av_strdup(url);
++      sem_post(media->download_data.to_thread);       // initiate download
++    }
++    if (media->download_data.url && !strcmp(media->download_data.url, url)) {
++      // download matches what we want
++      pthread_mutex_unlock(&media->download_data.mutex);
++      sem_wait(media->download_data.to_caller); // wait until finished
++      pthread_mutex_lock(&media->download_data.mutex);
++      av_freep(&media->download_data.url);
++      if (media->download_data.buffer) {
++          *buffer_out = media->download_data.buffer;
++          *buffer_size_out = media->download_data.buflen;
++          media->download_data.buffer = NULL;
++          media->download_data.buflen = 0;
++          ret = 0;
++          media->nb_fragments_read++;
++          //initiate retrieval of next url
++          if(!get_segment_fragment(c->is_live, media, &segment, &fragment))
++              memset(url, 0x00, sizeof(url));
++              if(!av_stristr(media->url, "?") && av_stristr(s->filename, "?"))
++                  construct_fragment_url(c->base_url, media->url, segment, fragment, av_stristr(s->filename, "?"), url, MAX_URL_SIZE);
++              else
++                  construct_fragment_url(c->base_url, media->url, segment, fragment, "", url, MAX_URL_SIZE);
++              media->download_data.url = av_strdup(url);
++              sem_post(media->download_data.to_thread);       // initiate download
++      } else {
++          // finished but failed
++          ret = AVERROR(EIO);
++      }
++    }
++    pthread_mutex_unlock(&media->download_data.mutex);
++    return ret;
++}
++
++static int get_next_fragment(AVFormatContext *s, HDSMedia *media)
++{
++    F4FBox box;
++    uint8_t *buffer = NULL;
++    int buffer_size = 0, ret;
++
++    if (ff_check_interrupt(media->download_data.interrupt_callback))
++      return AVERROR(EIO);
++
++    if((ret = download_fragment(s, media, &buffer, &buffer_size)) < 0)
++      return ret;
++
++    memset(&box, 0x00, sizeof(F4FBox));
++    if((ret = ff_parse_f4f_box(buffer, buffer_size, &box)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to parse bootstrap box, ret: %d\n", ret);
++      av_free(buffer);
++      ff_free_f4f_box(&box);
++      return ret;
++    }
++    av_free(buffer);
++
++    if((ret = ff_decode_flv_body(box.mdat.data, box.mdat.size, media->samples, &media->nb_samples)) < 0) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to decode FLV body, ret: %d\n", ret);
++      ff_free_f4f_box(&box);
++      return ret;
++    }
++
++    ff_free_f4f_box(&box);
++
++    return 0;
++}
++
++static void read_next_sample(HDSMedia *media, AVPacket *pkt)
++{
++    FLVMediaSample *sample;
++
++    sample = media->samples[media->sample_index];
++    media->sample_index++;
++
++    av_new_packet(pkt, sample->data_size);
++    memcpy(pkt->data, sample->data, sample->data_size);
++
++    pkt->dts = sample->dts;
++    pkt->pts = sample->pts;
++
++    if(sample->type == AVMEDIA_TYPE_VIDEO && media->video_stream)
++      pkt->stream_index = media->video_stream->index;
++    else if(sample->type == AVMEDIA_TYPE_AUDIO && media->audio_stream)
++      pkt->stream_index = media->audio_stream->index;
++}
++
++static void clear_samples(HDSMedia *media)
++{
++    FLVMediaSample *sample;
++    int i;
++
++    for(i = 0; i < media->nb_samples; i++) {
++      sample = media->samples[i];
++      av_freep(&sample->data);
++      av_freep(&sample);
++      media->samples[i] = NULL;
++    }
++
++    media->nb_samples = 0;
++    media->sample_index = 0;
++}
++
++static int get_next_packet(AVFormatContext *s, HDSMedia *media, AVPacket *pkt)
++{
++    HDSContext *c = s->priv_data;
++    int ret;
++
++    if (c->is_live && !media->nb_fragments_read) {
++      int64_t ts = media->bootstrap_info->box.abst.current_media_time;
++      media->nb_fragments_read = get_fragment_offset(media, ts);
++      if (media->nb_fragments_read > 1)
++              media->nb_fragments_read--;
++    }
++
++    if (c->seek_timestamp != AV_NOPTS_VALUE) {
++      int64_t ts = c->seek_timestamp;
++      c->seek_timestamp = AV_NOPTS_VALUE;
++      media->nb_fragments_read = get_fragment_offset(media, ts);
++      clear_samples(media);
++    }
++
++    if(media->nb_samples == 0) {
++      if((ret = get_next_fragment(s, media)) < 0) {
++          return ret;
++      }
++    }
++
++    if(media->nb_samples > 0) {
++      read_next_sample(media, pkt);
++    }
++
++    if(media->sample_index >= media->nb_samples) {
++      clear_samples(media);
++    }
++
++    return 0;
++}
++
++static int hds_read_packet(AVFormatContext *s, AVPacket *pkt)
++{
++    HDSContext *c = s->priv_data;
++    HDSMedia *media = NULL;
++    int i, ret;
++
++    for (i = c->last_media_index + 1; !media && i < c->nb_media; i++) {
++      media = c->media[i];
++      if ((!media->video_stream || (media->video_stream->discard == AVDISCARD_ALL))
++       && (!media->audio_stream || (media->audio_stream->discard == AVDISCARD_ALL)))
++          media = NULL;
++    }
++    for (i = 0; !media && i < c->nb_media; i++) {
++      media = c->media[i];
++      if ((!media->video_stream || (media->video_stream->discard == AVDISCARD_ALL))
++       && (!media->audio_stream || (media->audio_stream->discard == AVDISCARD_ALL)))
++          media = NULL;
++    }
++    c->last_media_index = i;
++
++    if (media && !media->bootstrap_info && c->nb_bootstraps)
++        media->bootstrap_info = c->bootstrap_info[0];
++
++    if (!media || !media->bootstrap_info) {
++      av_log(NULL, AV_LOG_ERROR, "hds Failed to find valid stream\n");
++      return AVERROR(EIO);
++    }
++
++    if((ret = get_next_packet(s, media, pkt)) < 0) {
++      if(ret != AVERROR_EOF)
++          av_log(NULL, AV_LOG_ERROR, "hds Failed to get next packet, ret: %d\n", ret);
++      return ret;
++    }
++
++    return 0;
++}
++
++static int hds_close(AVFormatContext *s)
++{
++    HDSContext *c = s->priv_data;
++    HDSBootstrapInfo *bootstrap_info;
++    HDSMedia *media;
++    int i;
++
++    for(i = 0; i < c->nb_media; i++)
++      download_thread_stop(c->media[i]);
++
++    for(i = 0; i < c->nb_bootstraps; i++) {
++      bootstrap_info = c->bootstrap_info[i];
++
++      ff_free_f4f_box(&bootstrap_info->box);
++      av_freep(&bootstrap_info);
++    }
++
++    for(i = 0; i < c->nb_media; i++) {
++      media = c->media[i];
++
++      if (media->download_data.buffer)
++          av_freep(&media->download_data.buffer);
++
++      clear_samples(media);
++      av_freep(&media);
++    }
++
++    if(c->cookies)
++      av_freep(&c->cookies);
++
++    memset(c, 0x00, sizeof(HDSContext));
++
++    return 0;
++}
++
++static int hds_probe(AVProbeData *p)
++{
++    if(p->filename && av_stristr(p->filename, ".f4m"))
++      return AVPROBE_SCORE_MAX;
++    return 0;
++}
++
++static int hds_read_seek(AVFormatContext *s, int stream_index,
++                             int64_t timestamp, int flags)
++{
++    HDSContext *c = s->priv_data;
++
++    if (flags & AVSEEK_FLAG_BYTE)
++      return AVERROR(ENOSYS);
++    if (s->duration < c->seek_timestamp) {
++      c->seek_timestamp = AV_NOPTS_VALUE;
++      return AVERROR(EIO);
++    }
++    c->seek_timestamp = timestamp;
++
++    if (c->is_live) {
++      int i;
++      for(i = 0; i < c->nb_media; i++)
++          c->media[i]->nb_fragments_read = 0;
++      c->seek_timestamp = AV_NOPTS_VALUE;
++      return 0;
++    }
++
++    return 0;
++}
++
++AVInputFormat ff_hds_demuxer = {
++    .name        = "hds",
++    .long_name      = NULL_IF_CONFIG_SMALL("Adobe HTTP Dynamic Streaming Demuxer"),
++    .priv_data_size = sizeof(HDSContext),
++    .read_probe     = hds_probe,
++    .read_header    = hds_read_header,
++    .read_packet    = hds_read_packet,
++    .read_close     = hds_close,
++    .read_seek      = hds_read_seek,
++};
+--- a/libavformat/Makefile     2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/Makefile     2023-02-14 17:27:17.200274887 +0100
+@@ -200,6 +200,7 @@ OBJS-$(CONFIG_H263_DEMUXER)
+ OBJS-$(CONFIG_H263_MUXER)                += rawenc.o
+ OBJS-$(CONFIG_H264_DEMUXER)              += h264dec.o rawdec.o
+ OBJS-$(CONFIG_H264_MUXER)                += rawenc.o
++OBJS-$(CONFIG_HDS_DEMUXER)               += hdsdec.o amfmetadata.o f4mmanifest.o f4fbox.o flvtag.o
+ OBJS-$(CONFIG_HASH_MUXER)                += hashenc.o
+ OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
+ OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0002-ffmpeg-aac-3.x.patch b/archive-patches/ffmpeg-cst/3.3.9/0002-ffmpeg-aac-3.x.patch
new file mode 100644 (file)
index 0000000..54b837d
--- /dev/null
@@ -0,0 +1,53 @@
+--- a/libavcodec/aacdec_template.c     2018-11-18 01:57:59.000000000 +0100
++++ b/libavcodec/aacdec_template.c     2023-02-14 17:54:57.904729510 +0100
+@@ -2414,7 +2414,7 @@ static int decode_extension_payload(AACC
+  * @param   decode  1 if tool is used normally, 0 if tool is used in LTP.
+  * @param   coef    spectral coefficients
+  */
+-static void apply_tns(INTFLOAT coef_param[1024], TemporalNoiseShaping *tns,
++static __attribute__((optimize(0))) void apply_tns(INTFLOAT coef_param[1024], TemporalNoiseShaping *tns,
+                       IndividualChannelStream *ics, int decode)
+ {
+     const int mmm = FFMIN(ics->tns_max_bands, ics->max_sfb);
+--- a/libavcodec/aacps.c       2018-11-18 01:57:59.000000000 +0100
++++ b/libavcodec/aacps.c       2023-02-14 17:52:02.998962048 +0100
+@@ -654,7 +654,7 @@ static void map_val_20_to_34(INTFLOAT pa
+     par[ 1] = AAC_HALF_SUM(par[ 0], par[ 1]);
+ }
+-static void decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const INTFLOAT (*s)[32][2], int is34)
++static void __attribute__((optimize(0))) decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const INTFLOAT (*s)[32][2], int is34)
+ {
+     LOCAL_ALIGNED_16(INTFLOAT, power, [34], [PS_QMF_TIME_SLOTS]);
+     LOCAL_ALIGNED_16(INTFLOAT, transient_gain, [34], [PS_QMF_TIME_SLOTS]);
+--- a/libavcodec/fft_template.c        2018-11-18 01:57:59.000000000 +0100
++++ b/libavcodec/fft_template.c        2023-02-14 17:52:02.998962048 +0100
+@@ -475,7 +475,7 @@ static void fft##n(FFTComplex *z)\
+     pass(z,FFT_NAME(ff_cos_##n),n4/2);\
+ }
+-static void fft4(FFTComplex *z)
++static void __attribute__((optimize(0))) fft4(FFTComplex *z)
+ {
+     FFTDouble t1, t2, t3, t4, t5, t6, t7, t8;
+@@ -489,7 +489,7 @@ static void fft4(FFTComplex *z)
+     BF(z[2].im, z[0].im, t2, t5);
+ }
+-static void fft8(FFTComplex *z)
++static void __attribute__((optimize(0))) fft8(FFTComplex *z)
+ {
+     FFTDouble t1, t2, t3, t4, t5, t6;
+--- a/libavcodec/mdct_template.c       2018-07-17 11:27:40.000000000 +0200
++++ b/libavcodec/mdct_template.c       2023-02-14 17:52:02.998962048 +0100
+@@ -102,7 +102,7 @@ av_cold int ff_mdct_init(FFTContext *s,
+  * @param output N/2 samples
+  * @param input N/2 samples
+  */
+-void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input)
++void __attribute__((optimize(0))) ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input)
+ {
+     int k, n8, n4, n2, n, j;
+     const uint16_t *revtab = s->revtab;
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0003-ffmpeg-increase-IO-BUFFER-SIZE-to-256k.patch b/archive-patches/ffmpeg-cst/3.3.9/0003-ffmpeg-increase-IO-BUFFER-SIZE-to-256k.patch
new file mode 100644 (file)
index 0000000..dbd6160
--- /dev/null
@@ -0,0 +1,17 @@
+diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
+index c2681ed..711fff8 100644
+--- a/libavformat/aviobuf.c
++++ b/libavformat/aviobuf.c
+@@ -33,7 +33,7 @@
+ #include "url.h"
+ #include <stdarg.h>
+-#define IO_BUFFER_SIZE 32768
++#define IO_BUFFER_SIZE 262144
+ /**
+  * Do seeks within this distance ahead of the current buffer by skipping
+-- 
+2.7.4.1.g5468f9e
+
+
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0004-ffmpeg-revert-libavformat-aviobuf-keep-track-of-the-original-buffer-size.patch b/archive-patches/ffmpeg-cst/3.3.9/0004-ffmpeg-revert-libavformat-aviobuf-keep-track-of-the-original-buffer-size.patch
new file mode 100644 (file)
index 0000000..0ab74bb
--- /dev/null
@@ -0,0 +1,55 @@
+--- a/libavformat/aviobuf.c    2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/aviobuf.c    2023-02-14 18:10:53.082399288 +0100
+@@ -88,7 +88,6 @@ int ffio_init_context(AVIOContext *s,
+                   int64_t (*seek)(void *opaque, int64_t offset, int whence))
+ {
+     s->buffer      = buffer;
+-    s->orig_buffer_size =
+     s->buffer_size = buffer_size;
+     s->buf_ptr     = buffer;
+     s->opaque      = opaque;
+@@ -536,16 +535,16 @@ static void fill_buffer(AVIOContext *s)
+     }
+     /* make buffer smaller in case it ended up large after probing */
+-    if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size) {
++      if (s->read_packet && s->buffer_size > max_buffer_size) {
+         if (dst == s->buffer) {
+-            int ret = ffio_set_buf_size(s, s->orig_buffer_size);
++                      int ret = ffio_set_buf_size(s, max_buffer_size);
+             if (ret < 0)
+                 av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");
+             s->checksum_ptr = dst = s->buffer;
+         }
+-        av_assert0(len >= s->orig_buffer_size);
+-        len = s->orig_buffer_size;
++        av_assert0(len >= max_buffer_size);
++        len = max_buffer_size;
+     }
+     if (s->read_packet)
+@@ -987,7 +986,6 @@ int ffio_set_buf_size(AVIOContext *s, in
+     av_free(s->buffer);
+     s->buffer = buffer;
+-    s->orig_buffer_size =
+     s->buffer_size = buf_size;
+     s->buf_ptr = buffer;
+     url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
+--- a/libavformat/avio.h       2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/avio.h       2023-02-14 18:05:28.759082028 +0100
+@@ -282,13 +282,6 @@ typedef struct AVIOContext {
+     int writeout_count;
+     /**
+-     * Original buffer size
+-     * used internally after probing and ensure seekback to reset the buffer size
+-     * This field is internal to libavformat and access from outside is not allowed.
+-     */
+-    int orig_buffer_size;
+-
+-    /**
+      * Threshold to favor readahead over seek.
+      * This is current internal only, do not use from outside.
+      */
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0005-ffmpeg-devel-v14-avformat-dashdec-add-dash-demuxer-base-version.patch b/archive-patches/ffmpeg-cst/3.3.9/0005-ffmpeg-devel-v14-avformat-dashdec-add-dash-demuxer-base-version.patch
new file mode 100755 (executable)
index 0000000..46199c8
--- /dev/null
@@ -0,0 +1,1902 @@
+--- a/configure        2018-11-18 01:57:59.000000000 +0100
++++ b/configure        2023-02-14 20:19:47.770726018 +0100
+@@ -274,6 +274,7 @@
+   --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]
+@@ -1581,6 +1582,7 @@
+     libvpx
+     libwavpack
+     libwebp
++    libxml2
+     libzimg
+     libzmq
+     libzvbi
+@@ -2916,6 +2918,7 @@
+ 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"
+@@ -5924,6 +5927,7 @@
+                                check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||
+                                die "ERROR: openssl not found"; }
+ enabled qtkit_indev      && { check_header_objcc QTKit/QTKit.h || disable qtkit_indev; }
++enabled libxml2             && require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion
+ # libdc1394 check
+ if enabled libdc1394; then
+--- a/libavformat/allformats.c 2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/allformats.c 2023-02-14 20:19:47.774726060 +0100
+@@ -96,7 +96,7 @@
+     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);
+--- a/libavformat/dashdec.c    1970-01-01 01:00:00.000000000 +0100
++++ b/libavformat/dashdec.c    2023-02-14 20:19:47.774726060 +0100
+@@ -0,0 +1,1801 @@
++/*
++ * 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/avstring.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;
++    enum DASHTmplUrlType tmp_url_type;
++    AVIOContext pb;
++    AVIOContext *input;
++    AVFormatContext *parent;
++    AVFormatContext *ctx;
++    AVPacket pkt;
++    int rep_idx;
++    int rep_count;
++    int stream_index;
++
++    enum AVMediaType type;
++    int64_t target_duration;
++
++    int n_fragments;
++    struct fragment **fragments; /* VOD list of fragment for profile */
++
++    int n_timelines;
++    struct timeline **timelines;
++
++    int64_t first_seq_no;
++    int64_t last_seq_no;
++
++    int64_t fragment_duration;
++    int64_t fragment_timescale;
++
++    int64_t cur_seq_no;
++    int64_t cur_seg_offset;
++    int64_t cur_seg_size;
++    struct fragment *cur_seg;
++
++    /* Currently active Media Initialization Section */
++    struct fragment *init_section;
++    uint8_t *init_sec_buf;
++    uint32_t init_sec_buf_size;
++    uint32_t init_sec_data_len;
++    uint32_t init_sec_buf_read_offset;
++    int fix_multiple_stsd_order;
++    int64_t cur_timestamp;
++};
++
++typedef struct DASHContext {
++    const AVClass *class;
++    char *base_url;
++    struct representation *cur_video;
++    struct representation *cur_audio;
++    uint64_t media_presentation_duration_sec;
++    uint64_t suggested_presentation_delay_sec;
++    uint64_t presentation_delay_sec;
++    uint64_t availability_start_time_sec;
++    uint64_t publish_time_sec;
++    uint64_t minimum_update_period_sec;
++    uint64_t time_shift_buffer_depth_sec;
++    uint64_t min_buffer_time_sec;
++    uint64_t period_duration_sec;
++    uint64_t period_start_sec;
++    int is_live;
++    AVIOInterruptCB *interrupt_callback;
++    char *user_agent;                    ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
++    char *cookies;                       ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
++    char *headers;                       ///< holds HTTP headers set as an AVOption to the HTTP protocol context
++    AVDictionary *avio_opts;
++} DASHContext;
++
++static uint64_t 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 void free_fragment(struct fragment **seg)
++{
++    if (!(*seg)) {
++        return;
++    }
++    av_freep(&(*seg)->url);
++    av_freep(seg);
++}
++
++static void free_fragment_list(struct representation *pls)
++{
++    int i;
++
++    for (i = 0; i < pls->n_fragments; i++) {
++        free_fragment(&pls->fragments[i]);
++    }
++    av_freep(&pls->fragments);
++    pls->n_fragments = 0;
++}
++
++static void free_timelines_list(struct representation *pls)
++{
++    int i;
++
++    for (i = 0; i < pls->n_timelines; i++) {
++        av_freep(&pls->timelines[i]);
++    }
++    av_freep(&pls->timelines);
++    pls->n_timelines = 0;
++}
++
++/*
++ * Used to reset a statically allocated AVPacket to a clean slate,
++ * containing no data.
++ */
++static void reset_packet(AVPacket *pkt)
++{
++    av_init_packet(pkt);
++    pkt->data = NULL;
++}
++
++static void free_representation(struct representation *pls)
++{
++    free_fragment_list(pls);
++    free_timelines_list(pls);
++    free_fragment(&pls->cur_seg);
++    free_fragment(&pls->init_section);
++    av_freep(&pls->init_sec_buf);
++    av_packet_unref(&pls->pkt);
++    reset_packet(&pls->pkt);
++    av_freep(&pls->pb.buffer);
++    if (pls->input)
++        ff_format_io_close(pls->parent, &pls->input);
++    if (pls->ctx) {
++        pls->ctx->pb = NULL;
++        avformat_close_input(&pls->ctx);
++    }
++
++    av_free(pls->url_template);
++    av_free(pls);
++}
++
++static void update_options(char **dest, const char *name, void *src)
++{
++    av_freep(dest);
++    av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
++    if (*dest)
++        av_freep(dest);
++}
++
++static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
++                    AVDictionary *opts, AVDictionary *opts2, int *is_http)
++{
++    DASHContext *c = s->priv_data;
++    AVDictionary *tmp = NULL;
++    const char *proto_name = NULL;
++    int ret;
++    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 *replace_template_str(const char *url, const char *marker)
++{
++    char *prefix = NULL;
++    char *start = 0;
++    char *end = NULL;
++    char *tmp_url = NULL;
++    int marker_len;
++
++    prefix = av_strdup(marker);
++    marker_len = strlen(prefix) - 1;
++    prefix[marker_len] = '\0';
++
++    start = av_stristr(url, prefix);
++    if (!start)
++        goto finish;
++    end = strchr(start + 1, '$');
++    if (!end)
++        goto finish;
++
++    tmp_url = av_mallocz(MAX_URL_SIZE);
++    if (!tmp_url) {
++        return NULL;
++    }
++
++    av_strlcpy(tmp_url, url, start - url+ 1);
++    av_strlcat(tmp_url, start + marker_len, strlen(tmp_url) + end - start - marker_len + 1);
++    av_strlcat(tmp_url, end + 1, MAX_URL_SIZE);
++
++finish:
++    av_free(prefix);
++    return tmp_url;
++}
++
++static char *get_content_url(xmlNodePtr *baseurl_nodes,
++                             int n_baseurl_nodes,
++                             xmlChar *rep_id_val,
++                             xmlChar *rep_bandwidth_val,
++                             xmlChar *val)
++{
++    int i;
++    xmlChar *text;
++    char *url = NULL;
++    char *tmp_str = av_mallocz(MAX_URL_SIZE);
++    char *tmp_str_2 = NULL;
++
++    if (!tmp_str) {
++        return NULL;
++    }
++    for (i = 0; i < n_baseurl_nodes; ++i) {
++        if (baseurl_nodes[i] &&
++            baseurl_nodes[i]->children &&
++            baseurl_nodes[i]->children->type == XML_TEXT_NODE) {
++            text = xmlNodeGetContent(baseurl_nodes[i]->children);
++            if (text) {
++                tmp_str_2 = av_mallocz(MAX_URL_SIZE);
++                if (!tmp_str_2) {
++                    av_free(tmp_str);
++                    return NULL;
++                }
++                ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str, text);
++                av_free(tmp_str);
++                tmp_str = tmp_str_2;
++                xmlFree(text);
++            }
++        }
++    }
++    if (val)
++        av_strlcat(tmp_str, (const char*)val, MAX_URL_SIZE);
++
++    if (rep_id_val) {
++        url = 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 void check_full_number(struct representation *rep)
++{
++    char *temp_string = NULL;
++
++    if (av_stristr(rep->url_template, "$Number$")) {
++        temp_string = av_strireplace(rep->url_template, "$Number$", "$Number%d$");
++        av_free(rep->url_template);
++        rep->url_template = temp_string;
++    }
++}
++
++static enum AVMediaType get_content_type(xmlNodePtr node)
++{
++    enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
++    int i = 0;
++    const char *attr;
++
++    if (node) {
++        while (type == AVMEDIA_TYPE_UNKNOWN && i < 2) {
++            attr = (i) ? "mimeType" : "contentType";
++            xmlChar *val = xmlGetProp(node, attr);
++            if (val) {
++                if (av_stristr((const char *)val, "video")) {
++                    type = AVMEDIA_TYPE_VIDEO;
++                } else if (av_stristr((const char *)val, "audio")) {
++                    type = AVMEDIA_TYPE_AUDIO;
++                }
++                xmlFree(val);
++            }
++            i++;
++        }
++    }
++    return type;
++}
++
++static int parse_manifest_segmenturlnode(AVFormatContext *s, struct representation *rep,
++                                         xmlNodePtr fragmenturl_node,
++                                         xmlNodePtr *baseurl_nodes,
++                                         xmlChar *rep_id_val,
++                                         xmlChar *rep_bandwidth_val)
++{
++    xmlChar *initialization_val = NULL;
++    xmlChar *media_val = NULL;
++
++    if (!xmlStrcmp(fragmenturl_node->name, (const xmlChar *)"Initialization")) {
++        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
++        if (initialization_val) {
++            rep->init_section = av_mallocz(sizeof(struct fragment));
++            if (!rep->init_section) {
++                xmlFree(initialization_val);
++                return AVERROR(ENOMEM);
++            }
++            rep->init_section->url = get_content_url(baseurl_nodes, 4,
++                                                     rep_id_val,
++                                                     rep_bandwidth_val,
++                                                     initialization_val);
++            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 *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");
++        reset_packet(&rep->pkt);
++
++        baseurl_nodes[0] = mpd_baseurl_node;
++        baseurl_nodes[1] = period_baseurl_node;
++        baseurl_nodes[2] = adaptionset_baseurl_node;
++        baseurl_nodes[3] = representation_baseurl_node;
++
++        if (representation_segmenttemplate_node || fragment_template_node) {
++            fragment_timeline_node = NULL;
++            fragment_templates_tab[0] = representation_segmenttemplate_node;
++            fragment_templates_tab[1] = fragment_template_node;
++
++            duration_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "duration");
++            startnumber_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "startNumber");
++            timescale_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "timescale");
++            initialization_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "initialization");
++            media_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "media");
++
++            if (initialization_val) {
++                rep->init_section = av_mallocz(sizeof(struct fragment));
++                if (!rep->init_section) {
++                    av_free(rep);
++                    ret = AVERROR(ENOMEM);
++                    goto end;
++                }
++                rep->init_section->url = get_content_url(baseurl_nodes, 4,
++                                                         rep_id_val,
++                                                         rep_bandwidth_val,
++                                                         initialization_val);
++                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")) {
++                        rep->tmp_url_type = TMP_URL_TYPE_NUMBER;  /* Number-Based. */
++                        check_full_number(rep);
++                    } else if (av_stristr(temp_string, "$Time")) {
++                        rep->tmp_url_type = TMP_URL_TYPE_TIME; /* Time-Based. */
++                    } else {
++                        temp_string = NULL;
++                    }
++                }
++                xmlFree(media_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_baseurl_node && representation_segmentlist_node) {
++            // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
++            // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
++            xmlNodePtr fragmenturl_node = NULL;
++            duration_val = xmlGetProp(representation_segmentlist_node, "duration");
++            timescale_val = xmlGetProp(representation_segmentlist_node, "timescale");
++            if (duration_val) {
++                rep->fragment_duration = (int64_t) 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);
++            }
++        } else {
++            free_representation(rep);
++            rep = NULL;
++            av_log(s, AV_LOG_ERROR, "Unknown format of Representation node id[%s] \n", (const char *)rep_id_val);
++        }
++
++        if (rep) {
++            if (rep->fragment_duration > 0 && !rep->fragment_timescale)
++                rep->fragment_timescale = 1;
++            if (type == AVMEDIA_TYPE_VIDEO) {
++                rep->rep_idx = video_rep_idx;
++                c->cur_video = rep;
++            } else {
++                rep->rep_idx = audio_rep_idx;
++                c->cur_audio = rep;
++            }
++        }
++    }
++
++    video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
++    audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
++
++end:
++    if (rep_id_val)
++        xmlFree(rep_id_val);
++    if (rep_bandwidth_val)
++        xmlFree(rep_bandwidth_val);
++
++    return ret;
++}
++
++static int parse_manifest_adaptationset(AVFormatContext *s, const char *url,
++                                        xmlNodePtr adaptionset_node,
++                                        xmlNodePtr mpd_baseurl_node,
++                                        xmlNodePtr period_baseurl_node)
++{
++    int ret = 0;
++    xmlNodePtr fragment_template_node = NULL;
++    xmlNodePtr content_component_node = NULL;
++    xmlNodePtr adaptionset_baseurl_node = NULL;
++    xmlNodePtr node = NULL;
++
++    node = xmlFirstElementChild(adaptionset_node);
++    while (node) {
++        if (!xmlStrcmp(node->name, (const xmlChar *)"SegmentTemplate")) {
++            fragment_template_node = node;
++        } else if (!xmlStrcmp(node->name, (const xmlChar *)"ContentComponent")) {
++            content_component_node = node;
++        } else if (!xmlStrcmp(node->name, (const xmlChar *)"BaseURL")) {
++            adaptionset_baseurl_node = node;
++        } else if (!xmlStrcmp(node->name, (const xmlChar *)"Representation")) {
++            ret = parse_manifest_representation(s, url, node,
++                                                adaptionset_node,
++                                                mpd_baseurl_node,
++                                                period_baseurl_node,
++                                                fragment_template_node,
++                                                content_component_node,
++                                                adaptionset_baseurl_node);
++            if (ret < 0) {
++                return ret;
++            }
++        }
++        node = xmlNextElementSibling(node);
++    }
++    return 0;
++}
++
++
++static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
++{
++    DASHContext *c = s->priv_data;
++    int ret = 0;
++    int close_in = 0;
++    uint8_t *new_url = NULL;
++    int64_t filesize = 0;
++    char *buffer = NULL;
++    AVDictionary *opts = NULL;
++    xmlDoc *doc = NULL;
++    xmlNodePtr root_element = NULL;
++    xmlNodePtr node = NULL;
++    xmlNodePtr period_node = NULL;
++    xmlNodePtr mpd_baseurl_node = NULL;
++    xmlNodePtr period_baseurl_node = NULL;
++    xmlNodePtr adaptionset_node = NULL;
++    xmlAttrPtr attr = NULL;
++    xmlChar *val  = NULL;
++    uint32_t perdiod_duration_sec = 0;
++    uint32_t perdiod_start_sec = 0;
++    int32_t audio_rep_idx = 0;
++    int32_t video_rep_idx = 0;
++
++    if (!in) {
++        close_in = 1;
++        /* This is XML manifest there is no need to set range header */
++        av_dict_set(&opts, "seekable", "0", 0);
++        // broker prior HTTP options that should be consistent across requests
++        av_dict_set(&opts, "user-agent", c->user_agent, 0);
++        av_dict_set(&opts, "cookies", c->cookies, 0);
++        av_dict_set(&opts, "headers", c->headers, 0);
++
++        ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
++        av_dict_free(&opts);
++        if (ret < 0)
++            return ret;
++    }
++
++    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) {
++        c->base_url = av_strdup(new_url);
++    } else {
++        c->base_url = av_strdup(url);
++    }
++
++    filesize = avio_size(in);
++    if (filesize <= 0) {
++        filesize = 8 * 1024;
++    }
++
++    buffer = av_mallocz(filesize);
++    if (!buffer) {
++        return AVERROR(ENOMEM);
++    }
++
++    filesize = avio_read(in, buffer, filesize);
++    if (filesize > 0) {
++        LIBXML_TEST_VERSION
++
++        doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
++        root_element = xmlDocGetRootElement(doc);
++        node = root_element;
++
++        if (!node) {
++            ret = AVERROR_INVALIDDATA;
++            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing root node\n", url);
++            goto cleanup;
++        }
++
++        if (node->type != XML_ELEMENT_NODE ||
++            xmlStrcmp(node->name, (const xmlChar *)"MPD")) {
++            ret = AVERROR_INVALIDDATA;
++            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root node name[%s] type[%d]\n", url, node->name, (int)node->type);
++            goto cleanup;
++        }
++
++        val = xmlGetProp(node, "type");
++        if (!val) {
++            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing type attrib\n", url);
++            ret = AVERROR_INVALIDDATA;
++            goto cleanup;
++        }
++        if (!xmlStrcmp(val, (const xmlChar *)"dynamic"))
++            c->is_live = 1;
++        xmlFree(val);
++
++        attr = node->properties;
++        while (attr) {
++            val = xmlGetProp(node, attr->name);
++
++            if (!xmlStrcmp(attr->name, (const xmlChar *)"availabilityStartTime")) {
++                c->availability_start_time_sec = get_utc_date_time_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"publishTime")) {
++                c->publish_time_sec = get_utc_date_time_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minimumUpdatePeriod")) {
++                c->minimum_update_period_sec = get_duration_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"timeShiftBufferDepth")) {
++                c->time_shift_buffer_depth_sec = get_duration_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"minBufferTime")) {
++                c->min_buffer_time_sec = get_duration_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"suggestedPresentationDelay")) {
++                c->suggested_presentation_delay_sec = get_duration_insec(s, (const char *)val);
++            } else if (!xmlStrcmp(attr->name, (const xmlChar *)"mediaPresentationDuration")) {
++                c->media_presentation_duration_sec = 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_sec)) {
++                    period_node = node;
++                    c->period_duration_sec = perdiod_duration_sec;
++                    c->period_start_sec = perdiod_start_sec;
++                    if (c->period_start_sec > 0)
++                        c->media_presentation_duration_sec = c->period_duration_sec;
++                }
++            }
++            node = xmlNextElementSibling(node);
++        }
++        if (!period_node) {
++            av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing Period node\n", url);
++            ret = AVERROR_INVALIDDATA;
++            goto cleanup;
++        }
++
++        adaptionset_node = xmlFirstElementChild(period_node);
++        while (adaptionset_node) {
++            if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"BaseURL")) {
++                period_baseurl_node = adaptionset_node;
++            } else if (!xmlStrcmp(adaptionset_node->name, (const xmlChar *)"AdaptationSet")) {
++                parse_manifest_adaptationset(s, url, adaptionset_node, mpd_baseurl_node, period_baseurl_node);
++            }
++            adaptionset_node = xmlNextElementSibling(adaptionset_node);
++        }
++        if (c->cur_video) {
++            c->cur_video->rep_count = video_rep_idx;
++            c->cur_video->fix_multiple_stsd_order = 1;
++            av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n", (int)c->cur_video->rep_idx);
++            av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n", (int)video_rep_idx);
++        }
++        if (c->cur_audio) {
++            c->cur_audio->rep_count = audio_rep_idx;
++        }
++
++cleanup:
++        /*free the document */
++        xmlFreeDoc(doc);
++        xmlCleanupParser();
++    } else {
++        av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
++        ret = AVERROR_INVALIDDATA;
++    }
++
++    av_free(new_url);
++    av_free(buffer);
++    if (close_in) {
++        avio_close(in);
++    }
++    return ret;
++}
++
++static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
++{
++    DASHContext *c = pls->parent->priv_data;
++    int64_t num = 0;
++
++    if (c->is_live) {
++        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_sec) - c->presentation_delay_sec) * pls->fragment_timescale) / pls->fragment_duration;
++    } else {
++        num = pls->first_seq_no;
++    }
++    return num;
++}
++
++static int64_t calc_min_seg_no(AVFormatContext *s, struct representation *pls)
++{
++    DASHContext *c = pls->parent->priv_data;
++    int64_t num = 0;
++
++    if (c->is_live) {
++        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_sec) - c->time_shift_buffer_depth_sec) * pls->fragment_timescale)  / pls->fragment_duration;
++    } else {
++        num = pls->first_seq_no;
++    }
++    return num;
++}
++
++static int64_t calc_max_seg_no(AVFormatContext *s, struct representation *pls)
++{
++    DASHContext *c = pls->parent->priv_data;
++    int64_t num = 0;
++    int64_t i = 0;
++
++    if (c->is_live) {
++        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_sec)) * pls->fragment_timescale)  / pls->fragment_duration;
++    } else {
++        if (pls->n_fragments) {
++            num = pls->first_seq_no + pls->n_fragments - 1;
++        } else if (pls->n_timelines) {
++            num = pls->first_seq_no + pls->n_timelines - 1;
++            for (i = 0; i < pls->n_timelines; ++i) {
++                num += pls->timelines[i]->r;
++            }
++        } else {
++            num = pls->first_seq_no + (c->media_presentation_duration_sec * pls->fragment_timescale) / pls->fragment_duration;
++        }
++    }
++
++    return num;
++}
++
++static int64_t get_fragment_start_time(struct representation *pls, int64_t cur_seq_no)
++{
++    int64_t i = 0;
++    int64_t j = 0;
++    int64_t num = 0;
++    int64_t startTime = 0;
++
++    if (pls->n_timelines) {
++        for (i = 0; i < pls->n_timelines; ++i) {
++            if (pls->timelines[i]->t > 0) {
++                startTime = pls->timelines[i]->t;
++            }
++            if (num == cur_seq_no)
++                goto finish;
++            startTime += pls->timelines[i]->d;
++            for (j = 0; j < pls->timelines[i]->r; ++j) {
++                num++;
++                if (num == cur_seq_no)
++                    goto finish;
++                startTime += pls->timelines[i]->d;
++            }
++            num++;
++        }
++    }
++
++finish:
++    return startTime;
++}
++
++static struct fragment *get_current_fragment(struct representation *pls)
++{
++    int64_t tmp_val = 0;
++    int64_t min_seq_no = 0;
++    int64_t max_seq_no = 0;
++    char buffer[1024];
++    char *tmp_str = NULL;
++    struct fragment *seg = NULL;
++    struct fragment *seg_ptr = NULL;
++    DASHContext *c = pls->parent->priv_data;
++
++    if (pls->n_fragments > 0) {
++        if (pls->cur_seq_no < pls->n_fragments) {
++            seg_ptr = pls->fragments[pls->cur_seq_no];
++            seg = av_mallocz(sizeof(struct fragment));
++            if (!seg) {
++                return NULL;
++            }
++            seg->url = av_strdup(seg_ptr->url);
++            if (!seg->url) {
++                av_free(seg);
++                return NULL;
++            }
++            seg->size = seg_ptr->size;
++            seg->url_offset = seg_ptr->url_offset;
++            return seg;
++        }
++    }
++    if (c->is_live) {
++        while (1) {
++            min_seq_no = calc_min_seg_no(pls->parent, pls);
++            max_seq_no = calc_max_seg_no(pls->parent, pls);
++
++            if (pls->cur_seq_no <= min_seq_no) {
++                av_log(pls->parent, AV_LOG_VERBOSE, "old fragment: cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n",
++                       (int64_t)pls->cur_seq_no, min_seq_no, max_seq_no, (int)pls->rep_idx);
++                pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
++            } else if (pls->cur_seq_no > max_seq_no) {
++                av_log(pls->parent, AV_LOG_VERBOSE, "new fragment: min[%"PRId64"] max[%"PRId64"], playlist %d\n",
++                       min_seq_no, max_seq_no, (int)pls->rep_idx);
++                av_usleep(1000);
++                continue;
++            }
++            break;
++        }
++        seg = av_mallocz(sizeof(struct fragment));
++        if (!seg) {
++            return NULL;
++        }
++    } else if (pls->cur_seq_no <= pls->last_seq_no) {
++        seg = av_mallocz(sizeof(struct fragment));
++        if (!seg) {
++            return NULL;
++        }
++    }
++    if (seg) {
++        if (pls->tmp_url_type != TMP_URL_TYPE_UNSPECIFIED) {
++            tmp_val = pls->tmp_url_type == TMP_URL_TYPE_NUMBER ? pls->cur_seq_no : get_fragment_start_time(pls, pls->cur_seq_no);
++            if (av_get_frame_filename(buffer, sizeof(buffer), pls->url_template, (int64_t)tmp_val) < 0) {
++                av_log(pls->parent, AV_LOG_ERROR, "Invalid segment filename template %s\n", pls->url_template);
++                return NULL;
++            }
++
++            if (pls->tmp_url_type == TMP_URL_TYPE_NUMBER) {
++                tmp_str = replace_template_str(buffer, "$Number$");
++            } else if (pls->tmp_url_type == TMP_URL_TYPE_TIME) {
++                tmp_str = replace_template_str(buffer, "$Time$");
++            } else {
++                av_log(pls->parent, AV_LOG_ERROR, "Invalid tmp_url_type\n");
++                return NULL;
++            }
++
++            seg->url = av_strdup(tmp_str);
++            if (!seg->url) {
++                av_free(tmp_str);
++                return NULL;
++            }
++        } else {
++            av_log(pls->parent, AV_LOG_ERROR, "Unable to unable to resolve template url '%s'\n", pls->url_template);
++            seg->url = av_strdup(pls->url_template);
++            if (!seg->url) {
++                return NULL;
++            }
++        }
++        seg->size = -1;
++    }
++
++    av_free(tmp_str);
++    return seg;
++}
++
++enum ReadFromURLMode {
++    READ_NORMAL,
++    READ_COMPLETE,
++};
++
++static int read_from_url(struct representation *pls, struct fragment *seg,
++                         uint8_t *buf, int buf_size,
++                         enum ReadFromURLMode mode)
++{
++    int ret;
++
++    /* limit read if the fragment was only a part of a file */
++    if (seg->size >= 0)
++        buf_size = FFMIN(buf_size, pls->cur_seg_size - pls->cur_seg_offset);
++
++    if (mode == READ_COMPLETE) {
++        ret = avio_read(pls->input, buf, buf_size);
++        if (ret < buf_size) {
++            av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment.\n");
++        }
++    } else {
++        ret = avio_read(pls->input, buf, buf_size);
++    }
++    if (ret > 0)
++        pls->cur_seg_offset += ret;
++
++    return ret;
++}
++
++static int open_input(DASHContext *c, struct representation *pls, struct fragment *seg)
++{
++    AVDictionary *opts = NULL;
++    char url[MAX_URL_SIZE];
++    int ret;
++
++    // broker prior HTTP options that should be consistent across requests
++    av_dict_set(&opts, "user-agent", c->user_agent, 0);
++    av_dict_set(&opts, "cookies", c->cookies, 0);
++    av_dict_set(&opts, "headers", c->headers, 0);
++    if (c->is_live) {
++        av_dict_set(&opts, "seekable", "0", 0);
++    }
++
++    if (seg->size >= 0) {
++        /* try to restrict the HTTP request to the part we want
++         * (if this is in fact a HTTP request) */
++        av_dict_set_int(&opts, "offset", seg->url_offset, 0);
++        av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0);
++    }
++
++    ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
++    av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s', offset %"PRId64", playlist %d\n",
++           url, seg->url_offset, pls->rep_idx);
++    ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts, NULL);
++    if (ret < 0) {
++        goto cleanup;
++    }
++
++    /* Seek to the requested position. If this was a HTTP request, the offset
++     * should already be where want it to, but this allows e.g. local testing
++     * without a HTTP server. */
++    if (!ret && seg->url_offset) {
++        int64_t seekret = avio_seek(pls->input, seg->url_offset, SEEK_SET);
++        if (seekret < 0) {
++            av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of DASH fragment '%s'\n", seg->url_offset, seg->url);
++            ret = (int) seekret;
++            ff_format_io_close(pls->parent, &pls->input);
++        }
++    }
++
++cleanup:
++    av_dict_free(&opts);
++    pls->cur_seg_offset = 0;
++    pls->cur_seg_size = seg->size;
++    return ret;
++}
++
++static int update_init_section(struct representation *pls)
++{
++    static const int max_init_section_size = 1024*1024;
++    DASHContext *c = pls->parent->priv_data;
++    int64_t sec_size = 0;
++    int64_t urlsize = 0;
++    int ret = 0;
++
++    /* read init section only once per representation */
++    if (!pls->init_section || pls->init_sec_buf) {
++        return 0;
++    }
++
++    ret = open_input(c, pls, pls->init_section);
++    if (ret < 0) {
++        av_log(pls->parent, AV_LOG_WARNING, "Failed to open an initialization section in playlist %d\n", pls->rep_idx);
++        return ret;
++    }
++
++    if (pls->init_section->size >= 0) {
++        sec_size = pls->init_section->size;
++    } else if ((urlsize = avio_size(pls->input)) >= 0) {
++        sec_size = urlsize;
++    } else {
++        sec_size = max_init_section_size;
++    }
++    av_log(pls->parent, AV_LOG_DEBUG, "Downloading an initialization section of size %"PRId64"\n", sec_size);
++    sec_size = FFMIN(sec_size, max_init_section_size);
++    av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
++    ret = read_from_url(pls, pls->init_section, pls->init_sec_buf, pls->init_sec_buf_size, READ_COMPLETE);
++    ff_format_io_close(pls->parent, &pls->input);
++    if (ret < 0)
++        return ret;
++
++    if (pls->fix_multiple_stsd_order && pls->rep_idx > 0) {
++        uint8_t **stsd_entries = NULL;
++        int *stsd_entries_size = NULL;
++        int i = 4;
++
++        while (i <= (ret - 4)) {
++            // find start stsd atom
++            if (!memcmp(pls->init_sec_buf + i, "stsd", 4)) {
++                /* 1B version
++                 * 3B flags
++                 * 4B num of entries */
++                int stsd_first_offset = i + 8;
++                int stsd_offset = 0;
++                int j = 0;
++                uint32_t stsd_count = AV_RB32(pls->init_sec_buf + stsd_first_offset);
++                stsd_first_offset += 4;
++                if (stsd_count != pls->rep_count) {
++                    i++;
++                    continue;
++                }
++                // find all stsd entries
++                stsd_entries = av_mallocz_array(stsd_count, sizeof(*stsd_entries));
++                stsd_entries_size = av_mallocz_array(stsd_count, sizeof(*stsd_entries_size));
++                for (j = 0; j < stsd_count; ++j) {
++                    /* 4B - size
++                     * 4B - format */
++                    stsd_entries_size[j] = AV_RB32(pls->init_sec_buf + stsd_first_offset + stsd_offset);
++                    stsd_entries[j] = av_malloc(stsd_entries_size[j]);
++                    memcpy(stsd_entries[j], pls->init_sec_buf + stsd_first_offset + stsd_offset, stsd_entries_size[j]);
++                    stsd_offset += stsd_entries_size[j];
++                }
++                // reorder stsd entries
++                // as first put stsd entry for current representation
++                j = pls->rep_idx;
++                stsd_offset = stsd_first_offset;
++                memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
++                stsd_offset += stsd_entries_size[j];
++                for (j = 0; j < stsd_count; ++j) {
++                    if (j != pls->rep_idx) {
++                        memcpy(pls->init_sec_buf + stsd_offset, stsd_entries[j], stsd_entries_size[j]);
++                        stsd_offset += stsd_entries_size[j];
++                    }
++                    av_free(stsd_entries[j]);
++                }
++                av_freep(&stsd_entries);
++                av_freep(&stsd_entries_size);
++                break;
++            }
++            i++;
++        }
++    }
++
++    av_log(pls->parent, AV_LOG_TRACE, "pls[%p] init section size[%d]\n", pls, (int)ret);
++    pls->init_sec_data_len = ret;
++    pls->init_sec_buf_read_offset = 0;
++
++    return 0;
++}
++
++static int64_t seek_data(void *opaque, int64_t offset, int whence)
++{
++    struct representation *v = opaque;
++    if (v->n_fragments && !v->init_sec_data_len) {
++        return avio_seek(v->input, offset, whence);
++    }
++
++    return AVERROR(ENOSYS);
++}
++
++static int read_data(void *opaque, uint8_t *buf, int buf_size)
++{
++    int ret = 0;
++    struct representation *v = opaque;
++    DASHContext *c = v->parent->priv_data;
++
++restart:
++    if (!v->input) {
++        free_fragment(&v->cur_seg);
++        v->cur_seg = get_current_fragment(v);
++        if (!v->cur_seg) {
++            ret = AVERROR_EOF;
++            goto end;
++        }
++
++        /* load/update Media Initialization Section, if any */
++        ret = update_init_section(v);
++        if (ret)
++            goto end;
++
++        ret = open_input(c, v, v->cur_seg);
++        if (ret < 0) {
++            if (ff_check_interrupt(c->interrupt_callback)) {
++                goto end;
++                ret = AVERROR_EXIT;
++            }
++            av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment of playlist %d\n", v->rep_idx);
++            v->cur_seq_no++;
++            goto restart;
++        }
++    }
++
++    if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
++        /* Push init section out first before first actual fragment */
++        int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
++        memcpy(buf, v->init_sec_buf, copy_size);
++        v->init_sec_buf_read_offset += copy_size;
++        ret = copy_size;
++        goto end;
++    }
++
++    /* check the v->cur_seg, if it is null, get current and double check if the new v->cur_seg*/
++    if (!v->cur_seg) {
++        v->cur_seg = get_current_fragment(v);
++    }
++    if (!v->cur_seg) {
++        ret = AVERROR_EOF;
++        goto end;
++    }
++    ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
++    if (ret > 0)
++        goto end;
++    ff_format_io_close(v->parent, &v->input);
++    v->cur_seq_no++;
++    goto restart;
++
++end:
++    return ret;
++}
++
++static int save_avio_options(AVFormatContext *s)
++{
++    DASHContext *c = s->priv_data;
++    const char *opts[] = { "headers", "user_agent", "user-agent", "cookies", NULL }, **opt = opts;
++    uint8_t *buf = NULL;
++    int ret = 0;
++
++    while (*opt) {
++        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
++            if (buf[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 HDS playlist item '%s' referred to an external file '%s'. "
++           "Opening this file was forbidden for security reasons\n",
++           s->filename, url);
++    return AVERROR(EPERM);
++}
++
++static int reopen_demux_for_component(AVFormatContext *s, struct representation *pls)
++{
++    DASHContext *c = s->priv_data;
++    AVInputFormat *in_fmt = NULL;
++    AVDictionary  *in_fmt_opts = NULL;
++    uint8_t *avio_ctx_buffer  = NULL;
++    int ret = 0;
++
++    if (pls->ctx) {
++        /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
++        av_freep(&pls->pb.buffer);
++        memset(&pls->pb, 0x00, sizeof(AVIOContext));
++        pls->ctx->pb = NULL;
++        avformat_close_input(&pls->ctx);
++        pls->ctx = NULL;
++    }
++    if (!(pls->ctx = avformat_alloc_context())) {
++        ret = AVERROR(ENOMEM);
++        goto fail;
++    }
++
++    avio_ctx_buffer  = av_malloc(INITIAL_BUFFER_SIZE);
++    if (!avio_ctx_buffer ) {
++        ret = AVERROR(ENOMEM);
++        avformat_free_context(pls->ctx);
++        pls->ctx = NULL;
++        goto fail;
++    }
++    if (c->is_live) {
++        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
++    } else {
++        ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
++    }
++    pls->pb.seekable = 0;
++
++    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
++        goto fail;
++
++    pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
++    pls->ctx->probesize = 1024 * 4;
++    pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
++    ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
++    if (ret < 0) {
++        av_log(s, AV_LOG_ERROR, "Error when loading first fragment, playlist %d\n", (int)pls->rep_idx);
++        avformat_free_context(pls->ctx);
++        pls->ctx = NULL;
++        goto fail;
++    }
++
++    pls->ctx->pb = &pls->pb;
++    pls->ctx->io_open  = nested_io_open;
++
++    // provide additional information from mpd if available
++    ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
++    av_dict_free(&in_fmt_opts);
++    if (ret < 0)
++        goto fail;
++    if (pls->n_fragments) {
++        ret = avformat_find_stream_info(pls->ctx, NULL);
++        if (ret < 0)
++            goto fail;
++    }
++
++fail:
++    return ret;
++}
++
++static int open_demux_for_component(AVFormatContext *s, struct representation *pls)
++{
++    int ret = 0;
++    int i;
++
++    pls->parent = s;
++    pls->cur_seq_no  = calc_cur_seg_no(s, pls);
++    pls->last_seq_no = calc_max_seg_no(s, pls);
++
++    ret = reopen_demux_for_component(s, pls);
++    if (ret < 0) {
++        goto fail;
++    }
++    for (i = 0; i < pls->ctx->nb_streams; i++) {
++        AVStream *st = avformat_new_stream(s, NULL);
++        AVStream *ist = pls->ctx->streams[i];
++        if (!st) {
++            ret = AVERROR(ENOMEM);
++            goto fail;
++        }
++        st->id = i;
++        avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->codecpar);
++        avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
++    }
++
++    return 0;
++fail:
++    return ret;
++}
++
++static int dash_read_header(AVFormatContext *s)
++{
++    void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
++    DASHContext *c = s->priv_data;
++    int ret = 0;
++    int stream_index = 0;
++
++    c->interrupt_callback = &s->interrupt_callback;
++    // if the URL context is good, read important options we must broker later
++    if (u) {
++        update_options(&c->user_agent, "user-agent", u);
++        update_options(&c->cookies, "cookies", u);
++        update_options(&c->headers, "headers", u);
++    }
++
++    if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
++        goto fail;
++
++    if ((ret = save_avio_options(s)) < 0)
++        goto fail;
++
++    /* If this isn't a live stream, fill the total duration of the
++     * stream. */
++    if (!c->is_live) {
++        s->duration = (int64_t) c->media_presentation_duration_sec * AV_TIME_BASE;
++    }
++
++    /* Open the demuxer for curent video and current audio components if available */
++    if (!ret && c->cur_video) {
++        ret = open_demux_for_component(s, c->cur_video);
++        if (!ret) {
++            c->cur_video->stream_index = stream_index;
++            ++stream_index;
++        } else {
++            free_representation(c->cur_video);
++            c->cur_video = NULL;
++        }
++    }
++
++    if (!ret && c->cur_audio) {
++        ret = open_demux_for_component(s, c->cur_audio);
++        if (!ret) {
++            c->cur_audio->stream_index = stream_index;
++            ++stream_index;
++        } else {
++            free_representation(c->cur_audio);
++            c->cur_audio = NULL;
++        }
++    }
++
++    if (!stream_index) {
++        ret = AVERROR_INVALIDDATA;
++        goto fail;
++    }
++
++    /* Create a program */
++    if (!ret) {
++        AVProgram *program;
++        program = av_new_program(s, 0);
++        if (!program) {
++            goto fail;
++        }
++
++        if (c->cur_video) {
++            av_program_add_stream_index(s, 0, c->cur_video->stream_index);
++        }
++
++        if (c->cur_audio) {
++            av_program_add_stream_index(s, 0, c->cur_audio->stream_index);
++        }
++    }
++
++    return 0;
++fail:
++    return ret;
++}
++
++static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
++{
++    DASHContext *c = s->priv_data;
++    int ret = 0;
++    struct representation *cur = NULL;
++
++    if (!c->cur_audio && !c->cur_video) {
++        return AVERROR_INVALIDDATA;
++    }
++    if (c->cur_audio && !c->cur_video) {
++        cur = c->cur_audio;
++    } else if (!c->cur_audio && c->cur_video) {
++        cur = c->cur_video;
++    } else if (c->cur_video->cur_timestamp < c->cur_audio->cur_timestamp) {
++        cur = c->cur_video;
++    } else {
++        cur = c->cur_audio;
++    }
++
++    if (cur->ctx) {
++        ret = av_read_frame(cur->ctx, &cur->pkt);
++        if (ret < 0) {
++            av_packet_unref(&cur->pkt);
++        } else {
++            /* If we got a packet, return it */
++            *pkt = cur->pkt;
++            cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
++            pkt->stream_index = cur->stream_index;
++            reset_packet(&cur->pkt);
++            return 0;
++        }
++    }
++    return AVERROR_EOF;
++}
++
++static int dash_close(AVFormatContext *s)
++{
++    DASHContext *c = s->priv_data;
++
++    if (c->cur_audio) {
++        free_representation(c->cur_audio);
++    }
++
++    if (c->cur_video) {
++        free_representation(c->cur_video);
++    }
++
++    av_freep(&c->cookies);
++    av_freep(&c->user_agent);
++    av_dict_free(&c->avio_opts);
++    av_freep(&c->base_url);
++    return 0;
++}
++
++static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t seek_pos_msec, int flags)
++{
++    int ret = 0;
++    int i = 0;
++    int j = 0;
++    int64_t duration = 0;
++
++    av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms], playlist %d\n", seek_pos_msec, pls->rep_idx);
++
++    // single fragment mode
++    if (pls->n_fragments == 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 >> 1;
++    }
++
++    return 0;
++}
++
++#define OFFSET(x) offsetof(DASHContext, x)
++#define FLAGS AV_OPT_FLAG_DECODING_PARAM
++static const AVOption dash_options[] = {
++    {NULL}
++};
++
++static const AVClass dash_class = {
++    .class_name = "dash",
++    .item_name  = av_default_item_name,
++    .option     = dash_options,
++    .version    = LIBAVUTIL_VERSION_INT,
++};
++
++AVInputFormat ff_dash_demuxer = {
++    .name           = "dash",
++    .long_name      = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
++    .priv_class     = &dash_class,
++    .priv_data_size = sizeof(DASHContext),
++    .read_probe     = dash_probe,
++    .read_header    = dash_read_header,
++    .read_packet    = dash_read_packet,
++    .read_close     = dash_close,
++    .read_seek      = dash_read_seek,
++    .flags          = AVFMT_NO_BYTE_SEEK,
++};
+--- a/libavformat/Makefile     2018-11-18 01:58:00.000000000 +0100
++++ b/libavformat/Makefile     2023-02-14 20:19:47.774726060 +0100
+@@ -134,6 +134,7 @@
+ 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
+--- a/libavutil/avstring.c     2018-11-18 01:58:00.000000000 +0100
++++ b/libavutil/avstring.c     2023-02-14 20:19:47.778726097 +0100
+@@ -231,6 +231,29 @@
+     return c1 - c2;
+ }
++char *av_strireplace(const char *str, const char *from, const char *to)
++{
++    char *ret = NULL;
++    const char *pstr2, *pstr = str;
++    size_t tolen = strlen(to), fromlen = strlen(from);
++    AVBPrint pbuf;
++
++    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
++    while ((pstr2 = av_stristr(pstr, from))) {
++        av_bprint_append_data(&pbuf, pstr, pstr2 - pstr);
++        pstr = pstr2 + fromlen;
++        av_bprint_append_data(&pbuf, to, tolen);
++    }
++    av_bprint_append_data(&pbuf, pstr, strlen(pstr));
++    if (!av_bprint_is_complete(&pbuf)) {
++        av_bprint_finalize(&pbuf, NULL);
++    } else {
++        av_bprint_finalize(&pbuf, &ret);
++    }
++
++    return ret;
++}
++
+ const char *av_basename(const char *path)
+ {
+     char *p = strrchr(path, '/');
+--- a/libavutil/avstring.h     2018-11-18 01:58:00.000000000 +0100
++++ b/libavutil/avstring.h     2023-02-14 20:19:47.778726097 +0100
+@@ -67,6 +67,8 @@
+  */
+ char *av_stristr(const char *haystack, const char *needle);
++char *av_strireplace(const char *str, const char *from, const char *to);
++
+ /**
+  * Locate the first occurrence of the string needle in the string haystack
+  * where not more than hay_length characters are searched. A zero-length
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0006-ffmpeg-reset-compressed-header-flag.patch b/archive-patches/ffmpeg-cst/3.3.9/0006-ffmpeg-reset-compressed-header-flag.patch
new file mode 100644 (file)
index 0000000..7d94b3d
--- /dev/null
@@ -0,0 +1,11 @@
+# reset compressed header flag, fix http 302 request
+--- a/libavformat/http.c       2017-05-04 15:49:41.890902000 +0200
++++ b/libavformat/http.c       2017-07-30 11:30:47.502581448 +0200
+@@ -1248,6 +1248,7 @@
+     s->willclose        = 0;
+     s->end_chunked_post = 0;
+     s->end_header       = 0;
++    s->compressed       = 0;
+     if (post && !s->post_data && !send_expect_100) {
+         /* Pretend that it did work. We didn't read any header yet, since
+          * we've still to send the POST data, but the code calling this
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0007-ffmpeg-CST-fix-pts.patch b/archive-patches/ffmpeg-cst/3.3.9/0007-ffmpeg-CST-fix-pts.patch
new file mode 100644 (file)
index 0000000..bad6105
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/mpegts.c     2016-06-27 02:54:30.000000000 +0300
++++ b/libavformat/mpegts.c     2016-08-20 01:39:36.668873256 +0300
+@@ -904,10 +904,10 @@
+     pes->buffer = NULL;
+     reset_pes_packet_state(pes);
+-    sd = av_packet_new_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, 1);
++    /*sd = av_packet_new_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, 1);
+     if (!sd)
+         return AVERROR(ENOMEM);
+-    *sd = pes->stream_id;
++    *sd = pes->stream_id;*/
+     return 0;
+ }
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0008-ffmpeg-CST-fix-sps-pps.patch b/archive-patches/ffmpeg-cst/3.3.9/0008-ffmpeg-CST-fix-sps-pps.patch
new file mode 100755 (executable)
index 0000000..8d87f3a
--- /dev/null
@@ -0,0 +1,29 @@
+From 1c7b83f945e710a17a41ad9feb7dc929f26f2b0e Mon Sep 17 00:00:00 2001
+From: Jacek Jendrzej <satbaby@kawaii.com>
+Date: Wed, 28 Jun 2017 11:38:20 +0200
+Subject: [PATCH] fix sps/pps for cooli;This is commit that breaks seek in some
+ mkv.break with commit 6d2219e9f950b96279fd8464cc11c4d02518b629
+
+---
+ libavcodec/h264_mp4toannexb_bsf.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/libavcodec/h264_mp4toannexb_bsf.c b/libavcodec/h264_mp4toannexb_bsf.c
+index 163d0f59ce..c895cf2829 100644
+--- a/libavcodec/h264_mp4toannexb_bsf.c
++++ b/libavcodec/h264_mp4toannexb_bsf.c
+@@ -235,6 +235,11 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
+         if (!s->new_idr && unit_type == 5 && (buf[1] & 0x80))
+             s->new_idr = 1;
++      if (s->new_idr && unit_type == 6 && s->idr_sps_seen && s->idr_pps_seen){
++                  s->idr_sps_seen= 0;
++                  s->idr_pps_seen= 0;
++      }
++
+         /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */
+         if (s->new_idr && unit_type == 5 && !s->idr_sps_seen && !s->idr_pps_seen) {
+             if ((ret=alloc_and_copy(out,
+-- 
+2.13.2
+
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0009-ffmpeg-CST-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch b/archive-patches/ffmpeg-cst/3.3.9/0009-ffmpeg-CST-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch
new file mode 100644 (file)
index 0000000..37df0fc
--- /dev/null
@@ -0,0 +1,326 @@
+From c6f8769255ec6c4bb4ce02879d2332461ef1e24e Mon Sep 17 00:00:00 2001
+From: Jacek Jendrzej <satbaby@kawaii.com>
+Date: Sat, 12 Aug 2017 17:05:08 +0200
+Subject: [PATCH 7/7] add ASF VC1 Annex-G and RCV bitstream filters. Originally
+ done by Google but modified by us to convert certain VC1 in MKV without
+ startcodes. port fot 3.x
+
+Signed-off-by: Jacek Jendrzej <satbaby@kawaii.com>
+---
+ libavcodec/Makefile              |   2 +
+ libavcodec/bitstream_filters.c   |   2 +
+ libavcodec/vc1_asftoannexg_bsf.c | 182 +++++++++++++++++++++++++++++++++++++++
+ libavcodec/vc1_asftorcv_bsf.c    |  95 ++++++++++++++++++++
+ 4 files changed, 281 insertions(+)
+ create mode 100644 libavcodec/vc1_asftoannexg_bsf.c
+ create mode 100644 libavcodec/vc1_asftorcv_bsf.c
+
+--- a/libavcodec/bitstream_filters.c   2018-11-18 01:58:03.000000000 +0100
++++ b/libavcodec/bitstream_filters.c   2023-02-14 18:26:27.787945035 +0100
+@@ -41,6 +41,8 @@ extern const AVBitStreamFilter ff_noise_
+ extern const AVBitStreamFilter ff_remove_extradata_bsf;
+ extern const AVBitStreamFilter ff_text2movsub_bsf;
+ extern const AVBitStreamFilter ff_vp9_superframe_bsf;
++extern const AVBitStreamFilter ff_vc1_asftoannexg_bsf;
++extern const AVBitStreamFilter ff_vc1_asftorcv_bsf;
+ #include "libavcodec/bsf_list.c"
+--- a/libavcodec/Makefile      2018-11-18 01:57:59.000000000 +0100
++++ b/libavcodec/Makefile      2023-02-14 18:31:48.627199402 +0100
+@@ -987,6 +987,8 @@ OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF)
+ OBJS-$(CONFIG_NOISE_BSF)                  += noise_bsf.o
+ OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF)       += remove_extradata_bsf.o
+ OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
++OBJS-$(CONFIG_VC1_ASFTORCV_BSF)           += vc1_asftorcv_bsf.o
++OBJS-$(CONFIG_VC1_ASFTOANNEXG_BSF)        += vc1_asftoannexg_bsf.o vc1.o
+ OBJS-$(CONFIG_VP9_SUPERFRAME_BSF)         += vp9_superframe_bsf.o
+ # thread libraries
+--- a/libavcodec/vc1_asftoannexg_bsf.c 1970-01-01 01:00:00.000000000 +0100
++++ b/libavcodec/vc1_asftoannexg_bsf.c 2023-02-14 18:21:36.120977196 +0100
+@@ -0,0 +1,182 @@
++/*
++ * copyright (c) 2010 Google Inc.
++ * copyright (c) 2013 CoolStream International Ltd.
++ * copyright (c) 2017 Jacek Jendrzej port to 3.x
++ * 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 "avcodec.h"
++#include "bytestream.h"
++#include "vc1.h"
++#include "bsf.h"
++
++// An arbitrary limit in bytes greater than the current bytes used.
++#define MAX_SEQ_HEADER_SIZE 50
++
++typedef struct ASFTOANNEXGBSFContext {
++    int frames;
++    uint8_t *seq_header;
++    int seq_header_size;
++    uint8_t *ep_header;
++    int ep_header_size;
++} ASFTOANNEXGBSFContext;
++
++static int find_codec_data(ASFTOANNEXGBSFContext *ctx, uint8_t *data, int data_size, int keyframe) {
++    const uint8_t *start = data;
++    const uint8_t *end = data + data_size;
++    const uint8_t *next;
++    int size;
++    int has_seq_header = 0;
++    int has_ep_header = 0;
++    int has_frame_header = 0;
++
++    start = find_next_marker(start, end);
++    next = start;
++    for(; next < end; start = next){
++        next = find_next_marker(start + 4, end);
++        size = next - start;
++        if(size <= 0) continue;
++        switch(AV_RB32(start)){
++        case VC1_CODE_SEQHDR:
++            has_seq_header = 1;
++            break;
++        case VC1_CODE_ENTRYPOINT:
++            has_ep_header = 1;
++            break;
++        case VC1_CODE_FRAME:
++            has_frame_header = 1;
++            break;
++        default:
++            break;
++        }
++    }
++
++    if((has_seq_header && has_ep_header && has_frame_header && keyframe) ||
++       (!has_seq_header && !has_ep_header && has_frame_header) ) return 0;
++
++    return -1;
++}
++
++static int parse_extradata(ASFTOANNEXGBSFContext *ctx, uint8_t *extradata, int extradata_size) {
++    const uint8_t *start = extradata;
++    const uint8_t *end = extradata + extradata_size;
++    const uint8_t *next;
++    int size;
++
++    start = find_next_marker(start, end);
++    next = start;
++    for(; next < end; start = next){
++        next = find_next_marker(start + 4, end);
++        size = next - start;
++        if(size <= 0) continue;
++        switch(AV_RB32(start)){
++        case VC1_CODE_SEQHDR:
++            ctx->seq_header = av_mallocz(size);
++            ctx->seq_header_size = size;
++            memcpy(ctx->seq_header, start, size);
++            break;
++        case VC1_CODE_ENTRYPOINT:
++            ctx->ep_header = av_malloc(size);
++            ctx->ep_header_size = size;
++            memcpy(ctx->ep_header, start, size);
++            break;
++        default:
++            break;
++        }
++    }
++
++    if(!ctx->seq_header || !ctx->ep_header) {
++        av_log(NULL, AV_LOG_ERROR, "Incomplete extradata\n");
++        return -1;
++    }
++    return 0;
++}
++
++static int asftoannexg_filter(AVBSFContext *ctx, AVPacket *out)
++{
++    ASFTOANNEXGBSFContext* bsfctx = ctx->priv_data;
++    AVPacket *in;
++    int keyframe = 0;
++    int ret = 0;
++    uint8_t* bs = NULL;
++
++    ret = ff_bsf_get_packet(ctx, &in);
++    if (ret < 0)
++        return ret;
++
++    keyframe = in->flags & AV_PKT_FLAG_KEY;
++    if(in->size >= 1 && !find_codec_data(bsfctx, in->data, in->size, keyframe)) {
++//         av_log(NULL, AV_LOG_INFO, "Nothing to do: %i\n",in->size);
++        out->data = in->data;
++        out->size = in->size;
++        return 0;
++    }
++
++    if(!ctx->par_in->extradata || ctx->par_in->extradata_size < 16) {
++        av_log(NULL, AV_LOG_INFO, "Extradata size too small: %i\n", ctx->par_in->extradata_size);
++        out->data = in->data;
++        out->size = in->size;
++        return 0;
++    }
++
++    if (!bsfctx->frames && parse_extradata(bsfctx, ctx->par_in->extradata , ctx->par_in->extradata_size ) < 0) {
++       av_freep(in);
++        av_log(NULL, AV_LOG_ERROR, "Cannot parse extra data!\n");
++        return -1;
++    }
++
++    if (keyframe) {
++        // If this is the keyframe, need to put sequence header and entry point header.
++        out->size = bsfctx->seq_header_size + bsfctx->ep_header_size + 4 + in->size;
++        out->data = av_malloc(out->size);
++        bs = out->data;
++
++        memcpy(bs, bsfctx->seq_header, bsfctx->seq_header_size);
++        bs += bsfctx->seq_header_size;
++        memcpy(bs, bsfctx->ep_header, bsfctx->ep_header_size);
++        bs += bsfctx->ep_header_size;
++    } else {
++        out->size = 4 + in->size;
++        out->data = av_malloc(out->size);
++        bs = out->data;
++    }
++
++    // Put the frame start code and frame data.
++    bytestream_put_be32(&bs, VC1_CODE_FRAME);
++    memcpy(bs, in->data, in->size);
++    ++bsfctx->frames;
++    return 1;
++
++ }
++
++static void asftoannexg_close(AVBSFContext *bsfc) {
++    ASFTOANNEXGBSFContext *bsfctx = bsfc->priv_data;
++    av_freep(&bsfctx->seq_header);
++    av_freep(&bsfctx->ep_header);
++}
++
++static const enum AVCodecID codec_ids[] = {
++    AV_CODEC_ID_VC1, AV_CODEC_ID_NONE,
++};
++
++AVBitStreamFilter ff_vc1_asftoannexg_bsf = {
++    .name           = "vc1_asftoannexg",
++    .priv_data_size = sizeof(ASFTOANNEXGBSFContext),
++    .filter         = asftoannexg_filter,
++    .close          = asftoannexg_close,
++    .codec_ids      = codec_ids
++};
+--- a/libavcodec/vc1_asftorcv_bsf.c    1970-01-01 01:00:00.000000000 +0100
++++ b/libavcodec/vc1_asftorcv_bsf.c    2023-02-14 18:21:36.120977196 +0100
+@@ -0,0 +1,95 @@
++/*
++ * copyright (c) 2010 Google Inc.
++ * copyright (c) 2017 Jacek Jendrzej port to 3.x
++ * 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 "avcodec.h"
++#include "bytestream.h"
++#include "bsf.h"
++
++#define RCV_STREAM_HEADER_SIZE 36
++#define RCV_PICTURE_HEADER_SIZE 8
++
++typedef struct ASFTORCVBSFContext {
++    int frames;
++} ASFTORCVBSFContext;
++
++static int asftorcv_filter(AVBSFContext *ctx, AVPacket *out){
++    ASFTORCVBSFContext* bsfctx = ctx->priv_data;
++    AVPacket *in;
++    int keyframe = 0;
++    int ret = 0;
++    uint8_t* bs = NULL;
++
++    ret = ff_bsf_get_packet(ctx, &in);
++    if (ret < 0)
++        return ret;
++
++    keyframe = in->flags & AV_PKT_FLAG_KEY;
++
++    if (!bsfctx->frames) {
++        // Write the header if this is the first frame.
++        out->data = av_malloc(RCV_STREAM_HEADER_SIZE + RCV_PICTURE_HEADER_SIZE + in->size);
++        out->size = RCV_STREAM_HEADER_SIZE + RCV_PICTURE_HEADER_SIZE + in->size;
++        bs = out->data;
++
++        // The following structure of stream header comes from libavformat/vc1testenc.c.
++        bytestream_put_le24(&bs, 0);  // Frame count. 0 for streaming.
++        bytestream_put_byte(&bs, 0xC5);
++        bytestream_put_le32(&bs, 4);  // 4 bytes of extra data.
++        bytestream_put_byte(&bs, ctx->par_in->extradata[0]);
++        bytestream_put_byte(&bs, ctx->par_in->extradata[1]);
++        bytestream_put_byte(&bs, ctx->par_in->extradata[2]);
++        bytestream_put_byte(&bs, ctx->par_in->extradata[3]);
++        bytestream_put_le32(&bs, ctx->par_in->height);
++        bytestream_put_le32(&bs, ctx->par_in->width);
++        bytestream_put_le32(&bs, 0xC);
++        bytestream_put_le24(&bs, 0);  // hrd_buffer
++        bytestream_put_byte(&bs, 0x80);  // level|cbr|res1
++        bytestream_put_le32(&bs, 0);  // hrd_rate
++
++        // The following LE32 describes the frame rate. Since we don't care so fill
++        // it with 0xFFFFFFFF which means variable framerate.
++        // See: libavformat/vc1testenc.c
++        bytestream_put_le32(&bs, 0xFFFFFFFF);
++    } else {
++        out->data = av_malloc(RCV_PICTURE_HEADER_SIZE + in->size);
++        out->size = RCV_PICTURE_HEADER_SIZE + in->size;
++        bs = out->data;
++    }
++
++    // Write the picture header.
++    bytestream_put_le32(&bs, in->size | (keyframe ? 0x80000000 : 0));
++
++    //  The following LE32 describes the pts. Since we don't care so fill it with 0.
++    bytestream_put_le32(&bs, 0);
++    memcpy(bs, in->data, in->size);
++
++    ++bsfctx->frames;
++    return 0;
++}
++
++static const enum AVCodecID codec_ids[] = {
++    AV_CODEC_ID_WMV3, AV_CODEC_ID_NONE,
++};
++
++AVBitStreamFilter ff_vc1_asftorcv_bsf = {
++    .name         = "vc1_asftorcv",
++    .priv_data_size = sizeof(ASFTORCVBSFContext),
++    .filter         = asftorcv_filter
++};
+
+-- 
+2.14.1
+
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0010-ffmpeg-CST-allow-to-choose-rtmp-impl-at-runtime.patch b/archive-patches/ffmpeg-cst/3.3.9/0010-ffmpeg-CST-allow-to-choose-rtmp-impl-at-runtime.patch
new file mode 100644 (file)
index 0000000..14225bc
--- /dev/null
@@ -0,0 +1,112 @@
+--- ffmpeg-3.2.2/configure
++++ ffmpeg-3.2.2/configure
+@@ -3033,10 +3033,8 @@
+ # protocols
+ async_protocol_deps="threads"
+ bluray_protocol_deps="libbluray"
+-ffrtmpcrypt_protocol_deps="!librtmp_protocol"
+ ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl"
+ ffrtmpcrypt_protocol_select="tcp_protocol"
+-ffrtmphttp_protocol_deps="!librtmp_protocol"
+ ffrtmphttp_protocol_select="http_protocol"
+ ftp_protocol_select="tcp_protocol"
+ gopher_protocol_select="network"
+@@ -3053,14 +3051,12 @@
+ libssh_protocol_deps="libssh"
+ mmsh_protocol_select="http_protocol"
+ mmst_protocol_select="network"
+-rtmp_protocol_deps="!librtmp_protocol"
+-rtmp_protocol_select="tcp_protocol"
+-rtmpe_protocol_select="ffrtmpcrypt_protocol"
+-rtmps_protocol_deps="!librtmp_protocol"
+-rtmps_protocol_select="tls_protocol"
+-rtmpt_protocol_select="ffrtmphttp_protocol"
+-rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
+-rtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
++ffrtmp_protocol_select="tcp_protocol"
++ffrtmpe_protocol_select="ffrtmpcrypt_protocol"
++ffrtmps_protocol_select="tls_protocol"
++ffrtmpt_protocol_select="ffrtmphttp_protocol"
++ffrtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
++ffrtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
+ rtp_protocol_select="udp_protocol"
+ sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags"
+ sctp_protocol_select="network"
+--- ffmpeg-3.2.2/libavformat/rtmpproto.c
++++ ffmpeg-3.2.2/libavformat/rtmpproto.c
+@@ -2612,7 +2612,7 @@
+ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts)
+ {
+     RTMPContext *rt = s->priv_data;
+-    char proto[8], hostname[256], path[1024], auth[100], *fname;
++    char *proto, tmpProto[10], hostname[256], path[1024], auth[100], *fname;
+     char *old_app, *qmark, *n, fname_buffer[1024];
+     uint8_t buf[2048];
+     int port;
+@@ -2623,7 +2623,7 @@
+     rt->is_input = !(flags & AVIO_FLAG_WRITE);
+-    av_url_split(proto, sizeof(proto), auth, sizeof(auth),
++    memset(tmpProto, 0, sizeof(tmpProto)); proto = &tmpProto[2]; av_url_split(tmpProto, sizeof(tmpProto), auth, sizeof(auth),
+                  hostname, sizeof(hostname), &port,
+                  path, sizeof(path), s->filename);
+@@ -3157,9 +3157,9 @@
+ };
+-RTMP_PROTOCOL(rtmp)
+-RTMP_PROTOCOL(rtmpe)
+-RTMP_PROTOCOL(rtmps)
+-RTMP_PROTOCOL(rtmpt)
+-RTMP_PROTOCOL(rtmpte)
+-RTMP_PROTOCOL(rtmpts)
++RTMP_PROTOCOL(ffrtmp)
++RTMP_PROTOCOL(ffrtmpe)
++RTMP_PROTOCOL(ffrtmps)
++RTMP_PROTOCOL(ffrtmpt)
++RTMP_PROTOCOL(ffrtmpte)
++RTMP_PROTOCOL(ffrtmpts)
+--- a/libavformat/Makefile
++++ b/libavformat/Makefile
+@@ -566,12 +566,12 @@
+ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o asf.o
+ OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
+ OBJS-$(CONFIG_PROMPEG_PROTOCOL)          += prompeg.o
+-OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPE_PROTOCOL)            += rtmpproto.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTE_PROTOCOL)           += rtmpproto.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMP_PROTOCOL)           += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPE_PROTOCOL)          += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPS_PROTOCOL)          += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPT_PROTOCOL)          += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTE_PROTOCOL)         += rtmpproto.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTS_PROTOCOL)         += rtmpproto.o rtmppkt.o
+ OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o
+ OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
+ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
+--- a/libavformat/protocols.c
++++ b/libavformat/protocols.c
+@@ -44,12 +44,12 @@
+ extern const URLProtocol ff_md5_protocol;
+ extern const URLProtocol ff_pipe_protocol;
+ extern const URLProtocol ff_prompeg_protocol;
+-extern const URLProtocol ff_rtmp_protocol;
+-extern const URLProtocol ff_rtmpe_protocol;
+-extern const URLProtocol ff_rtmps_protocol;
+-extern const URLProtocol ff_rtmpt_protocol;
+-extern const URLProtocol ff_rtmpte_protocol;
+-extern const URLProtocol ff_rtmpts_protocol;
++extern const URLProtocol ff_ffrtmp_protocol;
++extern const URLProtocol ff_ffrtmpe_protocol;
++extern const URLProtocol ff_ffrtmps_protocol;
++extern const URLProtocol ff_ffrtmpt_protocol;
++extern const URLProtocol ff_ffrtmpte_protocol;
++extern const URLProtocol ff_ffrtmpts_protocol;
+ extern const URLProtocol ff_rtp_protocol;
+ extern const URLProtocol ff_sctp_protocol;
+ extern const URLProtocol ff_srtp_protocol;
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0011-ffmpeg-discont.patch b/archive-patches/ffmpeg-cst/3.3.9/0011-ffmpeg-discont.patch
new file mode 100644 (file)
index 0000000..67b86b0
--- /dev/null
@@ -0,0 +1,125 @@
+diff -u -pr a/ffmpeg.c b/ffmpeg.c
+--- a/ffmpeg.c 2021-10-02 14:03:12.000000000 +0200
++++ b/ffmpeg.c 2021-11-30 17:52:38.171328331 +0100
+@@ -4332,7 +4332,7 @@ static int process_input(int file_index)
+     if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
+          ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
+         pkt_dts != AV_NOPTS_VALUE && ist->next_dts == AV_NOPTS_VALUE && !copy_ts
+-        && (is->iformat->flags & AVFMT_TS_DISCONT) && ifile->last_ts != AV_NOPTS_VALUE) {
++        && (is->iformat->flags & AVFMT_TS_DISCONT) && ifile->last_ts != AV_NOPTS_VALUE && !force_dts_monotonicity) {
+         int64_t delta   = pkt_dts - ifile->last_ts;
+         if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
+             delta >  1LL*dts_delta_threshold*AV_TIME_BASE){
+@@ -4360,7 +4360,7 @@ static int process_input(int file_index)
+     if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
+          ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
+          pkt_dts != AV_NOPTS_VALUE && ist->next_dts != AV_NOPTS_VALUE &&
+-        !copy_ts) {
++        !copy_ts && !force_dts_monotonicity) {
+         int64_t delta   = pkt_dts - ist->next_dts;
+         if (is->iformat->flags & AVFMT_TS_DISCONT) {
+             if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
+@@ -4395,6 +4395,43 @@ static int process_input(int file_index)
+     if (pkt.dts != AV_NOPTS_VALUE)
+         ifile->last_ts = av_rescale_q(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q);
++    if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
++         ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
++        (pkt.pts != AV_NOPTS_VALUE || pkt.dts != AV_NOPTS_VALUE) &&
++        force_dts_monotonicity) {
++        int64_t ff_pts_error = 0;
++        int64_t ff_dts_error = 0;
++        int64_t ff_dts_threshold = av_rescale_q(dts_monotonicity_threshold, AV_TIME_BASE_Q, ist->st->time_base);
++
++        // adjust the incoming packet by the accumulated monotonicity error
++        if (pkt.pts != AV_NOPTS_VALUE) {
++            pkt.pts += ifile->ff_timestamp_monotonicity_offset;
++            if (ist->next_pts != AV_NOPTS_VALUE) {
++                ff_pts_error = av_rescale_q(ist->next_pts, AV_TIME_BASE_Q, ist->st->time_base) - pkt.pts;
++            }
++        }
++        if (pkt.dts != AV_NOPTS_VALUE) {
++            pkt.dts += ifile->ff_timestamp_monotonicity_offset;
++            if (ist->next_dts != AV_NOPTS_VALUE) {
++                ff_dts_error = av_rescale_q(ist->next_dts, AV_TIME_BASE_Q, ist->st->time_base) - pkt.dts;
++            }
++        }
++
++        if (ff_dts_error > 0 || ff_dts_error < (-ff_dts_threshold) || ff_pts_error < (-ff_dts_threshold)) {
++            if (pkt.dts == AV_NOPTS_VALUE /*|| ist->next_dts != AV_NOPTS_VALUE*/) {
++                pkt.pts += ff_pts_error;
++                ifile->ff_timestamp_monotonicity_offset += ff_pts_error;
++                av_log(is, AV_LOG_INFO, "Incoming PTS error %"PRId64", offsetting subsequent timestamps by %"PRId64" to correct\n", ff_pts_error, ifile->ff_timestamp_monotonicity_offset);
++            }
++            else {
++                pkt.pts += ff_dts_error;
++                pkt.dts += ff_dts_error;
++                ifile->ff_timestamp_monotonicity_offset += ff_dts_error;
++                av_log(is, AV_LOG_INFO, "Incoming DTS error %"PRId64", offsetting subsequent timestamps by %"PRId64" to correct\n", ff_dts_error, ifile->ff_timestamp_monotonicity_offset);
++            }
++        }
++    }
++
+     if (debug_ts) {
+         av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s off:%s off_time:%s\n",
+                ifile->ist_index + pkt.stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
+Nur in _ffmpeg-git-2ba896f: ffmpeg.c.orig.
+Nur in _ffmpeg-git-2ba896f: ffmpeg.c.rej.
+diff -u -pr a/ffmpeg.h b/ffmpeg.h
+--- a/ffmpeg.h 2021-10-02 14:03:12.000000000 +0200
++++ b/ffmpeg.h 2021-11-30 17:47:55.128659787 +0100
+@@ -417,6 +417,10 @@ typedef struct InputFile {
+     int joined;                 /* the thread has been joined */
+     int thread_queue_size;      /* maximum number of queued packets */
+ #endif
++
++    // A value added to inbound timestamps to prevent them from going "backward" in cases such as HLS discontinuities
++    int64_t ff_timestamp_monotonicity_offset;
++
+ } InputFile;
+ enum forced_keyframes_const {
+@@ -584,6 +588,9 @@ extern float audio_drift_threshold;
+ extern float dts_delta_threshold;
+ extern float dts_error_threshold;
++extern int dts_monotonicity_threshold;
++extern int force_dts_monotonicity;
++
+ extern int audio_volume;
+ extern int audio_sync_method;
+ extern int video_sync_method;
+Nur in _ffmpeg-git-2ba896f: ffmpeg.h.orig.
+diff -u -pr a/ffmpeg_opt.c b/ffmpeg_opt.c
+--- a/ffmpeg_opt.c     2021-10-02 14:03:12.000000000 +0200
++++ b/ffmpeg_opt.c     2021-11-30 17:47:55.128659787 +0100
+@@ -99,6 +99,9 @@ float audio_drift_threshold = 0.1;
+ float dts_delta_threshold   = 10;
+ float dts_error_threshold   = 3600*30;
++int dts_monotonicity_threshold = AV_TIME_BASE;
++int force_dts_monotonicity = 0;
++
+ int audio_volume      = 256;
+ int audio_sync_method = 0;
+ int video_sync_method = VSYNC_AUTO;
+@@ -1070,6 +1073,7 @@ static int open_input_file(OptionsContex
+     f->recording_time = o->recording_time;
+     f->input_ts_offset = o->input_ts_offset;
+     f->ts_offset  = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
++    f->ff_timestamp_monotonicity_offset = 0;
+     f->nb_streams = ic->nb_streams;
+     f->rate_emu   = o->rate_emu;
+     f->accurate_seek = o->accurate_seek;
+@@ -3404,6 +3408,10 @@ const OptionDef options[] = {
+     { "apad",           OPT_STRING | HAS_ARG | OPT_SPEC |
+                         OPT_OUTPUT,                                  { .off = OFFSET(apad) },
+         "audio pad", "" },
++    { "force_dts_monotonicity", OPT_BOOL | OPT_EXPERT,               { &force_dts_monotonicity },
++        "correct dts monotonicity errors" },
++    { "dts_monotonicity_threshold", HAS_ARG | OPT_INT | OPT_EXPERT,  { &dts_monotonicity_threshold },
++        "dts correction threshold for forward jumps", "microseconds" },
+     { "dts_delta_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,       { &dts_delta_threshold },
+         "timestamp discontinuity delta threshold", "threshold" },
+     { "dts_error_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,       { &dts_error_threshold },
+Nur in _ffmpeg-git-2ba896f: ffmpeg_opt.c.orig.
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0012-ffmpeg-nolog.patch b/archive-patches/ffmpeg-cst/3.3.9/0012-ffmpeg-nolog.patch
new file mode 100644 (file)
index 0000000..b8add3f
--- /dev/null
@@ -0,0 +1,28 @@
+diff --git a/libavutil/log.c b/libavutil/log.c
+index 5948e50467..528eaa728c 100644
+--- a/libavutil/log.c
++++ b/libavutil/log.c
+@@ -53,7 +53,7 @@ static AVMutex mutex = AV_MUTEX_INITIALIZER;
+ #define BACKTRACE_LOGLEVEL AV_LOG_ERROR
+ #endif
+-static int av_log_level = AV_LOG_INFO;
++static int av_log_level = AV_LOG_ERROR; // NOT WORKING for libs
+ static int flags;
+ #define NB_LEVELS 8
+@@ -366,14 +366,6 @@ static void (*av_log_callback)(void*, int, const char*, va_list) =
+ void av_log(void* avcl, int level, const char *fmt, ...)
+ {
+-    AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
+-    va_list vl;
+-    va_start(vl, fmt);
+-    if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) &&
+-        avc->log_level_offset_offset && level >= AV_LOG_FATAL)
+-        level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset);
+-    av_vlog(avcl, level, fmt, vl);
+-    va_end(vl);
+ }
+ void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...)
diff --git a/archive-patches/ffmpeg-cst/3.3.9/0013-ffmpeg-disable-whitelist.patch b/archive-patches/ffmpeg-cst/3.3.9/0013-ffmpeg-disable-whitelist.patch
new file mode 100644 (file)
index 0000000..df1560f
--- /dev/null
@@ -0,0 +1,19 @@
+diff --git a/libavformat/avio.c b/libavformat/avio.c
+index 4846bbd8c6..978bf72994 100644
+--- a/libavformat/avio.c
++++ b/libavformat/avio.c
+@@ -180,12 +180,12 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)
+                (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));
+     av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
+                (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));
+-
++/*
+     if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
+         av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);
+         return AVERROR(EINVAL);
+     }
+-
++*/
+     if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {
+         av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);
+         return AVERROR(EINVAL);
diff --git a/archive-patches/ffmpeg-cst/3.3/ffmpeg34-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch b/archive-patches/ffmpeg-cst/3.3/ffmpeg34-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch
deleted file mode 100644 (file)
index b028809..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-diff --git a/libavcodec/Makefile b/libavcodec/Makefile
-index c4ec09b1c4..4462d98ff9 100644
---- a/libavcodec/Makefile
-+++ b/libavcodec/Makefile
-@@ -1024,6 +1024,8 @@ OBJS-$(CONFIG_NOISE_BSF)                  += noise_bsf.o
- OBJS-$(CONFIG_NULL_BSF)                   += null_bsf.o
- OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF)       += remove_extradata_bsf.o
- OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
-+OBJS-$(CONFIG_VC1_ASFTORCV_BSF)           += vc1_asftorcv_bsf.o
-+OBJS-$(CONFIG_VC1_ASFTOANNEXG_BSF)        += vc1_asftoannexg_bsf.o vc1.o
- OBJS-$(CONFIG_VP9_RAW_REORDER_BSF)        += vp9_raw_reorder_bsf.o
- OBJS-$(CONFIG_VP9_SUPERFRAME_BSF)         += vp9_superframe_bsf.o
- OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF)   += vp9_superframe_split_bsf.o
-diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
-index ace2a1e01f..ac8be8c90b 100644
---- a/libavcodec/bitstream_filters.c
-+++ b/libavcodec/bitstream_filters.c
-@@ -41,6 +41,8 @@ extern const AVBitStreamFilter ff_noise_bsf;
- extern const AVBitStreamFilter ff_null_bsf;
- extern const AVBitStreamFilter ff_remove_extradata_bsf;
- extern const AVBitStreamFilter ff_text2movsub_bsf;
-+extern const AVBitStreamFilter ff_vc1_asftoannexg_bsf;
-+extern const AVBitStreamFilter ff_vc1_asftorcv_bsf;
- extern const AVBitStreamFilter ff_vp9_raw_reorder_bsf;
- extern const AVBitStreamFilter ff_vp9_superframe_bsf;
- extern const AVBitStreamFilter ff_vp9_superframe_split_bsf;
-diff --git a/libavcodec/vc1_asftoannexg_bsf.c b/libavcodec/vc1_asftoannexg_bsf.c
-new file mode 100644
-index 0000000000..ba68346118
---- /dev/null
-+++ b/libavcodec/vc1_asftoannexg_bsf.c
-@@ -0,0 +1,189 @@
-+/*
-+ * copyright (c) 2010 Google Inc.
-+ * copyright (c) 2013 CoolStream International Ltd.
-+ * copyright (c) 2017 Jacek Jendrzej port to 3.x
-+ * 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 "avcodec.h"
-+#include "bytestream.h"
-+#include "vc1.h"
-+#include "bsf.h"
-+
-+// An arbitrary limit in bytes greater than the current bytes used.
-+#define MAX_SEQ_HEADER_SIZE 50
-+
-+typedef struct ASFTOANNEXGBSFContext {
-+    int frames;
-+    uint8_t *seq_header;
-+    int seq_header_size;
-+    uint8_t *ep_header;
-+    int ep_header_size;
-+} ASFTOANNEXGBSFContext;
-+
-+static int find_codec_data(ASFTOANNEXGBSFContext *ctx, uint8_t *data, int data_size, int keyframe) {
-+    const uint8_t *start = data;
-+    const uint8_t *end = data + data_size;
-+    const uint8_t *next;
-+    int size;
-+    int has_seq_header = 0;
-+    int has_ep_header = 0;
-+    int has_frame_header = 0;
-+
-+    start = find_next_marker(start, end);
-+    next = start;
-+    for(; next < end; start = next){
-+        next = find_next_marker(start + 4, end);
-+        size = next - start;
-+        if(size <= 0) continue;
-+        switch(AV_RB32(start)){
-+        case VC1_CODE_SEQHDR:
-+            has_seq_header = 1;
-+            break;
-+        case VC1_CODE_ENTRYPOINT:
-+            has_ep_header = 1;
-+            break;
-+        case VC1_CODE_FRAME:
-+            has_frame_header = 1;
-+            break;
-+        default:
-+            break;
-+        }
-+    }
-+
-+    if((has_seq_header && has_ep_header && has_frame_header && keyframe) ||
-+       (!has_seq_header && !has_ep_header && has_frame_header) ) return 0;
-+
-+    return -1;
-+}
-+
-+static int parse_extradata(ASFTOANNEXGBSFContext *ctx, uint8_t *extradata, int extradata_size) {
-+    const uint8_t *start = extradata;
-+    const uint8_t *end = extradata + extradata_size;
-+    const uint8_t *next;
-+    int size;
-+
-+    start = find_next_marker(start, end);
-+    next = start;
-+    for(; next < end; start = next){
-+        next = find_next_marker(start + 4, end);
-+        size = next - start;
-+        if(size <= 0) continue;
-+        switch(AV_RB32(start)){
-+        case VC1_CODE_SEQHDR:
-+            ctx->seq_header = av_mallocz(size);
-+            ctx->seq_header_size = size;
-+            memcpy(ctx->seq_header, start, size);
-+            break;
-+        case VC1_CODE_ENTRYPOINT:
-+            ctx->ep_header = av_malloc(size);
-+            ctx->ep_header_size = size;
-+            memcpy(ctx->ep_header, start, size);
-+            break;
-+        default:
-+            break;
-+        }
-+    }
-+
-+    if(!ctx->seq_header || !ctx->ep_header) {
-+        av_log(NULL, AV_LOG_ERROR, "Incomplete extradata\n");
-+        return -1;
-+    }
-+    return 0;
-+}
-+
-+static int asftoannexg_filter(AVBSFContext *ctx, AVPacket *out)
-+{
-+    ASFTOANNEXGBSFContext* bsfctx = ctx->priv_data;
-+    AVPacket *in;
-+    int keyframe;
-+    int ret;
-+    uint8_t* bs = NULL;
-+
-+    ret = ff_bsf_get_packet(ctx, &in);
-+    if (ret < 0)
-+        return ret;
-+
-+    keyframe = in->flags & AV_PKT_FLAG_KEY;
-+    if(in->size >= 1 && !find_codec_data(bsfctx, in->data, in->size, keyframe)) {
-+//         av_log(NULL, AV_LOG_INFO, "Nothing to do: %i\n",in->size);
-+        av_packet_move_ref(out, in);
-+        av_packet_free(&in);
-+        return 0;
-+    }
-+
-+    if(!ctx->par_in->extradata || ctx->par_in->extradata_size < 16) {
-+        av_log(NULL, AV_LOG_INFO, "Extradata size too small: %i\n", ctx->par_in->extradata_size);
-+        av_packet_move_ref(out, in);
-+        av_packet_free(&in);
-+        return 0;
-+    }
-+
-+    if (!bsfctx->frames && parse_extradata(bsfctx, ctx->par_in->extradata , ctx->par_in->extradata_size) < 0) {
-+        av_packet_free(&in);
-+        av_log(NULL, AV_LOG_ERROR, "Cannot parse extra data!\n");
-+        return -1;
-+    }
-+
-+    if (keyframe) {
-+        // If this is the keyframe, need to put sequence header and entry point header.
-+        ret = av_new_packet(out, bsfctx->seq_header_size + bsfctx->ep_header_size + 4 + in->size);
-+        if (ret < 0)
-+            goto exit;
-+        bs = out->data;
-+
-+        memcpy(bs, bsfctx->seq_header, bsfctx->seq_header_size);
-+        bs += bsfctx->seq_header_size;
-+        memcpy(bs, bsfctx->ep_header, bsfctx->ep_header_size);
-+        bs += bsfctx->ep_header_size;
-+    } else {
-+        ret = av_new_packet(out, 4 + in->size);
-+        if (ret < 0)
-+            goto exit;
-+        bs = out->data;
-+    }
-+
-+    // Put the frame start code and frame data.
-+    bytestream_put_be32(&bs, VC1_CODE_FRAME);
-+    memcpy(bs, in->data, in->size);
-+    ++bsfctx->frames;
-+
-+    ret = av_packet_copy_props(out, in);
-+
-+exit:
-+    av_packet_free(&in);
-+
-+    return ret;
-+}
-+
-+static void asftoannexg_close(AVBSFContext *bsfc) {
-+    ASFTOANNEXGBSFContext *bsfctx = bsfc->priv_data;
-+    av_freep(&bsfctx->seq_header);
-+    av_freep(&bsfctx->ep_header);
-+}
-+
-+static const enum AVCodecID codec_ids[] = {
-+    AV_CODEC_ID_VC1, AV_CODEC_ID_NONE,
-+};
-+
-+AVBitStreamFilter ff_vc1_asftoannexg_bsf = {
-+    .name           = "vc1_asftoannexg",
-+    .priv_data_size = sizeof(ASFTOANNEXGBSFContext),
-+    .filter         = asftoannexg_filter,
-+    .close          = asftoannexg_close,
-+    .codec_ids      = codec_ids,
-+};
-diff --git a/libavcodec/vc1_asftorcv_bsf.c b/libavcodec/vc1_asftorcv_bsf.c
-new file mode 100644
-index 0000000000..bdce5b5003
---- /dev/null
-+++ b/libavcodec/vc1_asftorcv_bsf.c
-@@ -0,0 +1,104 @@
-+/*
-+ * copyright (c) 2010 Google Inc.
-+ * copyright (c) 2017 Jacek Jendrzej port to 3.x
-+ * 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 "avcodec.h"
-+#include "bytestream.h"
-+#include "bsf.h"
-+
-+#define RCV_STREAM_HEADER_SIZE 36
-+#define RCV_PICTURE_HEADER_SIZE 8
-+
-+typedef struct ASFTORCVBSFContext {
-+    int frames;
-+} ASFTORCVBSFContext;
-+
-+static int asftorcv_filter(AVBSFContext *ctx, AVPacket *out){
-+    ASFTORCVBSFContext* bsfctx = ctx->priv_data;
-+    AVPacket *in;
-+    int keyframe;
-+    int ret;
-+    uint8_t* bs = NULL;
-+
-+    ret = ff_bsf_get_packet(ctx, &in);
-+    if (ret < 0)
-+        return ret;
-+
-+    keyframe = in->flags & AV_PKT_FLAG_KEY;
-+
-+    if (!bsfctx->frames) {
-+        // Write the header if this is the first frame.
-+        ret = av_new_packet(out, RCV_STREAM_HEADER_SIZE + RCV_PICTURE_HEADER_SIZE + in->size);
-+        if (ret < 0)
-+            goto exit;
-+        bs = out->data;
-+
-+        // The following structure of stream header comes from libavformat/vc1testenc.c.
-+        bytestream_put_le24(&bs, 0);  // Frame count. 0 for streaming.
-+        bytestream_put_byte(&bs, 0xC5);
-+        bytestream_put_le32(&bs, 4);  // 4 bytes of extra data.
-+        bytestream_put_byte(&bs, ctx->par_in->extradata[0]);
-+        bytestream_put_byte(&bs, ctx->par_in->extradata[1]);
-+        bytestream_put_byte(&bs, ctx->par_in->extradata[2]);
-+        bytestream_put_byte(&bs, ctx->par_in->extradata[3]);
-+        bytestream_put_le32(&bs, ctx->par_in->height);
-+        bytestream_put_le32(&bs, ctx->par_in->width);
-+        bytestream_put_le32(&bs, 0xC);
-+        bytestream_put_le24(&bs, 0);  // hrd_buffer
-+        bytestream_put_byte(&bs, 0x80);  // level|cbr|res1
-+        bytestream_put_le32(&bs, 0);  // hrd_rate
-+
-+        // The following LE32 describes the frame rate. Since we don't care so fill
-+        // it with 0xFFFFFFFF which means variable framerate.
-+        // See: libavformat/vc1testenc.c
-+        bytestream_put_le32(&bs, 0xFFFFFFFF);
-+    } else {
-+        ret = av_new_packet(out, RCV_PICTURE_HEADER_SIZE + in->size);
-+        if (ret < 0)
-+            goto exit;
-+        bs = out->data;
-+    }
-+
-+    // Write the picture header.
-+    bytestream_put_le32(&bs, in->size | (keyframe ? 0x80000000 : 0));
-+
-+    //  The following LE32 describes the pts. Since we don't care so fill it with 0.
-+    bytestream_put_le32(&bs, 0);
-+    memcpy(bs, in->data, in->size);
-+
-+    ++bsfctx->frames;
-+
-+    ret = av_packet_copy_props(out, in);
-+
-+exit:
-+    av_packet_free(&in);
-+
-+    return ret;
-+}
-+
-+static const enum AVCodecID codec_ids[] = {
-+    AV_CODEC_ID_WMV3, AV_CODEC_ID_NONE,
-+};
-+
-+AVBitStreamFilter ff_vc1_asftorcv_bsf = {
-+    .name             = "vc1_asftorcv",
-+    .priv_data_size = sizeof(ASFTORCVBSFContext),
-+    .filter         = asftorcv_filter,
-+    .codec_ids      = codec_ids,
-+};