]> git.webhop.me Git - bs-cst-neutrino-hd.git/commitdiff
bump version ffmpeg-6.1.4
authorMarkham <markham001@gmx.de>
Sat, 29 Nov 2025 09:21:05 +0000 (10:21 +0100)
committerMarkham <markham001@gmx.de>
Sat, 29 Nov 2025 09:21:05 +0000 (10:21 +0100)
54 files changed:
archive-patches/ffmpeg-arm/6.1.4/0001-ffmpeg-nolog.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0002-fix-mpegts.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0004-hls-replace-key-uri.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0005-mips64-cpu-detection.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0006-optimize-aac.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0007-increase-buffer-size.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0008-recheck-discard-flags.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0010-ffmpeg-replay_dash.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0011-rtsp.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0012-dxva2.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0014-ffmpeg-whitelist.patch [new file with mode: 0644]
archive-patches/ffmpeg-arm/6.1.4/0015-ffmpeg-support-kid-key.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0001-aac-optimize0.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0002-add64_c-parentheses.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0003-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0004-cst-allow-to-choose-rtmp-impl-at-runtime.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0005-cst-fix-h264-sps-pps.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0006-cst-fix-mpegts-pts.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0007-disable-whitelist.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0008-mov-cenc-fixes.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0009-ffmpeg-discont.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0010-hls-replace-key-uri.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0011-recheck-discard-flags.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/0012-rtsp.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/ffmpeg-increase-IO_BUFFER_SIZE-to-256k.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-dash-hacks.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-hls-changes.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/optional/README.txt [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-dash-hacks.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-hls-changes.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.2/0022-ffmpeg61-hls-discontinuity.patch [new file with mode: 0755]
archive-patches/ffmpeg-cst/6.1.4/0001-ffmpeg-nolog.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0002-fix-mpegts.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0004-hls-replace-key-uri.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0005-mips64-cpu-detection.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0006-optimize-aac.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0007-increase-buffer-size.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0008-recheck-discard-flags.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0010-ffmpeg-replay_dash.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0011-rtsp.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0012-dxva2.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0014-ffmpeg-whitelist.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0015-ffmpeg-support-kid-key.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0016-fixparentheses-warning.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0020-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0021-cst-fix-h264-sps-pps.patch [new file with mode: 0644]
archive-patches/ffmpeg-cst/6.1.4/0022-ffmpeg61-hls-discontinuity.patch [new file with mode: 0755]
make/versions.mk

diff --git a/archive-patches/ffmpeg-arm/6.1.4/0001-ffmpeg-nolog.patch b/archive-patches/ffmpeg-arm/6.1.4/0001-ffmpeg-nolog.patch
new file mode 100644 (file)
index 0000000..ecb6ee4
--- /dev/null
@@ -0,0 +1,24 @@
+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
+@@ -408,10 +408,6 @@ static void (*av_log_callback)(void*, int, const char*, va_list) =
+ void av_log(void* avcl, int level, const char *fmt, ...)
+ {
+-    va_list vl;
+-    va_start(vl, fmt);
+-    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-arm/6.1.4/0002-fix-mpegts.patch b/archive-patches/ffmpeg-arm/6.1.4/0002-fix-mpegts.patch
new file mode 100644 (file)
index 0000000..3976ca7
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/mpegts.c
++++ b/libavformat/mpegts.c
+@@ -1041,10 +1041,12 @@
+     pes->buffer = NULL;
+     reset_pes_packet_state(pes);
++    /*
+     sd = av_packet_new_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, 1);
+     if (!sd)
+         return AVERROR(ENOMEM);
+     *sd = pes->stream_id;
++    */
+     return 0;
+ }
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch b/archive-patches/ffmpeg-arm/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch
new file mode 100644 (file)
index 0000000..7a7c340
--- /dev/null
@@ -0,0 +1,126 @@
+--- a/configure
++++ b/configure
+@@ -3615,10 +3615,8 @@
+ # protocols
+ async_protocol_deps="threads"
+ bluray_protocol_deps="libbluray"
+-ffrtmpcrypt_protocol_conflict="librtmp_protocol"
+ ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls"
+ ffrtmpcrypt_protocol_select="tcp_protocol"
+-ffrtmphttp_protocol_conflict="librtmp_protocol"
+ ffrtmphttp_protocol_select="http_protocol"
+ ftp_protocol_select="tcp_protocol"
+ gopher_protocol_select="tcp_protocol"
+@@ -3632,20 +3630,18 @@
+ icecast_protocol_select="http_protocol"
+ mmsh_protocol_select="http_protocol"
+ mmst_protocol_select="network"
+-rtmp_protocol_conflict="librtmp_protocol"
+-rtmp_protocol_select="tcp_protocol"
+-rtmp_protocol_suggest="zlib"
+-rtmpe_protocol_select="ffrtmpcrypt_protocol"
+-rtmpe_protocol_suggest="zlib"
+-rtmps_protocol_conflict="librtmp_protocol"
+-rtmps_protocol_select="tls_protocol"
+-rtmps_protocol_suggest="zlib"
+-rtmpt_protocol_select="ffrtmphttp_protocol"
+-rtmpt_protocol_suggest="zlib"
+-rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
+-rtmpte_protocol_suggest="zlib"
+-rtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
+-rtmpts_protocol_suggest="zlib"
++ffrtmp_protocol_select="tcp_protocol"
++ffrtmp_protocol_suggest="zlib"
++ffrtmpe_protocol_select="ffrtmpcrypt_protocol"
++ffrtmpe_protocol_suggest="zlib"
++ffrtmps_protocol_select="tls_protocol"
++ffrtmps_protocol_suggest="zlib"
++ffrtmpt_protocol_select="ffrtmphttp_protocol"
++ffrtmpt_protocol_suggest="zlib"
++ffrtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
++ffrtmpte_protocol_suggest="zlib"
++ffrtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
++ffrtmpts_protocol_suggest="zlib"
+ rtp_protocol_select="udp_protocol"
+ schannel_conflict="openssl gnutls libtls mbedtls"
+ sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags"
+--- a/libavformat/Makefile
++++ b/libavformat/Makefile
+@@ -676,12 +676,12 @@
+ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o asf_tags.o
+ OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
+ OBJS-$(CONFIG_PROMPEG_PROTOCOL)          += prompeg.o
+-OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPE_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTE_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMP_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPE_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPS_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPT_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTE_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTS_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
+ OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o ip.o
+ OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
+ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
+--- a/libavformat/protocols.c
++++ b/libavformat/protocols.c
+@@ -48,12 +48,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;
+--- a/libavformat/rtmpproto.c
++++ b/libavformat/rtmpproto.c
+@@ -2627,7 +2627,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;
+@@ -2638,7 +2638,9 @@
+     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);
+@@ -3185,9 +3187,9 @@
+ #define RTMP_PROTOCOL(flavor, uppercase)         \
+     RTMP_PROTOCOL_3(flavor, CONFIG_ ## uppercase ## _PROTOCOL)
+-RTMP_PROTOCOL(rtmp,   RTMP)
+-RTMP_PROTOCOL(rtmpe,  RTMPE)
+-RTMP_PROTOCOL(rtmps,  RTMPS)
+-RTMP_PROTOCOL(rtmpt,  RTMPT)
+-RTMP_PROTOCOL(rtmpte, RTMPTE)
+-RTMP_PROTOCOL(rtmpts, RTMPTS)
++RTMP_PROTOCOL(ffrtmp,   FFRTMP)
++RTMP_PROTOCOL(ffrtmpe,  FFRTMPE)
++RTMP_PROTOCOL(ffrtmps,  FFRTMPS)
++RTMP_PROTOCOL(ffrtmpt,  FFRTMPT)
++RTMP_PROTOCOL(ffrtmpte, FFRTMPTE)
++RTMP_PROTOCOL(ffrtmpts, FFRTMPTS)
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0004-hls-replace-key-uri.patch b/archive-patches/ffmpeg-arm/6.1.4/0004-hls-replace-key-uri.patch
new file mode 100644 (file)
index 0000000..6b0fa7c
--- /dev/null
@@ -0,0 +1,47 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -226,6 +226,8 @@
+     int http_persistent;
+     int http_multiple;
+     int http_seekable;
++    char *key_uri_replace_old;
++    char *key_uri_replace_new;
+     int seg_max_retry;
+     AVIOContext *playlist_pb;
+     HLSCryptoContext  crypto_ctx;
+@@ -1295,8 +1297,16 @@
+     if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
+         if (strcmp(seg->key, pls->key_url)) {
++            char *key_url = NULL;
+             AVIOContext *pb = NULL;
+-            if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts, NULL) == 0) {
++            if (NULL != c->key_uri_replace_old && \
++                NULL != c-> key_uri_replace_new && \
++                '\0' != c->key_uri_replace_old[0]) {
++                key_url = av_strireplace(seg->key, c->key_uri_replace_old, c->key_uri_replace_new);
++            } else {
++                key_url = seg->key;
++            }
++            if (open_url(pls->parent, &pb, key_url, &c->avio_opts, opts, NULL) == 0) {
+                 ret = avio_read(pb, pls->key, sizeof(pls->key));
+                 if (ret != sizeof(pls->key)) {
+                     av_log(pls->parent, AV_LOG_ERROR, "Unable to read key file %s\n",
+@@ -1308,6 +1318,8 @@
+                        seg->key);
+             }
+             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
++            if (key_url != seg->key)
++                av_free(key_url);
+         }
+     }
+@@ -2579,6 +2591,8 @@
+         OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
+     {"seg_max_retry", "Maximum number of times to reload a segment on error.",
+      OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS},
++    { "key_uri_old", "allow to replace part of AES key uri - old", OFFSET(key_uri_replace_old), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
++    { "key_uri_new", "allow to replace part of AES key uri - new", OFFSET(key_uri_replace_new), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
+     {NULL}
+ };
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0005-mips64-cpu-detection.patch b/archive-patches/ffmpeg-arm/6.1.4/0005-mips64-cpu-detection.patch
new file mode 100644 (file)
index 0000000..0a3a2ea
--- /dev/null
@@ -0,0 +1,20 @@
+--- a/configure
++++ b/configure
+@@ -6132,17 +6132,10 @@
+     # Check toolchain ISA level
+     if enabled mips64; then
+-        enabled mips64r6 && check_inline_asm mips64r6 '"dlsa $0, $0, $0, 1"' &&
+-            disable mips64r2
+-
+         enabled mips64r2 && check_inline_asm mips64r2 '"dext $0, $0, 0, 1"'
+         disable mips32r6 && disable mips32r5 && disable mips32r2
+     else
+-        enabled mips32r6 && check_inline_asm mips32r6 '"aui $0, $0, 0"' &&
+-            disable mips32r5 && disable mips32r2
+-
+-        enabled mips32r5 && check_inline_asm mips32r5 '"eretnc"'
+         enabled mips32r2 && check_inline_asm mips32r2 '"ext $0, $0, 0, 1"'
+         disable mips64r6 && disable mips64r5 && disable mips64r2
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0006-optimize-aac.patch b/archive-patches/ffmpeg-arm/6.1.4/0006-optimize-aac.patch
new file mode 100644 (file)
index 0000000..d7044a4
--- /dev/null
@@ -0,0 +1,22 @@
+--- a/libavcodec/aacdec_template.c
++++ b/libavcodec/aacdec_template.c
+@@ -2520,7 +2520,7 @@
+  * @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
++++ b/libavcodec/aacps.c
+@@ -397,7 +397,7 @@ static void map_val_20_to_34(INTFLOAT par[PS_MAX_NR_IIDICC])
+     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]);
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0007-increase-buffer-size.patch b/archive-patches/ffmpeg-arm/6.1.4/0007-increase-buffer-size.patch
new file mode 100644 (file)
index 0000000..9a6b05d
--- /dev/null
@@ -0,0 +1,31 @@
+--- a/libavformat/aviobuf.c
++++ b/libavformat/aviobuf.c
+@@ -34,7 +34,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
+@@ -580,16 +580,15 @@
+     }
+     /* make buffer smaller in case it ended up large after probing */
+-    if (s->read_packet && ctx->orig_buffer_size &&
+-        s->buffer_size > ctx->orig_buffer_size  && len >= ctx->orig_buffer_size) {
++    if (s->read_packet && s->buffer_size > max_buffer_size) {
+         if (dst == s->buffer && s->buf_ptr != dst) {
+-            int ret = set_buf_size(s, ctx->orig_buffer_size);
++            int ret = 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;
+         }
+-        len = ctx->orig_buffer_size;
++        len = max_buffer_size;
+     }
+     len = read_packet_wrapper(s, dst, len);
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0008-recheck-discard-flags.patch b/archive-patches/ffmpeg-arm/6.1.4/0008-recheck-discard-flags.patch
new file mode 100644 (file)
index 0000000..04eef54
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -2298,8 +2298,10 @@
+     HLSContext *c = s->priv_data;
+     int ret, i, minplaylist = -1;
+-    recheck_discard_flags(s, c->first_packet);
+-    c->first_packet = 0;
++    if (c->first_packet) {
++        recheck_discard_flags(s, 1);
++        c->first_packet = 0;
++    }
+     for (i = 0; i < c->n_playlists; i++) {
+         struct playlist *pls = c->playlists[i];
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch b/archive-patches/ffmpeg-arm/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch
new file mode 100644 (file)
index 0000000..2451bc0
--- /dev/null
@@ -0,0 +1,13 @@
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -3966,8 +3966,10 @@
+             if (ctts_data_old && ctts_index_old < ctts_count_old) {
+                 curr_ctts = ctts_data_old[ctts_index_old].duration;
++                /*
+                 av_log(mov->fc, AV_LOG_TRACE, "stts: %"PRId64" ctts: %"PRId64", ctts_index: %"PRId64", ctts_count: %"PRId64"\n",
+                        curr_cts, curr_ctts, ctts_index_old, ctts_count_old);
++                */
+                 curr_cts += curr_ctts;
+                 ctts_sample_old++;
+                 if (ctts_sample_old == ctts_data_old[ctts_index_old].count) {
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0010-ffmpeg-replay_dash.patch b/archive-patches/ffmpeg-arm/6.1.4/0010-ffmpeg-replay_dash.patch
new file mode 100644 (file)
index 0000000..ffb6eb4
--- /dev/null
@@ -0,0 +1,12 @@
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -1400,6 +1400,9 @@
+                 } else {
+                     num = pls->first_seq_no + (((c->publish_time - c->time_shift_buffer_depth + pls->fragment_duration) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+                 }
++            } else if (c->period_start && c->availability_start_time && pls->fragment_timescale) {
++                              num = pls->first_seq_no + ((( get_current_time_in_sec() - (c->availability_start_time + c->period_start)) * pls->fragment_timescale) - pls->fragment_duration)/ pls->fragment_duration;
++
+             } else {
+                 num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+             }
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0011-rtsp.patch b/archive-patches/ffmpeg-arm/6.1.4/0011-rtsp.patch
new file mode 100644 (file)
index 0000000..cb96bdc
--- /dev/null
@@ -0,0 +1,53 @@
+--- a/libavformat/rtsp.c
++++ b/libavformat/rtsp.c
+@@ -2392,6 +2392,8 @@ static int sdp_read_header(AVFormatContext *s)
+     int i, err;
+     char url[MAX_URL_SIZE];
+     AVBPrint bp;
++    const char *p, *sp="", *sources="", *sp2, *sources2;
++    char sources_buf[1024];
+     if (!ff_network_init())
+         return AVERROR(EIO);
+@@ -2413,6 +2415,16 @@ static int sdp_read_header(AVFormatContext *s)
+     av_bprint_finalize(&bp, NULL);
+     if (err) goto fail;
++    /* Search for sources= tag in original URL for rtp protocol only */
++    if (strncmp(s->url, "rtp://", 6) == 0) {
++        p = strchr(s->url, '?');
++        if (p && av_find_info_tag(sources_buf, sizeof(sources_buf), "sources", p)) {
++            /* av_log(s, AV_LOG_VERBOSE, "sdp_read_header found sources %s\n", sources_buf);  */
++            sp = sources_buf;
++            sources = "&sources=";
++        }
++    }
++
+     /* open each RTP stream */
+     for (i = 0; i < rt->nb_rtsp_streams; i++) {
+         char namebuf[50];
+@@ -2432,12 +2445,22 @@ static int sdp_read_header(AVFormatContext *s)
+                 av_dict_free(&opts);
+                 goto fail;
+             }
++
++            /* Prepare to add sources to the url to be opened.
++               Otherwise the join to the source specific muliticast will be missing */
++            sources2 = sources;
++            sp2 = sp;
++            /* ignore sources from original URL, when sources are already set in rtsp_st */
++            if (rtsp_st->nb_include_source_addrs > 0)
++                sources2 = sp2 = "";
++
+             ff_url_join(url, sizeof(url), "rtp", NULL,
+                         namebuf, rtsp_st->sdp_port,
+-                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d",
++                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d%s%s",
+                         rtsp_st->sdp_port, rtsp_st->sdp_ttl,
+                         rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0,
+-                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0);
++                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0,
++                        sources2, sp2);
+             p = strchr(s->url, '?');
+             if (p && av_find_info_tag(buf, sizeof(buf), "localaddr", p))
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0012-dxva2.patch b/archive-patches/ffmpeg-arm/6.1.4/0012-dxva2.patch
new file mode 100644 (file)
index 0000000..4e9dfe8
--- /dev/null
@@ -0,0 +1,107 @@
+--- a/libavcodec/dxva2.c
++++ b/libavcodec/dxva2.c
+@@ -777,16 +777,18 @@
+ #if CONFIG_D3D11VA
+     if (avctx->pix_fmt == AV_PIX_FMT_D3D11)
+         return (intptr_t)frame->data[1];
+-    if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) {
++    if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD && surface) {
+         D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
+         ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc);
+         return viewDesc.Texture2D.ArraySlice;
+     }
+ #endif
+ #if CONFIG_DXVA2
+-    for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) {
+-        if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD && ctx->dxva2.surface[i] == surface)
+-            return i;
++    if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
++        for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) {
++            if (ctx->dxva2.surface[i] == surface)
++                return i;
++        }
+     }
+ #endif
+--- a/libavcodec/dxva2_h264.c
++++ b/libavcodec/dxva2_h264.c
+@@ -507,6 +507,14 @@
+     if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0)
+         return -1;
++
++    // Wait for an I-frame before start decoding. Workaround for ATI UVD and UVD+ GPUs
++    if (!h->got_first_iframe) {
++        if (!(ctx_pic->pp.wBitFields & (1 << 15)))
++            return -1;
++        h->got_first_iframe = 1;
++    }
++
+     ret = ff_dxva2_common_end_frame(avctx, h->cur_pic_ptr->f,
+                                     &ctx_pic->pp, sizeof(ctx_pic->pp),
+                                     &ctx_pic->qm, sizeof(ctx_pic->qm),
+--- a/libavcodec/h264_slice.c
++++ b/libavcodec/h264_slice.c
+@@ -978,6 +978,7 @@
+     h->first_field           = 0;
+     h->prev_interlaced_frame = 1;
++    h->got_first_iframe = 0;
+     init_scan_tables(h);
+     ret = ff_h264_alloc_tables(h);
+--- a/libavcodec/h264dec.c
++++ b/libavcodec/h264dec.c
+@@ -456,6 +456,7 @@
+     h->next_outputed_poc = INT_MIN;
+     h->prev_interlaced_frame = 1;
++    h->got_first_iframe = 0;
+     idr(h);
+     h->poc.prev_frame_num = -1;
+--- a/libavcodec/h264dec.h
++++ b/libavcodec/h264dec.h
+@@ -533,6 +533,8 @@
+      * slices) anymore */
+     int setup_finished;
++    int got_first_iframe;
++
+     int cur_chroma_format_idc;
+     int cur_bit_depth_luma;
+     int16_t slice_row[MAX_SLICES]; ///< to detect when MAX_SLICES is too low
+--- a/libavcodec/vaapi_h264.c
++++ b/libavcodec/vaapi_h264.c
+@@ -314,6 +314,11 @@
+     H264SliceContext *sl = &h->slice_ctx[0];
+     int ret;
++    if (pic->nb_slices == 0) {
++        ret = AVERROR_INVALIDDATA;
++        goto finish;
++    }
++
+     ret = ff_vaapi_decode_issue(avctx, pic);
+     if (ret < 0)
+         goto finish;
+--- a/libavformat/bintext.c
++++ b/libavformat/bintext.c
+@@ -149,7 +149,7 @@
+             return AVPROBE_SCORE_EXTENSION + 1;
+         predict_width(&par, p->buf_size, got_width);
+-        if (par.width < 8)
++        if (par.width <= 0)
+             return 0;
+         calculate_height(&par, p->buf_size);
+         if (par.height <= 0)
+@@ -189,8 +189,6 @@
+             next_tag_read(s, &bin->fsize);
+         if (!bin->width) {
+             predict_width(st->codecpar, bin->fsize, got_width);
+-            if (st->codecpar->width < 8)
+-                return AVERROR_INVALIDDATA;
+             calculate_height(st->codecpar, bin->fsize);
+         }
+         avio_seek(pb, 0, SEEK_SET);
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch b/archive-patches/ffmpeg-arm/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch
new file mode 100644 (file)
index 0000000..7ddf525
--- /dev/null
@@ -0,0 +1,29 @@
+--- a/libavformat/avformat.h
++++ b/libavformat/avformat.h
+@@ -1030,6 +1030,10 @@
+ int64_t    av_stream_get_end_pts(const AVStream *st);
+ #endif
++// Chromium: We use the internal field first_dts vvv
++int64_t    av_stream_get_first_dts(const AVStream *st);
++// Chromium: We use the internal field first_dts ^^^
++
+ #define AV_PROGRAM_RUNNING 1
+ /**
+--- a/libavformat/utils.c
++++ b/libavformat/utils.c
+@@ -100,6 +100,13 @@
+     return pkt->size > orig_size ? pkt->size - orig_size : ret;
+ }
++// Chromium: We use the internal field first_dts vvv
++int64_t av_stream_get_first_dts(const AVStream *st)
++{
++  return cffstream(st)->first_dts;
++}
++// Chromium: We use the internal field first_dts ^^^
++
+ int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
+ {
+ #if FF_API_INIT_PACKET
diff --git a/archive-patches/ffmpeg-arm/6.1.4/0014-ffmpeg-whitelist.patch b/archive-patches/ffmpeg-arm/6.1.4/0014-ffmpeg-whitelist.patch
new file mode 100644 (file)
index 0000000..69e520a
--- /dev/null
@@ -0,0 +1,17 @@
+--- a/libavformat/avio.c
++++ b/libavformat/avio.c
+@@ -171,12 +171,12 @@
+                (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-arm/6.1.4/0015-ffmpeg-support-kid-key.patch b/archive-patches/ffmpeg-arm/6.1.4/0015-ffmpeg-support-kid-key.patch
new file mode 100644 (file)
index 0000000..3b4a622
--- /dev/null
@@ -0,0 +1,454 @@
+--- a/doc/demuxers.texi
++++ b/doc/demuxers.texi
+@@ -281,7 +281,11 @@
+ @table @option
+ @item cenc_decryption_key
+-16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++Default 16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++
++ at item cenc_decryption_keys
++Dictionary of 16-byte key ID => 16-byte key, both in hex, to decrypt files encrypted using ISO Common Encryption
++(CENC/AES-128 CTR; ISO/IEC 23001-7).
+ @end table
+@@ -769,7 +773,11 @@
+ specify.
+ @item decryption_key
+-16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++Default 16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++
++ at item decryption_keys
++Dictionary of 16-byte key ID => 16-byte key, both in hex, to decrypt files encrypted using ISO Common Encryption
++(CENC/AES-128 CTR; ISO/IEC 23001-7).
+ @item max_stts_delta
+ Very high sample deltas written in a trak's stts box may occasionally be intended but usually they are written in
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -153,6 +153,7 @@
+     AVDictionary *avio_opts;
+     int max_url_size;
+     char *cenc_decryption_key;
++    char *cenc_decryption_keys;
+     /* Flags for init section*/
+     int is_init_section_common_video;
+@@ -1906,6 +1907,8 @@
+     if (c->cenc_decryption_key)
+         av_dict_set(&in_fmt_opts, "decryption_key", c->cenc_decryption_key, 0);
++    if (c->cenc_decryption_keys)
++        av_dict_set(&in_fmt_opts, "decryption_keys", c->cenc_decryption_keys, 0);
+     // provide additional information from mpd if available
+     ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
+@@ -2347,7 +2350,8 @@
+         OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+         {.str = "aac,m4a,m4s,m4v,mov,mp4,webm,ts"},
+         INT_MIN, INT_MAX, FLAGS},
+-    { "cenc_decryption_key", "Media decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
++    { "cenc_decryption_key", "Media default decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
++    { "cenc_decryption_keys", "Media decryption keys by KID (hex)", OFFSET(cenc_decryption_keys), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
+     {NULL}
+ };
+--- a/libavformat/isom.h
++++ b/libavformat/isom.h
+@@ -312,8 +312,8 @@
+     void *audible_iv;
+     int audible_iv_size;
+     struct AVAES *aes_decrypt;
+-    uint8_t *decryption_key;
+-    int decryption_key_len;
++    uint8_t *decryption_default_key;
++    int decryption_default_key_len;
+     int enable_drefs;
+     int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
+     int have_read_mfra_size;
+@@ -328,6 +328,7 @@
+     } *avif_info;
+     int avif_info_size;
+     int interleaved_read;
++    AVDictionary* decryption_keys;
+ } MOVContext;
+ int ff_mp4_read_descr_len(AVIOContext *pb);
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -7130,19 +7130,62 @@
+     return 0;
+ }
++static int get_key_from_kid(uint8_t* out, int len, MOVContext *c, AVEncryptionInfo *sample) {
++    AVDictionaryEntry *key_entry_hex;
++    char kid_hex[16*2+1];
++
++    if (c->decryption_default_key && c->decryption_default_key_len != len) {
++        av_log(c->fc, AV_LOG_ERROR, "invalid default decryption key length: got %d, expected %d\n", c->decryption_default_key_len, len);
++        return -1;
++    }
++
++    if (!c->decryption_keys) {
++        av_assert0(c->decryption_default_key);
++        memcpy(out, c->decryption_default_key, len);
++        return 0;
++    }
++
++    if (sample->key_id_size != 16) {
++        av_log(c->fc, AV_LOG_ERROR, "invalid key ID size: got %u, expected 16\n", sample->key_id_size);
++        return -1;
++    }
++
++    ff_data_to_hex(kid_hex, sample->key_id, 16, 1);
++    key_entry_hex = av_dict_get(c->decryption_keys, kid_hex, NULL, AV_DICT_DONT_STRDUP_KEY|AV_DICT_DONT_STRDUP_VAL);
++    if (!key_entry_hex) {
++        if (!c->decryption_default_key) {
++            av_log(c->fc, AV_LOG_ERROR, "unable to find KID %s\n", kid_hex);
++            return -1;
++        }
++        memcpy(out, c->decryption_default_key, len);
++        return 0;
++    }
++    if (strlen(key_entry_hex->value) != len*2) {
++        return -1;
++    }
++    ff_hex_to_data(out, key_entry_hex->value);
++    return 0;
++}
++
+ static int cenc_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+ {
+     int i, ret;
+     int bytes_of_protected_data;
++    uint8_t decryption_key[AES_CTR_KEY_SIZE];
+     if (!sc->cenc.aes_ctr) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctr = av_aes_ctr_alloc();
+         if (!sc->cenc.aes_ctr) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
++        ret = av_aes_ctr_init(sc->cenc.aes_ctr, decryption_key);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7188,15 +7231,21 @@
+     int i, ret;
+     int num_of_encrypted_blocks;
+     uint8_t iv[16];
++    uint8_t decryption_key[16];
+     if (!sc->cenc.aes_ctx) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctx = av_aes_alloc();
+         if (!sc->cenc.aes_ctx) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
++        ret = av_aes_init(sc->cenc.aes_ctx, decryption_key, 16 * 8, 1);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7247,15 +7296,21 @@
+ {
+     int i, ret, rem_bytes;
+     uint8_t *data;
++    uint8_t decryption_key[AES_CTR_KEY_SIZE];
+     if (!sc->cenc.aes_ctr) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctr = av_aes_ctr_alloc();
+         if (!sc->cenc.aes_ctr) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
++        ret = av_aes_ctr_init(sc->cenc.aes_ctr, decryption_key);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7313,15 +7368,21 @@
+     int i, ret, rem_bytes;
+     uint8_t iv[16];
+     uint8_t *data;
++    uint8_t decryption_key[16];
+     if (!sc->cenc.aes_ctx) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctx = av_aes_alloc();
+         if (!sc->cenc.aes_ctx) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
++        ret = av_aes_init(sc->cenc.aes_ctx, decryption_key, 16 * 8, 1);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7464,7 +7525,7 @@
+             return AVERROR_INVALIDDATA;
+         }
+-        if (mov->decryption_key) {
++        if (mov->decryption_keys || mov->decryption_default_key) {
+             return cenc_decrypt(mov, sc, encrypted_sample, pkt->data, pkt->size);
+         } else {
+             size_t size;
+@@ -8613,12 +8674,6 @@
+     MOVAtom atom = { AV_RL32("root") };
+     int i;
+-    if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
+-        av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
+-            mov->decryption_key_len, AES_CTR_KEY_SIZE);
+-        return AVERROR(EINVAL);
+-    }
+-
+     mov->fc = s;
+     mov->trak_index = -1;
+     /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
+@@ -9317,7 +9372,8 @@
+         "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
+         AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
+         .flags = AV_OPT_FLAG_DECODING_PARAM },
+-    { "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
++    { "decryption_key", "The default media decryption key (hex)", OFFSET(decryption_default_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
++    { "decryption_keys", "The media decryption keys by KID (hex)", OFFSET(decryption_keys), AV_OPT_TYPE_DICT, .flags = AV_OPT_FLAG_DECODING_PARAM },
+     { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
+         {.i64 = 0}, 0, 1, FLAGS },
+     { "max_stts_delta", "treat offsets above this value as invalid", OFFSET(max_stts_delta), AV_OPT_TYPE_INT, {.i64 = UINT_MAX-48000*10 }, 0, UINT_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM },
+--- a/tests/fate/mov.mak
++++ b/tests/fate/mov.mak
+@@ -8,6 +8,9 @@
+            fate-mov-3elist-encrypted \
+            fate-mov-frag-encrypted \
+            fate-mov-tenc-only-encrypted \
++           fate-mov-3elist-encrypted-kid \
++           fate-mov-frag-encrypted-kid \
++           fate-mov-tenc-only-encrypted-kid \
+            fate-mov-invalid-elst-entry-count \
+            fate-mov-gpmf-remux \
+            fate-mov-440hz-10ms \
+@@ -57,6 +60,15 @@ fate-mov-frag-encrypted: CMD = framemd5 -decryption_key 123456789012345678901234
+ # Full-sample encryption and constant IV using only tenc atom (no senc/saio/saiz).
+ fate-mov-tenc-only-encrypted: CMD = framemd5 -decryption_key 12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-tenc-only-encrypted.mp4
++# Edit list with encryption, using the decryption_keys option.
++fate-mov-3elist-encrypted-kid: CMD = framemd5 -decryption_keys 12345678901234567890123456789012=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-3elist-encrypted.mov
++
++# Fragmented encryption with senc boxes in movie fragments, using the decryption_keys option.
++fate-mov-frag-encrypted-kid: CMD = framemd5 -decryption_keys abba271e8bcf552bbd2e86a434a9a5d9=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-frag-encrypted.mp4
++
++# Full-sample encryption and constant IV using only tenc atom (no senc/saio/saiz), using the decryption_keys option.
++fate-mov-tenc-only-encrypted-kid: CMD = framemd5 -decryption_keys abba271e8bcf552bbd2e86a434a9a5d9=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-tenc-only-encrypted.mp4
++
+ # Makes sure that the CTTS is also modified when we fix avindex in mov.c while parsing edit lists.
+ fate-mov-elist-starts-ctts-2ndsample: CMD = framemd5 -i $(TARGET_SAMPLES)/mov/mov-elist-starts-ctts-2ndsample.mov
+--- /dev/null
++++ b/tests/ref/fate/mov-3elist-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 640x480
++#sar 0: 0/1
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,   460800, 80fbbdec589e15e6c493b44d243f92a9
++0,          1,          1,        1,   460800, f4b23293bb2ecf69cc3570853d8c56a1
++0,          2,          2,        1,   460800, 0c03ce2c1c6ec405d7455465ecd559a3
++0,          3,          3,        1,   460800, 7921791695537fba2c3c123da4834cb9
++0,          4,          4,        1,   460800, 30c8e2903a561b84d4cbaf95c668d236
++0,          5,          5,        1,   460800, 7ff42e998217c17592ddf6b584f26cef
++0,          6,          6,        1,   460800, 5e402c48bf097db2d31b82bb4194a382
++0,          7,          7,        1,   460800, 824c49e92c8ae6d99a0207b514dd756c
++0,          8,          8,        1,   460800, 24f189216a1d9cf2313b2d6dbe3dbdd3
++0,          9,          9,        1,   460800, 519179a8e74275d26b183374637e003f
++0,         10,         10,        1,   460800, f18331ddcef0adf5b069bfa98baf8db4
++0,         11,         11,        1,   460800, 081f61688690d47dbdddd5384e5d5a70
++0,         12,         12,        1,   460800, 90dbf019b9035433371a8df41a9268b7
++0,         13,         13,        1,   460800, bb5adfb9c66732898b34186eca1667ba
++0,         14,         14,        1,   460800, cc08cfd64f37783ecddaf143f6ad78bc
++0,         15,         15,        1,   460800, b8ae21d024fe4df903d56f4521993c72
++0,         16,         16,        1,   460800, b45a99907f045dcadf0a2befc11555e3
++0,         17,         17,        1,   460800, 603ba935845e65ab6cccbbec88bbf60d
++0,         18,         18,        1,   460800, df80c8d3e6a77258a306903f17995a18
++0,         19,         19,        1,   460800, 4b7e90c0a5fd0e0cd958d47f0afac636
++0,         20,         20,        1,   460800, 9feb6e36182f1745be6387edea240eb6
++0,         21,         21,        1,   460800, 86e6de4bd0a5ff7558f4cf6c1ec3930d
++0,         22,         22,        1,   460800, 726b69df77edbe7b503d4698656d1320
++0,         23,         23,        1,   460800, d282fb7a953ac205b0a43d00c2d60a33
++0,         24,         24,        1,   460800, eece3daa70cc20208dd75d91ac84c8fd
++0,         25,         25,        1,   460800, c86d23e73bcce351fc315fb1f13348da
++0,         26,         26,        1,   460800, 93497b4f7c5ad9d61212239b7c9d2770
++0,         27,         27,        1,   460800, eb217d2c12de67903835a8c58f620488
++0,         28,         28,        1,   460800, d966480867bb54c8cd044f18388ed486
++0,         29,         29,        1,   460800, 3ea6207942b3181fdd8e8aa6cae1062a
++0,         30,         30,        1,   460800, 2620df54aca086ec0fb9527c6e6f5135
++0,         31,         31,        1,   460800, 43bb7320f0bb583188dc965ddbfade90
++0,         32,         32,        1,   460800, 0cddaa04645f804e02f65b0836412113
++0,         33,         33,        1,   460800, 83b2dc95807289d7f4a4632bf18c2e97
++0,         34,         34,        1,   460800, 98134d0e41e6dd12827049ccf33b4669
++0,         35,         35,        1,   460800, 56f55631731fa39c7acbab0afeb2eb1b
++0,         36,         36,        1,   460800, 379c1105be09d836a515dc909455ddf4
++0,         37,         37,        1,   460800, 1df87c47e9d98731faf1c3885b77e5da
++0,         38,         38,        1,   460800, 9a8734bcbfdb4d97e530683b8b556a26
++0,         39,         39,        1,   460800, c7a7990d0cddc5adfbe27da7a42e025e
++0,         40,         40,        1,   460800, 0c81e46011e03be410feaf056207fd55
++0,         41,         41,        1,   460800, ca76e4e63016ff29d8aeeb9cb053bb6c
++0,         42,         42,        1,   460800, cebfbe299c17c1f8fc1e6b189555c3c2
++0,         43,         43,        1,   460800, 4f002c5feca5e75f07089e0df47507dd
++0,         44,         44,        1,   460800, c5fd83fc4a745abee9b3d9a6eec9dd3e
++0,         45,         45,        1,   460800, 57d9bad9b45aa2746de5d8bdc2c24969
++0,         46,         46,        1,   460800, 9831673ad7dec167af4a959f64258949
++0,         47,         47,        1,   460800, 77a1cb208f70f51bcb01e28d8cba73b4
+--- /dev/null
++++ b/tests/ref/fate/mov-frag-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 120x52
++#sar 0: 544/545
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,     9360, 920bdc277a6a31c1daed9aca44b10caf
++0,          1,          1,        1,     9360, f1c0b61fef593de57cb97be7fa846569
++0,          2,          2,        1,     9360, 6ef32d9d4398355aebf6d3fb11d51d3f
++0,          3,          3,        1,     9360, d38fd3ef1e5a92fc109b8dd9eb6dadeb
++0,          4,          4,        1,     9360, 54cc0c8a25d2f14f32663837d5e646f1
++0,          5,          5,        1,     9360, b4b6829726dc3decb8b80ba0c35bcf30
++0,          6,          6,        1,     9360, fca3f941e60a2f0a4ce30d5e0efbec3c
++0,          7,          7,        1,     9360, cda6e26b6c1039ff3d229b262c9210c3
++0,          8,          8,        1,     9360, f0d69255e3a27a8b4ae8a4b7b210929d
++0,          9,          9,        1,     9360, 12cb23dd4e32af9c3b35f943714e3fdd
++0,         10,         10,        1,     9360, 082aaf3216124ddcecb422fe5c832e82
++0,         11,         11,        1,     9360, ff37bb8cd6bd0412a3b3cb45db54afc9
++0,         12,         12,        1,     9360, dfb9085441575732844b6c2f05d5f542
++0,         13,         13,        1,     9360, 0017100feaaa9fc7eacd2447d50d7542
++0,         14,         14,        1,     9360, 4e2f1b8c4e04c59934c2f58541e62613
++0,         15,         15,        1,     9360, 27a44dfea7cd2d30e488194c34ab473c
++0,         16,         16,        1,     9360, fc7b56bd95e990a33cf575d1ef820902
++0,         17,         17,        1,     9360, fa2d1609e69714dffc410e65f3c8b755
++0,         18,         18,        1,     9360, 705d7429f447cb13febe202d567795f2
++0,         19,         19,        1,     9360, 234802ce86e868faaf2cd40a286846ea
++0,         20,         20,        1,     9360, 2f0354b40d211d0a4ade4568bea4f85e
++0,         21,         21,        1,     9360, e96af3b6c0cc931463ca77d6be0f1148
++0,         22,         22,        1,     9360, 04a904d798361959971361401879c7e4
++0,         23,         23,        1,     9360, 2f119642340df6d25362b5590ded46b7
++0,         24,         24,        1,     9360, 5993fca2e60050706f857ac76e48f386
++0,         25,         25,        1,     9360, 2ff3b5775fed3d527bfbbeea786787fe
++0,         26,         26,        1,     9360, 42024dbe23d3fb5b0d8987ae1ce390a8
++0,         27,         27,        1,     9360, d804204f0bd9db5f6a758e2c934d9e38
++0,         28,         28,        1,     9360, e322712e6e34c58ec1a2ab5e2c1e3bfe
++0,         29,         29,        1,     9360, 3975bd1a5f6a6b6260276777f9de611e
++0,         30,         30,        1,     9360, 4388f0412efc6310706a7cdedc859ea9
++0,         31,         31,        1,     9360, b4b9a11b0b86635267345a569640e8d4
++0,         32,         32,        1,     9360, 31879c7b8d6b67a4209ffde786bb8cb4
++0,         33,         33,        1,     9360, 4b6dc02d7c889fe4abd4e013b25f585a
++0,         34,         34,        1,     9360, dc73aae82bd39a1220d1106c8d3e8252
++0,         35,         35,        1,     9360, 54c7dfbd49f312806f6c1a89f7c2c36f
++0,         36,         36,        1,     9360, 150abc64f8994d444a521ea90570443c
++0,         37,         37,        1,     9360, d277cdc7dcadbe0016f2e950459e7ebf
++0,         38,         38,        1,     9360, 2196bf338ead90ea54687b85c73c8229
++0,         39,         39,        1,     9360, 53ce5da5365abc0bd3217dd98e7c465d
++0,         40,         40,        1,     9360, 34ee9832aea55c0c4e6f4381c413c10e
++0,         41,         41,        1,     9360, 1769c7b5849e4681119067a06ac29a4f
++0,         42,         42,        1,     9360, 71f53df739ef283a5184c91ef4b158e8
++0,         43,         43,        1,     9360, d2d394739e9a59c06f0354c16843cb63
++0,         44,         44,        1,     9360, d8e458e92ae29344505a24a3059fc584
++0,         45,         45,        1,     9360, 0f1b11a09911851b798df2ef76253a7f
++0,         46,         46,        1,     9360, 5c4a9f22baecf4e749c0d5c65a4f1007
++0,         47,         47,        1,     9360, 3e2b7e7262fdca08d9d1ef6070125c4b
+--- /dev/null
++++ b/tests/ref/fate/mov-tenc-only-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 1024x436
++#sar 0: 1/1
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,   669696, f48f296a85eda5ba069dc851a3228bef
++0,          1,          1,        1,   669696, a50c5f69bfa3387d49b5bdf738e6529c
++0,          2,          2,        1,   669696, 05061299003760f6a4795b408f72aa31
++0,          3,          3,        1,   669696, 2572119f0b0cdd83f8a7e06252cecd3b
++0,          4,          4,        1,   669696, 29fe6a6bdb4a69018e318886a297f07e
++0,          5,          5,        1,   669696, e8233c7fbaecfbff965c7dfdd3982b1b
++0,          6,          6,        1,   669696, d9259df9880ff5d4a4b38282e67f407b
++0,          7,          7,        1,   669696, 3e8d795195038993503ea9ab6984c915
++0,          8,          8,        1,   669696, bc4e2d253b715a34f85aae1b080e3460
++0,          9,          9,        1,   669696, 09aba8b3a96f53f9268e7420a10bfab6
++0,         10,         10,        1,   669696, 179447977dd580da8b35fb5310a809ca
++0,         11,         11,        1,   669696, 7a0eea9d54577990345f5705ab9882be
++0,         12,         12,        1,   669696, 5bb96eb76f461825740e5938456df759
++0,         13,         13,        1,   669696, bd4ac4a760ead774b9422a27dc071964
++0,         14,         14,        1,   669696, 1cc05f760a9b751fc89e77f2bcc97259
++0,         15,         15,        1,   669696, 825d0dee6f0174ba7102892c7de30b4d
++0,         16,         16,        1,   669696, d26a2ef5267f6bb03c4e1d8514eee0df
++0,         17,         17,        1,   669696, c916ffdeadca76596a8f7fd47914b5ef
++0,         18,         18,        1,   669696, 6e085acfa7fee0658ea0ae6188274c17
++0,         19,         19,        1,   669696, 1e95fa5b3561283f05bf0bd44cb91721
++0,         20,         20,        1,   669696, 37e3d135aba9dfb8b87e441753115374
++0,         21,         21,        1,   669696, 9c398310e8564491de624393c16265ce
++0,         22,         22,        1,   669696, c87209e4d2617bc2ab40a75f455f09da
++0,         23,         23,        1,   669696, 2679c2f8d1d1af21982e245945c1ee60
++0,         24,         24,        1,   669696, 6151ab4781f31c5beb66b356ad547122
++0,         25,         25,        1,   669696, f7ef6293bfb3a6a329061cb6a5ed5a38
++0,         26,         26,        1,   669696, 2f6e666d14dfc407ca0c0f347b13eb08
++0,         27,         27,        1,   669696, 3454fa1730d79b1aa8dbbc865dc150f4
++0,         28,         28,        1,   669696, e93dc683e2453419a0419ab9af0f8f95
++0,         29,         29,        1,   669696, 031eb3154f7f83cf86d42bee66be9cf7
++0,         30,         30,        1,   669696, 1205c36723e88811206c68892d3aaed6
++0,         31,         31,        1,   669696, 7dd7a8a19dcd73b31ddc6a6d0c597a42
++0,         32,         32,        1,   669696, 7c91115368ea2531262a1197468bc3f4
++0,         33,         33,        1,   669696, 3cf6d9ba385e0fff76da33299ed5380c
++0,         34,         34,        1,   669696, 859fc8c3ef049e3c1175a85fb0a90a3d
++0,         35,         35,        1,   669696, 1d09ce6c7027103d99a4d5799f6e72ab
++0,         36,         36,        1,   669696, 3dcb8357408ac88abd734128d8f5dd6f
++0,         37,         37,        1,   669696, 4dafce137a0a5178f6efaec878e64d36
++0,         38,         38,        1,   669696, 44c478f29a1399ed03275a7357f57d48
++0,         39,         39,        1,   669696, 6e9edaac7414c0e14591ac3d4d0b1ac4
++0,         40,         40,        1,   669696, 522e4aaeea0825da27f631a9e690d654
++0,         41,         41,        1,   669696, 85f2502a718440834c40051d30f8a65e
++0,         42,         42,        1,   669696, ae8816f7bd4645ef1a17ee6d09b4c8d2
++0,         43,         43,        1,   669696, 914b006fa92f1eb3e590245749f6810d
++0,         44,         44,        1,   669696, 9406901542e94c429dff46108782ed69
++0,         45,         45,        1,   669696, 324c13641c39eef5c476023e358c0391
++0,         46,         46,        1,   669696, 4058e886e17c22e4eb9da1dd0d6ad891
++0,         47,         47,        1,   669696, 9edf9cd15eea985b42fd1f5035b1d693
+-- 
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0001-aac-optimize0.patch b/archive-patches/ffmpeg-cst/5.1.4/0001-aac-optimize0.patch
new file mode 100644 (file)
index 0000000..ff81c27
--- /dev/null
@@ -0,0 +1,48 @@
+diff --git a/libavcodec/aacps.c b/libavcodec/aacps.c
+index 655e8fe5b4..45190d678d 100644
+--- a/libavcodec/aacps.c
++++ b/libavcodec/aacps.c
+@@ -397,7 +397,7 @@ static void map_val_20_to_34(INTFLOAT par[PS_MAX_NR_IIDICC])
+     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]);
+diff --git a/libavcodec/fft_template.c b/libavcodec/fft_template.c
+index f2742a3ae8..59b4085eba 100644
+--- a/libavcodec/fft_template.c
++++ b/libavcodec/fft_template.c
+@@ -551,7 +551,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;
+@@ -565,7 +565,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;
+diff --git a/libavcodec/mdct_template.c b/libavcodec/mdct_template.c
+index a854ad2700..6119be0d1a 100644
+--- a/libavcodec/mdct_template.c
++++ b/libavcodec/mdct_template.c
+@@ -98,7 +98,7 @@ av_cold int ff_mdct_init(FFTContext *s, int nbits, int inverse, double scale)
+  * @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/5.1.4/0002-add64_c-parentheses.patch b/archive-patches/ffmpeg-cst/5.1.4/0002-add64_c-parentheses.patch
new file mode 100644 (file)
index 0000000..38b74bf
--- /dev/null
@@ -0,0 +1,14 @@
+diff --git a/libavutil/common.h b/libavutil/common.h
+index fd1404be6c..0bb8650e83 100644
+--- a/libavutil/common.h
++++ b/libavutil/common.h
+@@ -350,7 +350,8 @@ static av_always_inline int64_t av_sat_add64_c(int64_t a, int64_t b) {
+     return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
+ #else
+     int64_t s = a+(uint64_t)b;
+-    if ((int64_t)(a^b | ~s^b) >= 0)
++    // PATCH: GCC 4.9.4 complains about missing parentheses when combining bitwise operations.
++    if ((int64_t)((a^b) | (~s^b)) >= 0)
+         return INT64_MAX ^ (b >> 63);
+     return s;
+ #endif
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0003-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch b/archive-patches/ffmpeg-cst/5.1.4/0003-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch
new file mode 100644 (file)
index 0000000..86bdb91
--- /dev/null
@@ -0,0 +1,333 @@
+diff --git a/libavcodec/Makefile b/libavcodec/Makefile
+index 457ec58377..49bf260764 100644
+--- a/libavcodec/Makefile
++++ b/libavcodec/Makefile
+@@ -1209,6 +1209,8 @@ OBJS-$(CONFIG_SETTS_BSF)                  += setts_bsf.o
+ OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
+ OBJS-$(CONFIG_TRACE_HEADERS_BSF)          += trace_headers_bsf.o
+ OBJS-$(CONFIG_TRUEHD_CORE_BSF)            += truehd_core_bsf.o mlp_parse.o mlp.o
++OBJS-$(CONFIG_VC1_ASFTORCV_BSF)           += vc1_asftorcv_bsf.o
++OBJS-$(CONFIG_VC1_ASFTOANNEXG_BSF)        += vc1_asftoannexg_bsf.o vc1.o
+ OBJS-$(CONFIG_VP9_METADATA_BSF)           += vp9_metadata_bsf.o
+ OBJS-$(CONFIG_VP9_RAW_REORDER_BSF)        += vp9_raw_reorder_bsf.o
+ OBJS-$(CONFIG_VP9_SUPERFRAME_BSF)         += vp9_superframe_bsf.o
+diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
+index 444423ae93..482d3473b2 100644
+--- a/libavcodec/bitstream_filters.c
++++ b/libavcodec/bitstream_filters.c
+@@ -59,6 +59,8 @@ extern const FFBitStreamFilter ff_setts_bsf;
+ extern const FFBitStreamFilter ff_text2movsub_bsf;
+ extern const FFBitStreamFilter ff_trace_headers_bsf;
+ extern const FFBitStreamFilter ff_truehd_core_bsf;
++extern const FFBitStreamFilter ff_vc1_asftoannexg_bsf;
++extern const FFBitStreamFilter ff_vc1_asftorcv_bsf;
+ extern const FFBitStreamFilter ff_vp9_metadata_bsf;
+ extern const FFBitStreamFilter ff_vp9_raw_reorder_bsf;
+ extern const FFBitStreamFilter ff_vp9_superframe_bsf;
+diff --git a/libavcodec/vc1_asftoannexg_bsf.c b/libavcodec/vc1_asftoannexg_bsf.c
+new file mode 100644
+index 0000000000..2901f84d9c
+--- /dev/null
++++ b/libavcodec/vc1_asftoannexg_bsf.c
+@@ -0,0 +1,190 @@
++/*
++ * 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"
++#include "bsf_internal.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,
++};
++
++FFBitStreamFilter ff_vc1_asftoannexg_bsf = {
++    .p.name         = "vc1_asftoannexg",
++    .priv_data_size = sizeof(ASFTOANNEXGBSFContext),
++    .filter         = asftoannexg_filter,
++    .close          = asftoannexg_close,
++    .p.codec_ids    = codec_ids,
++};
+diff --git a/libavcodec/vc1_asftorcv_bsf.c b/libavcodec/vc1_asftorcv_bsf.c
+new file mode 100644
+index 0000000000..02e510b4b6
+--- /dev/null
++++ b/libavcodec/vc1_asftorcv_bsf.c
+@@ -0,0 +1,105 @@
++/*
++ * 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"
++#include "bsf_internal.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,
++};
++
++FFBitStreamFilter ff_vc1_asftorcv_bsf = {
++    .p.name       = "vc1_asftorcv",
++    .priv_data_size = sizeof(ASFTORCVBSFContext),
++    .filter         = asftorcv_filter,
++    .p.codec_ids    = codec_ids,
++};
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0004-cst-allow-to-choose-rtmp-impl-at-runtime.patch b/archive-patches/ffmpeg-cst/5.1.4/0004-cst-allow-to-choose-rtmp-impl-at-runtime.patch
new file mode 100644 (file)
index 0000000..9360812
--- /dev/null
@@ -0,0 +1,132 @@
+diff --git a/configure b/configure
+index ba5793b2ff..b199365427 100755
+--- a/configure
++++ b/configure
+@@ -3549,10 +3549,8 @@ xv_outdev_deps="xlib_xv xlib_x11 xlib_xext"
+ # protocols
+ async_protocol_deps="threads"
+ bluray_protocol_deps="libbluray"
+-ffrtmpcrypt_protocol_conflict="librtmp_protocol"
+ ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls"
+ ffrtmpcrypt_protocol_select="tcp_protocol"
+-ffrtmphttp_protocol_conflict="librtmp_protocol"
+ ffrtmphttp_protocol_select="http_protocol"
+ ftp_protocol_select="tcp_protocol"
+ gopher_protocol_select="tcp_protocol"
+@@ -3566,20 +3564,18 @@ https_protocol_suggest="zlib"
+ icecast_protocol_select="http_protocol"
+ mmsh_protocol_select="http_protocol"
+ mmst_protocol_select="network"
+-rtmp_protocol_conflict="librtmp_protocol"
+-rtmp_protocol_select="tcp_protocol"
+-rtmp_protocol_suggest="zlib"
+-rtmpe_protocol_select="ffrtmpcrypt_protocol"
+-rtmpe_protocol_suggest="zlib"
+-rtmps_protocol_conflict="librtmp_protocol"
+-rtmps_protocol_select="tls_protocol"
+-rtmps_protocol_suggest="zlib"
+-rtmpt_protocol_select="ffrtmphttp_protocol"
+-rtmpt_protocol_suggest="zlib"
+-rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
+-rtmpte_protocol_suggest="zlib"
+-rtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
+-rtmpts_protocol_suggest="zlib"
++ffrtmp_protocol_select="tcp_protocol"
++ffrtmp_protocol_suggest="zlib"
++ffrtmpe_protocol_select="ffrtmpcrypt_protocol"
++ffrtmpe_protocol_suggest="zlib"
++ffrtmps_protocol_select="tls_protocol"
++ffrtmps_protocol_suggest="zlib"
++ffrtmpt_protocol_select="ffrtmphttp_protocol"
++ffrtmpt_protocol_suggest="zlib"
++ffrtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
++ffrtmpte_protocol_suggest="zlib"
++ffrtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
++ffrtmpts_protocol_suggest="zlib"
+ rtp_protocol_select="udp_protocol"
+ schannel_conflict="openssl gnutls libtls mbedtls"
+ sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags"
+diff --git a/libavformat/Makefile b/libavformat/Makefile
+index 6c6b779080..f641b43de6 100644
+--- a/libavformat/Makefile
++++ b/libavformat/Makefile
+@@ -656,12 +656,12 @@ OBJS-$(CONFIG_MMSH_PROTOCOL)             += mmsh.o mms.o asf_tags.o
+ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o asf_tags.o
+ OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
+ OBJS-$(CONFIG_PROMPEG_PROTOCOL)          += prompeg.o
+-OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPE_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTE_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMP_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPE_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPS_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPT_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTE_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTS_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
+ OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o ip.o
+ OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
+ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
+diff --git a/libavformat/protocols.c b/libavformat/protocols.c
+index 6ee62a598a..3480a2b521 100644
+--- a/libavformat/protocols.c
++++ b/libavformat/protocols.c
+@@ -44,12 +44,12 @@ extern const URLProtocol ff_mmst_protocol;
+ 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/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
+index f0ef223f05..53a078b426 100644
+--- a/libavformat/rtmpproto.c
++++ b/libavformat/rtmpproto.c
+@@ -2594,7 +2594,7 @@ static int inject_fake_duration_metadata(RTMPContext *rt)
+ 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;
+@@ -2605,7 +2605,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o
+     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);
+@@ -3151,9 +3151,9 @@ const URLProtocol ff_##flavor##_protocol = {     \
+ #define RTMP_PROTOCOL(flavor, uppercase)         \
+     RTMP_PROTOCOL_3(flavor, CONFIG_ ## uppercase ## _PROTOCOL)
+-RTMP_PROTOCOL(rtmp,   RTMP)
+-RTMP_PROTOCOL(rtmpe,  RTMPE)
+-RTMP_PROTOCOL(rtmps,  RTMPS)
+-RTMP_PROTOCOL(rtmpt,  RTMPT)
+-RTMP_PROTOCOL(rtmpte, RTMPTE)
+-RTMP_PROTOCOL(rtmpts, RTMPTS)
++RTMP_PROTOCOL(ffrtmp,   FFRTMP)
++RTMP_PROTOCOL(ffrtmpe,  FFRTMPE)
++RTMP_PROTOCOL(ffrtmps,  FFRTMPS)
++RTMP_PROTOCOL(ffrtmpt,  FFRTMPT)
++RTMP_PROTOCOL(ffrtmpte, FFRTMPTE)
++RTMP_PROTOCOL(ffrtmpts, FFRTMPTS)
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0005-cst-fix-h264-sps-pps.patch b/archive-patches/ffmpeg-cst/5.1.4/0005-cst-fix-h264-sps-pps.patch
new file mode 100644 (file)
index 0000000..338ff1b
--- /dev/null
@@ -0,0 +1,27 @@
+# Test case: The.Purge.2013.1080p.BluRay.x264.YIFY.mp4
+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 2822644b10..d57647d25c 100644
+--- a/libavcodec/h264_mp4toannexb_bsf.c
++++ b/libavcodec/h264_mp4toannexb_bsf.c
+@@ -242,6 +242,11 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
+             if (!new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80))
+                 new_idr = 1;
+
++            if (s->new_idr && unit_type == H264_NAL_SEI && 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 (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) {
+                 if (ctx->par_out->extradata)
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0006-cst-fix-mpegts-pts.patch b/archive-patches/ffmpeg-cst/5.1.4/0006-cst-fix-mpegts-pts.patch
new file mode 100644 (file)
index 0000000..534e404
--- /dev/null
@@ -0,0 +1,17 @@
+diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
+index 8a3436f2be..abdfe41d85 100644
+--- a/libavformat/mpegts.c
++++ b/libavformat/mpegts.c
+@@ -1037,10 +1037,10 @@ static int new_pes_packet(PESContext *pes, AVPacket *pkt)
+     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/5.1.4/0007-disable-whitelist.patch b/archive-patches/ffmpeg-cst/5.1.4/0007-disable-whitelist.patch
new file mode 100644 (file)
index 0000000..343b124
--- /dev/null
@@ -0,0 +1,19 @@
+diff --git a/libavformat/avio.c b/libavformat/avio.c\r
+index 4846bbd8c6..978bf72994 100644\r
+--- a/libavformat/avio.c\r
++++ b/libavformat/avio.c\r
+@@ -177,12 +177,12 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)\r
+                (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));\r
+     av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||\r
+                (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));\r
+-\r
++/*\r
+     if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {\r
+         av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);\r
+         return AVERROR(EINVAL);\r
+     }\r
+-\r
++*/\r
+     if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {\r
+         av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);\r
+         return AVERROR(EINVAL);\r
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0008-mov-cenc-fixes.patch b/archive-patches/ffmpeg-cst/5.1.4/0008-mov-cenc-fixes.patch
new file mode 100644 (file)
index 0000000..42c9e57
--- /dev/null
@@ -0,0 +1,210 @@
+diff --git a/libavformat/mov.c b/libavformat/mov.c
+index 2b1131b911..bf56b41eb3 100644
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -1237,6 +1237,36 @@ static MOVFragmentStreamInfo * get_current_frag_stream_info(
+     return NULL;
+ }
++static MOVFragmentStreamInfo * get_frag_stream_by_sample_index(
++    MOVFragmentIndex *frag_index,
++    int sample_index,
++    int stream_id,
++    int *found_index)
++{
++    int i, j;
++    MOVFragmentIndexItem * item;
++
++    for (i = 0; i < frag_index->nb_items; i++) {
++        item = &frag_index->item[i];
++        for (j = 0; j < item->nb_stream_info; j++) {
++            if (item->stream_info[j].id == stream_id &&
++                item->stream_info[j].index_entry >= sample_index &&
++                (i == frag_index->nb_items - 1 ||
++                 (frag_index->item[i+1].nb_stream_info > j &&
++                  (frag_index->item[i+1].stream_info[j].index_entry == -1 ||
++                   sample_index < frag_index->item[i+1].stream_info[j].index_entry)))) {
++                if (found_index) {
++                    *found_index = i;
++                }
++                return &item->stream_info[j];
++            }
++        }
++    }
++
++    // This shouldn't happen
++    return NULL;
++}
++
+ static int search_frag_moof_offset(MOVFragmentIndex *frag_index, int64_t offset)
+ {
+     int a, b, m;
+@@ -5118,7 +5148,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+     sc->ctts_count = sti->nb_index_entries;
+     // Record the index_entry position in frag_index of this fragment
+-    if (frag_stream_info)
++    if (frag_stream_info && frag_stream_info->index_entry == -1)  // BUGFIX: In case of multiple trun, this must remain at the first value
+         frag_stream_info->index_entry = index_entry_pos;
+     if (index_entry_pos > 0)
+@@ -5986,6 +6016,8 @@ out:
+     return ret;
+ }
++static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom);
++
+ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+ {
+     AVStream *st;
+@@ -6004,6 +6036,10 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+         0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
+         0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd,
+     };
++    static const AVUUID uuid_piff_senc = {
++        0xA2, 0x39, 0x4F, 0x52, 0x5A, 0x9B, 0x4f, 0x14,
++        0xA2, 0x44, 0x6C, 0x42, 0x7C, 0x64, 0x8D, 0xF4
++    };
+     if (atom.size < AV_UUID_LEN || atom.size >= FFMIN(INT_MAX, SIZE_MAX))
+         return AVERROR_INVALIDDATA;
+@@ -6086,6 +6122,9 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+             return ret;
+         if (!sc->spherical)
+             av_log(c->fc, AV_LOG_WARNING, "Invalid spherical metadata found\n");
++    } else if (av_uuid_equal(uuid, uuid_piff_senc)) {
++        // PATCH: Netflix's stream packager outputs PIFF, which uses an uuid atom for senc boxes.
++        mov_read_senc(c, pb, atom);
+     }
+     return 0;
+@@ -6407,7 +6446,12 @@ static int mov_try_read_block(AVIOContext *pb, size_t size, uint8_t **data)
+         offset += to_read;
+     }
+-    *data = buffer;
++    // PATCH: The original code leads to huge amounts of memory being hogged during playback (especially CENC streams).
++    *data = av_realloc(buffer, size);
++    if (!*data) {
++        av_free(buffer);
++        return AVERROR(ENOMEM);
++    }
+     return 0;
+ }
+@@ -7026,15 +7070,16 @@ static int cbcs_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryption
+     }
+     /* whole-block full sample encryption */
+-    if (!sample->subsample_count) {
++    // PATCH: Fix for Apple TV+, they have subsamples but don't use pattern encryption.
++    if (!sample->subsample_count || (!sample->crypt_byte_block && !sample->skip_byte_block)) {
+         /* decrypt the whole packet */
+         memcpy(iv, sample->iv, 16);
+         av_aes_crypt(sc->cenc.aes_ctx, input, input, size/16, iv, 1);
+         return 0;
+-    } else if (!sample->crypt_byte_block && !sample->skip_byte_block) {
++    }/* else if (!sample->crypt_byte_block && !sample->skip_byte_block) {
+         av_log(c->fc, AV_LOG_ERROR, "pattern encryption is not present in 'cbcs' scheme\n");
+         return AVERROR_INVALIDDATA;
+-    }
++    }*/
+     for (i = 0; i < sample->subsample_count; i++) {
+         if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
+@@ -7102,10 +7147,20 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
+         // Note this only supports encryption info in the first sample descriptor.
+         if (mov->fragment.stsd_id == 1) {
+             if (frag_stream_info->encryption_index) {
+-                if (!current_index && frag_stream_info->index_entry)
++                // PATCH: Fix seeking backwards in encrypted streams.
++                //av_log(mov->fc, AV_LOG_INFO, "Frag Id: %d Index: %d (frag entry: %d)\n", frag_stream_info->id, current_index, frag_stream_info->index_entry);
++                encrypted_index = current_index - frag_stream_info->index_entry;
++                encryption_index = frag_stream_info->encryption_index;
++                if (encrypted_index < 0 || encrypted_index >= encryption_index->nb_encrypted_samples) {
++                    frag_stream_info = get_frag_stream_by_sample_index(&mov->frag_index, current_index, st->id,
++                                                                        &mov->frag_index.current);
++                    encrypted_index = current_index - frag_stream_info->index_entry;
++                    encryption_index = frag_stream_info->encryption_index;
++                }
++                /*if (!current_index && frag_stream_info->index_entry)
+                     sc->cenc.frag_index_entry_base = frag_stream_info->index_entry;
+                 encrypted_index = current_index - (frag_stream_info->index_entry - sc->cenc.frag_index_entry_base);
+-                encryption_index = frag_stream_info->encryption_index;
++                encryption_index = frag_stream_info->encryption_index;*/
+             } else {
+                 encryption_index = sc->cenc.encryption_index;
+             }
+@@ -7133,8 +7188,9 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
+             // Per-sample setting override.
+             encrypted_sample = encryption_index->encrypted_samples[encrypted_index];
+         } else {
+-            av_log(mov->fc, AV_LOG_ERROR, "Incorrect number of samples in encryption info\n");
+-            return AVERROR_INVALIDDATA;
++            av_log(mov->fc, AV_LOG_ERROR, "Incorrect number of samples in encryption info (index %d, nb %d)\n", encrypted_index, encryption_index->nb_encrypted_samples);
++            return 0; // CST hack - this can sometimes happen while seeking/switching audio streams, it's a non-fatal error.
++            //return AVERROR_INVALIDDATA;
+         }
+         if (mov->decryption_key) {
+@@ -8572,15 +8628,16 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
+     if (index >= 0 && index < mov->frag_index.nb_items)
+         target = mov->frag_index.item[index].moof_offset;
+-    if (avio_seek(s->pb, target, SEEK_SET) != target) {
++    // https://ffmpeg.org/pipermail/ffmpeg-devel/2020-April/261343.html
++    if (target >= 0 && avio_seek(s->pb, target, SEEK_SET) != target) {
+         av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target);
+         return AVERROR_INVALIDDATA;
+     }
+     mov->next_root_atom = 0;
+-    if (index < 0 || index >= mov->frag_index.nb_items)
++    if ((index < 0 && target >= 0) || index >= mov->frag_index.nb_items)
+         index = search_frag_moof_offset(&mov->frag_index, target);
+-    if (index < mov->frag_index.nb_items &&
++    if (index >= 0 && index < mov->frag_index.nb_items &&
+         mov->frag_index.item[index].moof_offset == target) {
+         if (index + 1 < mov->frag_index.nb_items)
+             mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset;
+@@ -8653,8 +8710,40 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
+     AVStream *st = NULL;
+     int64_t current_index;
+     int ret;
++    int i;
+     mov->fc = s;
+  retry:
++    // https://ffmpeg.org/pipermail/ffmpeg-devel/2020-April/261343.html
++    if (s->pb->pos == 0) {
++
++        // Discard current fragment index
++        if (mov->frag_index.allocated_size > 0) {
++            av_freep(&mov->frag_index.item);
++            mov->frag_index.nb_items = 0;
++            mov->frag_index.allocated_size = 0;
++            mov->frag_index.current = -1;
++            mov->frag_index.complete = 0;
++        }
++
++        for (i = 0; i < s->nb_streams; i++) {
++            AVStream *avst = s->streams[i];
++            FFStream *const avsti = ffstream(avst);
++            MOVStreamContext *msc = avst->priv_data;
++
++            // Clear current sample
++            mov_current_sample_set(msc, 0);
++
++            // Discard current index entries
++            if (avsti->index_entries_allocated_size > 0) {
++                av_freep(&avsti->index_entries);
++                avsti->index_entries_allocated_size = 0;
++                avsti->nb_index_entries = 0;
++            }
++        }
++
++        if ((ret = mov_switch_root(s, -1, -1)) < 0)
++            return ret;
++    }
+     sample = mov_find_next_sample(s, &st);
+     if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
+         if (!mov->next_root_atom)
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0009-ffmpeg-discont.patch b/archive-patches/ffmpeg-cst/5.1.4/0009-ffmpeg-discont.patch
new file mode 100644 (file)
index 0000000..71f3bb4
--- /dev/null
@@ -0,0 +1,124 @@
+diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
+index e7384f052a..bc75fe3df6 100644
+--- a/fftools/ffmpeg.c
++++ b/fftools/ffmpeg.c
+@@ -4080,7 +4080,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){
+@@ -4118,7 +4118,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 &&
+-        !disable_discontinuity_correction) {
++        !disable_discontinuity_correction && !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 ||
+@@ -4156,6 +4156,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 duration:%s duration_time:%s off:%s off_time:%s\n",
+                ifile->ist_index + pkt->stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
+diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
+index 391a35cf50..3ca42c8f0c 100644
+--- a/fftools/ffmpeg.h
++++ b/fftools/ffmpeg.h
+@@ -433,6 +433,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 {
+@@ -620,6 +624,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 enum VideoSyncMethod video_sync_method;
+diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
+index 6e18a4a23e..f2c2313219 100644
+--- a/fftools/ffmpeg_opt.c
++++ b/fftools/ffmpeg_opt.c
+@@ -156,6 +156,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;
+ enum VideoSyncMethod video_sync_method = VSYNC_AUTO;
+@@ -1361,6 +1364,7 @@ static int open_input_file(OptionsContext *o, const char *filename)
+     f->input_sync_ref = o->input_sync_ref;
+     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;
+@@ -3760,6 +3764,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 },
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0010-hls-replace-key-uri.patch b/archive-patches/ffmpeg-cst/5.1.4/0010-hls-replace-key-uri.patch
new file mode 100644 (file)
index 0000000..113cd28
--- /dev/null
@@ -0,0 +1,49 @@
+diff --git a/libavformat/hls.c b/libavformat/hls.c
+index faf92804a2..bbc8388c94 100644
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -225,6 +225,8 @@ typedef struct HLSContext {
+     int http_persistent;
+     int http_multiple;
+     int http_seekable;
++    char *key_uri_replace_old;
++    char *key_uri_replace_new;
+     AVIOContext *playlist_pb;
+     HLSCryptoContext  crypto_ctx;
+ } HLSContext;
+@@ -1293,8 +1295,16 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg,
+     if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
+         if (strcmp(seg->key, pls->key_url)) {
++            char *key_url = NULL;
+             AVIOContext *pb = NULL;
+-            if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts, NULL) == 0) {
++            if (NULL != c->key_uri_replace_old && \
++                NULL != c-> key_uri_replace_new && \
++                '\0' != c->key_uri_replace_old[0]) {
++                key_url = av_strireplace(seg->key, c->key_uri_replace_old, c->key_uri_replace_new);
++            } else {
++                key_url = seg->key;
++            }
++            if (open_url(pls->parent, &pb, key_url, &c->avio_opts, opts, NULL) == 0) {
+                 ret = avio_read(pb, pls->key, sizeof(pls->key));
+                 if (ret != sizeof(pls->key)) {
+                     av_log(pls->parent, AV_LOG_ERROR, "Unable to read key file %s\n",
+@@ -1306,6 +1316,8 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg,
+                        seg->key);
+             }
+             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
++            if (key_url != seg->key)
++                av_free(key_url);
+         }
+     }
+@@ -2530,6 +2542,8 @@ static int hls_probe(const AVProbeData *p)
+ #define OFFSET(x) offsetof(HLSContext, x)
+ #define FLAGS AV_OPT_FLAG_DECODING_PARAM
+ static const AVOption hls_options[] = {
++    { "key_uri_old", "allow to replace part of AES key uri - old", OFFSET(key_uri_replace_old), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
++    { "key_uri_new", "allow to replace part of AES key uri - new", OFFSET(key_uri_replace_new), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
+     {"live_start_index", "segment index to start live streams at (negative values are from the end)",
+         OFFSET(live_start_index), AV_OPT_TYPE_INT, {.i64 = -3}, INT_MIN, INT_MAX, FLAGS},
+     {"prefer_x_start", "prefer to use #EXT-X-START if it's in playlist instead of live_start_index",
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0011-recheck-discard-flags.patch b/archive-patches/ffmpeg-cst/5.1.4/0011-recheck-discard-flags.patch
new file mode 100644 (file)
index 0000000..afddf42
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -2171,8 +2171,10 @@
+     HLSContext *c = s->priv_data;
+     int ret, i, minplaylist = -1;
+-    recheck_discard_flags(s, c->first_packet);
+-    c->first_packet = 0;
++    if (c->first_packet) {
++        recheck_discard_flags(s, 1);
++        c->first_packet = 0;
++    }
+     for (i = 0; i < c->n_playlists; i++) {
+         struct playlist *pls = c->playlists[i];
diff --git a/archive-patches/ffmpeg-cst/5.1.4/0012-rtsp.patch b/archive-patches/ffmpeg-cst/5.1.4/0012-rtsp.patch
new file mode 100644 (file)
index 0000000..08930f4
--- /dev/null
@@ -0,0 +1,55 @@
+diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
+index f948f1d395..69507ec866 100644
+--- a/libavformat/rtsp.c
++++ b/libavformat/rtsp.c
+@@ -2391,6 +2391,8 @@ static int sdp_read_header(AVFormatContext *s)
+     int i, err;
+     char url[MAX_URL_SIZE];
+     AVBPrint bp;
++    const char *p, *sp="", *sources="", *sp2, *sources2;
++    char sources_buf[1024];
+     if (!ff_network_init())
+         return AVERROR(EIO);
+@@ -2412,6 +2414,16 @@ static int sdp_read_header(AVFormatContext *s)
+     av_bprint_finalize(&bp, NULL);
+     if (err) goto fail;
++    /* Search for sources= tag in original URL for rtp protocol only */
++    if (strncmp(s->url, "rtp://", 6) == 0) {
++        p = strchr(s->url, '?');
++        if (p && av_find_info_tag(sources_buf, sizeof(sources_buf), "sources", p)) {
++            /* av_log(s, AV_LOG_VERBOSE, "sdp_read_header found sources %s\n", sources_buf);  */
++            sp = sources_buf;
++            sources = "&sources=";
++        }
++    }
++
+     /* open each RTP stream */
+     for (i = 0; i < rt->nb_rtsp_streams; i++) {
+         char namebuf[50];
+@@ -2431,12 +2443,22 @@ static int sdp_read_header(AVFormatContext *s)
+                 av_dict_free(&opts);
+                 goto fail;
+             }
++
++            /* Prepare to add sources to the url to be opened.
++               Otherwise the join to the source specific muliticast will be missing */
++            sources2 = sources;
++            sp2 = sp;
++            /* ignore sources from original URL, when sources are already set in rtsp_st */
++            if (rtsp_st->nb_include_source_addrs > 0)
++                sources2 = sp2 = "";
++
+             ff_url_join(url, sizeof(url), "rtp", NULL,
+                         namebuf, rtsp_st->sdp_port,
+-                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d",
++                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d%s%s",
+                         rtsp_st->sdp_port, rtsp_st->sdp_ttl,
+                         rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0,
+-                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0);
++                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0,
++                        sources2, sp2);
+             p = strchr(s->url, '?');
+             if (p && av_find_info_tag(buf, sizeof(buf), "localaddr", p))
diff --git a/archive-patches/ffmpeg-cst/5.1.4/ffmpeg-increase-IO_BUFFER_SIZE-to-256k.patch b/archive-patches/ffmpeg-cst/5.1.4/ffmpeg-increase-IO_BUFFER_SIZE-to-256k.patch
new file mode 100644 (file)
index 0000000..1c96a07
--- /dev/null
@@ -0,0 +1,26 @@
+From f193409d354a41d8afaf1b75ca34dc1839a60b69 Mon Sep 17 00:00:00 2001
+From: gandharva <gandharva@gmx.de>
+Date: Wed, 14 Jun 2017 19:51:24 +0200
+Subject: [PATCH 8/8] - increase IO_BUFFER_SIZE to 128k
+
+performance improvement when using SMB/CIFS
+---
+ libavformat/aviobuf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+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.1.4
+
diff --git a/archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-dash-hacks.patch b/archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-dash-hacks.patch
new file mode 100644 (file)
index 0000000..5668bbd
--- /dev/null
@@ -0,0 +1,142 @@
+diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
+index 2ca91bea8b..ecaf50047d 100644
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -441,8 +441,12 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
+         return AVERROR_INVALIDDATA;
+     av_freep(pb);
++
+     av_dict_copy(&tmp, *opts, 0);
+     av_dict_copy(&tmp, opts2, 0);
++    av_dict_set_int(&tmp, "reconnect", 1, 0);
++    av_dict_set_int(&tmp, "icy", 0, 0);
++
+     ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp);
+     if (ret >= 0) {
+         // update cookies on http response with setcookies.
+@@ -768,7 +772,8 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur
+     baseurl = xmlNodeGetContent(node);
+     root_url = (av_strcasecmp(baseurl, "")) ? baseurl : path;
+     if (node) {
+-        xmlNodeSetContent(node, root_url);
++        // HACK: SetContent fails if the URL happens to include '&', which isn't that uncommon...
++        //xmlNodeSetContent(node, root_url);
+         updated = 1;
+     }
+@@ -802,7 +807,7 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur
+                 memset(p + 1, 0, strlen(p));
+             }
+             av_strlcat(tmp_str, text + start, tmp_max_url_size);
+-            xmlNodeSetContent(baseurl_nodes[i], tmp_str);
++            //xmlNodeSetContent(baseurl_nodes[i], tmp_str);
+             updated = 1;
+             xmlFree(text);
+         }
+@@ -1217,6 +1222,7 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+     char *val  = NULL;
+     uint32_t period_duration_sec = 0;
+     uint32_t period_start_sec = 0;
++    char *nfcs_key = NULL;
+     if (!in) {
+         close_in = 1;
+@@ -1335,6 +1341,12 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+             } else if (!av_strcasecmp(node->name, "ProgramInformation")) {
+                 parse_programinformation(s, node);
+             }
++            // HACK: libvodstream stores decrypted keys in a custom tag.
++            if (!av_strcasecmp(node->name, (const char*)"nfcskey")) {
++                nfcs_key = xmlNodeGetContent(node->children);
++                c->cenc_decryption_key = av_strdup(nfcs_key);
++                xmlFree(nfcs_key);
++            }
+             node = xmlNextElementSibling(node);
+         }
+         if (!period_node) {
+@@ -1879,7 +1891,8 @@ static int reopen_demux_for_component(AVFormatContext *s, struct representation
+     }
+     ffio_init_context(&pls->pb, avio_ctx_buffer, INITIAL_BUFFER_SIZE, 0,
+                       pls, read_data, NULL, c->is_live ? NULL : seek_data);
+-    pls->pb.pub.seekable = 0;
++    // PATCH: If this is 0, seeking in VOD streams downloads the entire content between origin and target.
++    pls->pb.pub.seekable = 1;
+     if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+         goto fail;
+@@ -2167,6 +2180,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+     int ret = 0, i;
+     int64_t mints = 0;
+     struct representation *cur = NULL;
++    struct representation *curHack = NULL;
+     struct representation *rep = NULL;
+     recheck_discard_flags(s, c->videos, c->n_videos);
+@@ -2188,6 +2202,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+             continue;
+         if (!cur || rep->cur_timestamp < mints) {
+             cur = rep;
++            curHack = rep;
+             mints = rep->cur_timestamp;
+         }
+     }
+@@ -2203,6 +2218,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+     }
+     if (!cur) {
++        av_log(s, AV_LOG_WARNING, "INVALIDDATA\n");
+         return AVERROR_INVALIDDATA;
+     }
+     while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
+@@ -2221,6 +2237,26 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+             cur->is_restart_needed = 0;
+         }
+     }
++    if (curHack && curHack != cur) {
++        cur = curHack;
++        ret = 0;
++        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
++            ret = av_read_frame(cur->ctx, pkt);
++            if (ret >= 0) {
++                /* If we got a packet, return it */
++                cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
++                pkt->stream_index = cur->stream_index;
++                return 0;
++            }
++            if (cur->is_restart_needed) {
++                cur->cur_seg_offset = 0;
++                cur->init_sec_buf_read_offset = 0;
++                ff_format_io_close(cur->parent, &cur->input);
++                ret = reopen_demux_for_component(s, cur);
++                cur->is_restart_needed = 0;
++            }
++        }
++    }
+     return AVERROR_EOF;
+ }
+@@ -2232,6 +2268,7 @@ static int dash_close(AVFormatContext *s)
+     free_subtitle_list(c);
+     av_dict_free(&c->avio_opts);
+     av_freep(&c->base_url);
++    av_freep(&c->cenc_decryption_key);
+     return 0;
+ }
+@@ -2319,10 +2356,11 @@ static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t timestam
+         if (!ret)
+             ret = dash_seek(s, c->audios[i], seek_pos_msec, flags, !c->audios[i]->ctx);
+     }
+-    for (i = 0; i < c->n_subtitles; i++) {
+-        if (!ret)
+-            ret = dash_seek(s, c->subtitles[i], seek_pos_msec, flags, !c->subtitles[i]->ctx);
+-    }
++    // PATCH: Seeking in subtitle streams breaks things, and it doesn't seem necessary.
++    //for (i = 0; i < c->n_subtitles; i++) {
++    //    if (!ret)
++    //        ret = dash_seek(s, c->subtitles[i], seek_pos_msec, flags, !c->subtitles[i]->ctx);
++    //}
+     return ret;
+ }
diff --git a/archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-hls-changes.patch b/archive-patches/ffmpeg-cst/5.1.4/ffmpeg51-hls-changes.patch
new file mode 100644 (file)
index 0000000..ea96739
--- /dev/null
@@ -0,0 +1,57 @@
+diff --git a/libavformat/hls.c b/libavformat/hls.c
+index e622425e80..faf92804a2 100644
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -46,7 +46,7 @@
+ #include "hls_sample_encryption.h"
+-#define INITIAL_BUFFER_SIZE 32768
++#define INITIAL_BUFFER_SIZE 1048576
+ #define MAX_FIELD_LEN 64
+ #define MAX_CHARACTERISTICS_LEN 512
+@@ -1580,7 +1580,7 @@ reload:
+     seg = next_segment(v);
+     if (c->http_multiple == 1 && !v->input_next_requested &&
+-        seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg && seg->key_type != KEY_AES_128 && av_strstart(seg->url, "http", NULL)) {
+         ret = open_input(c, v, seg, &v->input_next);
+         if (ret < 0) {
+             if (ff_check_interrupt(c->interrupt_callback))
+@@ -1613,7 +1613,7 @@ reload:
+         return ret;
+     }
+     if (c->http_persistent &&
+-        seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg->key_type != KEY_AES_128 && av_strstart(seg->url, "http", NULL)) {
+         v->input_read_done = 1;
+     } else {
+         ff_format_io_close(v->parent, &v->input);
+@@ -2475,6 +2475,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
+     /* set segment now so we do not need to search again below */
+     seek_pls->cur_seq_no = seq_no;
+     seek_pls->seek_stream_index = stream_subdemuxer_index;
++    //av_log(s, AV_LOG_INFO, "Seek to %d in %d\n", seq_no, stream_subdemuxer_index);
+     for (i = 0; i < c->n_playlists; i++) {
+         /* Reset reading */
+@@ -2535,7 +2536,7 @@ static const AVOption hls_options[] = {
+         OFFSET(prefer_x_start), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS},
+     {"allowed_extensions", "List of file extensions that hls is allowed to access",
+         OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+-        {.str = "3gp,aac,avi,ac3,eac3,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
++        {.str = "3gp,aac,avi,ac3,eac3,flac,key,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
+         INT_MIN, INT_MAX, FLAGS},
+     {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
+         OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS},
+@@ -2544,7 +2545,7 @@ static const AVOption hls_options[] = {
+     {"http_persistent", "Use persistent HTTP connections",
+         OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
+     {"http_multiple", "Use multiple HTTP connections for fetching segments",
+-        OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, FLAGS},
++        OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, FLAGS},
+     {"http_seekable", "Use HTTP partial requests, 0 = disable, 1 = enable, -1 = auto",
+         OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS},
+     {"seg_format_options", "Set options for segment demuxer",
diff --git a/archive-patches/ffmpeg-cst/5.1.4/optional/README.txt b/archive-patches/ffmpeg-cst/5.1.4/optional/README.txt
new file mode 100644 (file)
index 0000000..5a14c71
--- /dev/null
@@ -0,0 +1,5 @@
+These patches mostly fix processing DRM DASH/HLS streams.\r
+\r
+hls-changes sets a rather high buffer size (tuned towards 1080p VOD content), which may be undesirable for live streams or SD VOD content.\r
+\r
+dash-hacks contains some fixes that may help with DASH streams that contain subtitles and multiple audio streams (esp. switching between streams on CST).
\ No newline at end of file
diff --git a/archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-dash-hacks.patch b/archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-dash-hacks.patch
new file mode 100644 (file)
index 0000000..5668bbd
--- /dev/null
@@ -0,0 +1,142 @@
+diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
+index 2ca91bea8b..ecaf50047d 100644
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -441,8 +441,12 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
+         return AVERROR_INVALIDDATA;
+     av_freep(pb);
++
+     av_dict_copy(&tmp, *opts, 0);
+     av_dict_copy(&tmp, opts2, 0);
++    av_dict_set_int(&tmp, "reconnect", 1, 0);
++    av_dict_set_int(&tmp, "icy", 0, 0);
++
+     ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp);
+     if (ret >= 0) {
+         // update cookies on http response with setcookies.
+@@ -768,7 +772,8 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur
+     baseurl = xmlNodeGetContent(node);
+     root_url = (av_strcasecmp(baseurl, "")) ? baseurl : path;
+     if (node) {
+-        xmlNodeSetContent(node, root_url);
++        // HACK: SetContent fails if the URL happens to include '&', which isn't that uncommon...
++        //xmlNodeSetContent(node, root_url);
+         updated = 1;
+     }
+@@ -802,7 +807,7 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur
+                 memset(p + 1, 0, strlen(p));
+             }
+             av_strlcat(tmp_str, text + start, tmp_max_url_size);
+-            xmlNodeSetContent(baseurl_nodes[i], tmp_str);
++            //xmlNodeSetContent(baseurl_nodes[i], tmp_str);
+             updated = 1;
+             xmlFree(text);
+         }
+@@ -1217,6 +1222,7 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+     char *val  = NULL;
+     uint32_t period_duration_sec = 0;
+     uint32_t period_start_sec = 0;
++    char *nfcs_key = NULL;
+     if (!in) {
+         close_in = 1;
+@@ -1335,6 +1341,12 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+             } else if (!av_strcasecmp(node->name, "ProgramInformation")) {
+                 parse_programinformation(s, node);
+             }
++            // HACK: libvodstream stores decrypted keys in a custom tag.
++            if (!av_strcasecmp(node->name, (const char*)"nfcskey")) {
++                nfcs_key = xmlNodeGetContent(node->children);
++                c->cenc_decryption_key = av_strdup(nfcs_key);
++                xmlFree(nfcs_key);
++            }
+             node = xmlNextElementSibling(node);
+         }
+         if (!period_node) {
+@@ -1879,7 +1891,8 @@ static int reopen_demux_for_component(AVFormatContext *s, struct representation
+     }
+     ffio_init_context(&pls->pb, avio_ctx_buffer, INITIAL_BUFFER_SIZE, 0,
+                       pls, read_data, NULL, c->is_live ? NULL : seek_data);
+-    pls->pb.pub.seekable = 0;
++    // PATCH: If this is 0, seeking in VOD streams downloads the entire content between origin and target.
++    pls->pb.pub.seekable = 1;
+     if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+         goto fail;
+@@ -2167,6 +2180,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+     int ret = 0, i;
+     int64_t mints = 0;
+     struct representation *cur = NULL;
++    struct representation *curHack = NULL;
+     struct representation *rep = NULL;
+     recheck_discard_flags(s, c->videos, c->n_videos);
+@@ -2188,6 +2202,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+             continue;
+         if (!cur || rep->cur_timestamp < mints) {
+             cur = rep;
++            curHack = rep;
+             mints = rep->cur_timestamp;
+         }
+     }
+@@ -2203,6 +2218,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+     }
+     if (!cur) {
++        av_log(s, AV_LOG_WARNING, "INVALIDDATA\n");
+         return AVERROR_INVALIDDATA;
+     }
+     while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
+@@ -2221,6 +2237,26 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+             cur->is_restart_needed = 0;
+         }
+     }
++    if (curHack && curHack != cur) {
++        cur = curHack;
++        ret = 0;
++        while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
++            ret = av_read_frame(cur->ctx, pkt);
++            if (ret >= 0) {
++                /* If we got a packet, return it */
++                cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
++                pkt->stream_index = cur->stream_index;
++                return 0;
++            }
++            if (cur->is_restart_needed) {
++                cur->cur_seg_offset = 0;
++                cur->init_sec_buf_read_offset = 0;
++                ff_format_io_close(cur->parent, &cur->input);
++                ret = reopen_demux_for_component(s, cur);
++                cur->is_restart_needed = 0;
++            }
++        }
++    }
+     return AVERROR_EOF;
+ }
+@@ -2232,6 +2268,7 @@ static int dash_close(AVFormatContext *s)
+     free_subtitle_list(c);
+     av_dict_free(&c->avio_opts);
+     av_freep(&c->base_url);
++    av_freep(&c->cenc_decryption_key);
+     return 0;
+ }
+@@ -2319,10 +2356,11 @@ static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t timestam
+         if (!ret)
+             ret = dash_seek(s, c->audios[i], seek_pos_msec, flags, !c->audios[i]->ctx);
+     }
+-    for (i = 0; i < c->n_subtitles; i++) {
+-        if (!ret)
+-            ret = dash_seek(s, c->subtitles[i], seek_pos_msec, flags, !c->subtitles[i]->ctx);
+-    }
++    // PATCH: Seeking in subtitle streams breaks things, and it doesn't seem necessary.
++    //for (i = 0; i < c->n_subtitles; i++) {
++    //    if (!ret)
++    //        ret = dash_seek(s, c->subtitles[i], seek_pos_msec, flags, !c->subtitles[i]->ctx);
++    //}
+     return ret;
+ }
diff --git a/archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-hls-changes.patch b/archive-patches/ffmpeg-cst/5.1.4/optional/ffmpeg51-hls-changes.patch
new file mode 100644 (file)
index 0000000..ea96739
--- /dev/null
@@ -0,0 +1,57 @@
+diff --git a/libavformat/hls.c b/libavformat/hls.c
+index e622425e80..faf92804a2 100644
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -46,7 +46,7 @@
+ #include "hls_sample_encryption.h"
+-#define INITIAL_BUFFER_SIZE 32768
++#define INITIAL_BUFFER_SIZE 1048576
+ #define MAX_FIELD_LEN 64
+ #define MAX_CHARACTERISTICS_LEN 512
+@@ -1580,7 +1580,7 @@ reload:
+     seg = next_segment(v);
+     if (c->http_multiple == 1 && !v->input_next_requested &&
+-        seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg && seg->key_type != KEY_AES_128 && av_strstart(seg->url, "http", NULL)) {
+         ret = open_input(c, v, seg, &v->input_next);
+         if (ret < 0) {
+             if (ff_check_interrupt(c->interrupt_callback))
+@@ -1613,7 +1613,7 @@ reload:
+         return ret;
+     }
+     if (c->http_persistent &&
+-        seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg->key_type != KEY_AES_128 && av_strstart(seg->url, "http", NULL)) {
+         v->input_read_done = 1;
+     } else {
+         ff_format_io_close(v->parent, &v->input);
+@@ -2475,6 +2475,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
+     /* set segment now so we do not need to search again below */
+     seek_pls->cur_seq_no = seq_no;
+     seek_pls->seek_stream_index = stream_subdemuxer_index;
++    //av_log(s, AV_LOG_INFO, "Seek to %d in %d\n", seq_no, stream_subdemuxer_index);
+     for (i = 0; i < c->n_playlists; i++) {
+         /* Reset reading */
+@@ -2535,7 +2536,7 @@ static const AVOption hls_options[] = {
+         OFFSET(prefer_x_start), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS},
+     {"allowed_extensions", "List of file extensions that hls is allowed to access",
+         OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+-        {.str = "3gp,aac,avi,ac3,eac3,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
++        {.str = "3gp,aac,avi,ac3,eac3,flac,key,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
+         INT_MIN, INT_MAX, FLAGS},
+     {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
+         OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS},
+@@ -2544,7 +2545,7 @@ static const AVOption hls_options[] = {
+     {"http_persistent", "Use persistent HTTP connections",
+         OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
+     {"http_multiple", "Use multiple HTTP connections for fetching segments",
+-        OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, FLAGS},
++        OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, FLAGS},
+     {"http_seekable", "Use HTTP partial requests, 0 = disable, 1 = enable, -1 = auto",
+         OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS},
+     {"seg_format_options", "Set options for segment demuxer",
diff --git a/archive-patches/ffmpeg-cst/6.1.2/0022-ffmpeg61-hls-discontinuity.patch b/archive-patches/ffmpeg-cst/6.1.2/0022-ffmpeg61-hls-discontinuity.patch
new file mode 100755 (executable)
index 0000000..519a2c6
--- /dev/null
@@ -0,0 +1,488 @@
+diff --git a/libavformat/hls.c b/libavformat/hls.c
+index f5f549b24d..54cc02e592 100644
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -83,6 +83,7 @@ struct segment {
+     uint8_t iv[16];
+     /* associated Media Initialization Section, treated as a segment */
+     struct segment *init_section;
++    int is_discontinuity;
+ };
+ struct rendition;
+@@ -111,6 +112,7 @@ struct playlist {
+     AVFormatContext *ctx;
+     AVPacket *pkt;
+     int has_noheader_flag;
++    int had_discontinuity;
+     /* main demuxer streams associated with this playlist
+      * indexed by the subdemuxer stream indexes */
+@@ -737,6 +739,7 @@ static int parse_playlist(HLSContext *c, const char *url,
+     char line[MAX_URL_SIZE];
+     const char *ptr;
+     int close_in = 0;
++    int is_discontinuity = 0;
+     int64_t seg_offset = 0;
+     int64_t seg_size = -1;
+     uint8_t *new_url = NULL;
+@@ -921,6 +924,8 @@ static int parse_playlist(HLSContext *c, const char *url,
+             ptr = strchr(ptr, '@');
+             if (ptr)
+                 seg_offset = strtoll(ptr+1, NULL, 10);
++        } else if (!strcmp(line, "#EXT-X-DISCONTINUITY")) {
++            is_discontinuity = 1;
+         } else if (av_strstart(line, "#", NULL)) {
+             av_log(c->ctx, AV_LOG_INFO, "Skip ('%s')\n", line);
+             continue;
+@@ -1004,6 +1009,9 @@ static int parse_playlist(HLSContext *c, const char *url,
+                 }
+                 seg->init_section = cur_init_section;
++
++                seg->is_discontinuity = is_discontinuity;
++                is_discontinuity = 0;
+             }
+         }
+     }
+@@ -1592,7 +1600,8 @@ reload:
+     seg = next_segment(v);
+     if (c->http_multiple == 1 && !v->input_next_requested &&
+-        seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg && seg->key_type == KEY_NONE && !seg->is_discontinuity &&
++        av_strstart(seg->url, "http", NULL)) {
+         ret = open_input(c, v, seg, &v->input_next);
+         if (ret < 0) {
+             if (ff_check_interrupt(c->interrupt_callback))
+@@ -1634,6 +1643,13 @@ reload:
+     c->cur_seq_no = v->cur_seq_no;
++    /* If a discontinuity was encountered, the caller should re-open the demuxer. */
++    seg = current_segment(v);
++    if (seg && seg->is_discontinuity) {
++        v->had_discontinuity = 1;
++        return AVERROR(EAGAIN);
++    }
++
+     goto restart;
+ }
+@@ -1921,6 +1937,203 @@ static int hls_close(AVFormatContext *s)
+     return 0;
+ }
++static int reopen_demux_for_playlist(AVFormatContext *s, struct playlist *pls)
++{
++    HLSContext *c = s->priv_data;
++    const AVInputFormat *in_fmt = NULL;
++    char *url;
++    AVDictionary *options = NULL;
++    struct segment *seg = NULL;
++    int is_reopen = 0;
++    int ret = 0;
++
++    if (pls->ctx) {
++        is_reopen = 1;
++        av_freep(&pls->pb.pub.buffer);
++        memset(&pls->pb, 0, sizeof(pls->pb));
++        pls->ctx->pb = NULL;
++        avformat_close_input(&pls->ctx);
++
++        /* in case of a discontinuity, forget about all current streams */
++        for (unsigned i = 0; i < s->nb_streams; i++)
++            ff_free_stream(&s->streams[i]);
++        s->nb_streams = 0;
++
++        av_freep(&pls->main_streams);
++        pls->n_main_streams = 0;
++
++        /* duration and start_time will be set based on the new streams */
++        // s->duration = s->start_time = AV_NOPTS_VALUE;
++    }
++
++    if (ff_check_interrupt(&s->interrupt_callback)) {
++        ret = AVERROR_EXIT;
++        goto fail;
++    }
++
++    if (!(pls->ctx = avformat_alloc_context())) {
++        ret = AVERROR(ENOMEM);
++        goto fail;
++    }
++
++    pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
++    if (!pls->read_buffer) {
++        ret = AVERROR(ENOMEM);
++        avformat_free_context(pls->ctx);
++        pls->ctx = NULL;
++        goto fail;
++    }
++
++    ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
++                      read_data, NULL, NULL);
++
++    /*
++     * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
++     * external audio track that contains audio setup information
++     */
++    seg = current_segment(pls);
++    if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
++        pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
++        uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
++        if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
++            /* Fail if error was not end of file */
++            if (ret != AVERROR_EOF) {
++                avformat_free_context(pls->ctx);
++                pls->ctx = NULL;
++                return ret;
++            }
++        }
++        ret = 0;
++        /* Reset reading */
++        ff_format_io_close(pls->parent, &pls->input);
++        pls->input = NULL;
++        pls->input_read_done = 0;
++        ff_format_io_close(pls->parent, &pls->input_next);
++        pls->input_next = NULL;
++        pls->input_next_requested = 0;
++        pls->cur_seg_offset = 0;
++        pls->cur_init_section = NULL;
++        /* Reset EOF flag */
++        pls->pb.pub.eof_reached = 0;
++        /* Clear any buffered data */
++        pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer;
++        /* Reset the position */
++        pls->pb.pub.pos = 0;
++    }
++
++    /*
++     * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
++     * use that information to find the media format, otherwise probe input data
++     */
++    if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
++        pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
++        void *iter = NULL;
++        while ((in_fmt = av_demuxer_iterate(&iter)))
++            if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id)
++                break;
++    } else {
++        pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
++        pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
++        pls->ctx->interrupt_callback = s->interrupt_callback;
++        url = av_strdup(pls->segments[0]->url);
++        ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
++        if (ret < 0) {
++            /* Free the ctx - it isn't initialized properly at this point,
++             * so avformat_close_input shouldn't be called. If
++             * avformat_open_input fails below, it frees and zeros the
++             * context, so it doesn't need any special treatment like this. */
++            av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url);
++            avformat_free_context(pls->ctx);
++            pls->ctx = NULL;
++            av_free(url);
++            return ret;
++        }
++        av_free(url);
++    }
++
++    if (seg && seg->key_type == KEY_SAMPLE_AES) {
++        if (strstr(in_fmt->name, "mov")) {
++            char key[33];
++            ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
++            av_dict_set(&options, "decryption_key", key, 0);
++        } else if (!c->crypto_ctx.aes_ctx) {
++            c->crypto_ctx.aes_ctx = av_aes_alloc();
++            if (!c->crypto_ctx.aes_ctx) {
++                avformat_free_context(pls->ctx);
++                pls->ctx = NULL;
++                return AVERROR(ENOMEM);
++            }
++        }
++    }
++
++    pls->ctx->pb       = &pls->pb.pub;
++    pls->ctx->io_open  = nested_io_open;
++    pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
++
++    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
++        return ret;
++
++    av_dict_copy(&options, c->seg_format_opts, 0);
++
++    ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options);
++    av_dict_free(&options);
++    if (ret < 0)
++        return ret;
++
++    if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
++        ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra);
++        avformat_queue_attached_pictures(pls->ctx);
++        ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra);
++        ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
++    }
++
++    if (pls->is_id3_timestamped == -1)
++        av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
++
++    /*
++     * For ID3 timestamped raw audio streams we need to detect the packet
++     * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
++     * but for other streams we can rely on our user calling avformat_find_stream_info()
++     * on us if they want to.
++     */
++    if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
++        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
++            pls->ctx->nb_streams == 1)
++            ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
++        else
++            ret = avformat_find_stream_info(pls->ctx, NULL);
++
++        if (ret < 0)
++            return ret;
++    }
++
++    pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
++
++    if (is_reopen) {
++        /* Probe streams so correct timings are available for PTS computation (e.g., aac in mpegts) */
++        avformat_find_stream_info(pls->ctx, NULL);
++    }
++
++    /* Create new AVStreams for each stream in this playlist */
++    ret = update_streams_from_subdemuxer(s, pls);
++    if (ret < 0)
++        return ret;
++
++    /*
++     * Copy any metadata from playlist to main streams, but do not set
++     * event flags.
++     */
++    if (pls->n_main_streams)
++        av_dict_copy(&pls->main_streams[0]->metadata, pls->ctx->metadata, 0);
++
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
++
++fail:
++    return ret;
++}
++
+ static int hls_read_header(AVFormatContext *s)
+ {
+     HLSContext *c = s->priv_data;
+@@ -2018,16 +2231,13 @@ static int hls_read_header(AVFormatContext *s)
+     /* Open the demuxer for each playlist */
+     for (i = 0; i < c->n_playlists; i++) {
+         struct playlist *pls = c->playlists[i];
+-        const AVInputFormat *in_fmt = NULL;
+-        char *url;
+-        AVDictionary *options = NULL;
+-        struct segment *seg = NULL;
+-
+-        if (!(pls->ctx = avformat_alloc_context()))
+-            return AVERROR(ENOMEM);
+-        if (pls->n_segments == 0)
++        if (pls->n_segments == 0) {
++            /* Assign an empty context anyway */
++            if (!(pls->ctx = avformat_alloc_context()))
++                return AVERROR(ENOMEM);
+             continue;
++        }
+         pls->index  = i;
+         pls->needed = 1;
+@@ -2045,153 +2255,10 @@ static int hls_read_header(AVFormatContext *s)
+             pls->cur_seq_no = highest_cur_seq_no;
+         }
+-        pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
+-        if (!pls->read_buffer){
+-            avformat_free_context(pls->ctx);
+-            pls->ctx = NULL;
+-            return AVERROR(ENOMEM);
+-        }
+-
+-        ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
+-                          read_data, NULL, NULL);
+-
+-        /*
+-         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
+-         * external audio track that contains audio setup information
+-         */
+-        seg = current_segment(pls);
+-        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
+-            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
+-            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
+-            if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
+-                /* Fail if error was not end of file */
+-                if (ret != AVERROR_EOF) {
+-                    avformat_free_context(pls->ctx);
+-                    pls->ctx = NULL;
+-                    return ret;
+-                }
+-            }
+-            ret = 0;
+-            /* Reset reading */
+-            ff_format_io_close(pls->parent, &pls->input);
+-            pls->input = NULL;
+-            pls->input_read_done = 0;
+-            ff_format_io_close(pls->parent, &pls->input_next);
+-            pls->input_next = NULL;
+-            pls->input_next_requested = 0;
+-            pls->cur_seg_offset = 0;
+-            pls->cur_init_section = NULL;
+-            /* Reset EOF flag */
+-            pls->pb.pub.eof_reached = 0;
+-            /* Clear any buffered data */
+-            pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer;
+-            /* Reset the position */
+-            pls->pb.pub.pos = 0;
+-        }
+-
+-        /*
+-         * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
+-         * use that information to find the media format, otherwise probe input data
+-         */
+-        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
+-            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
+-            void *iter = NULL;
+-            while ((in_fmt = av_demuxer_iterate(&iter)))
+-                if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id)
+-                    break;
+-        } else {
+-            pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
+-            pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
+-            pls->ctx->interrupt_callback = s->interrupt_callback;
+-            url = av_strdup(pls->segments[0]->url);
+-            ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
+-            if (ret < 0) {
+-                /* Free the ctx - it isn't initialized properly at this point,
+-                * so avformat_close_input shouldn't be called. If
+-                * avformat_open_input fails below, it frees and zeros the
+-                * context, so it doesn't need any special treatment like this. */
+-                av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url);
+-                avformat_free_context(pls->ctx);
+-                pls->ctx = NULL;
+-                av_free(url);
+-                return ret;
+-            }
+-            av_free(url);
+-        }
+-
+-        if (seg && seg->key_type == KEY_SAMPLE_AES) {
+-            if (strstr(in_fmt->name, "mov")) {
+-                char key[33];
+-                ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
+-                av_dict_set(&options, "decryption_key", key, 0);
+-            } else if (!c->crypto_ctx.aes_ctx) {
+-                c->crypto_ctx.aes_ctx = av_aes_alloc();
+-                if (!c->crypto_ctx.aes_ctx) {
+-                    avformat_free_context(pls->ctx);
+-                    pls->ctx = NULL;
+-                    return AVERROR(ENOMEM);
+-                }
+-            }
+-        }
+-
+-        pls->ctx->pb       = &pls->pb.pub;
+-        pls->ctx->io_open  = nested_io_open;
+-        pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
+-
+-        if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+-            return ret;
+-
+-        av_dict_copy(&options, c->seg_format_opts, 0);
+-
+-        ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options);
+-        av_dict_free(&options);
+-        if (ret < 0)
++        ret = reopen_demux_for_playlist(s, pls);
++        if (ret < 0) {
+             return ret;
+-
+-        if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
+-            ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra);
+-            avformat_queue_attached_pictures(pls->ctx);
+-            ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra);
+-            ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
+-        }
+-
+-        if (pls->is_id3_timestamped == -1)
+-            av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
+-
+-        /*
+-         * For ID3 timestamped raw audio streams we need to detect the packet
+-         * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
+-         * but for other streams we can rely on our user calling avformat_find_stream_info()
+-         * on us if they want to.
+-         */
+-        if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
+-            if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
+-                pls->ctx->nb_streams == 1)
+-                ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
+-            else
+-                ret = avformat_find_stream_info(pls->ctx, NULL);
+-
+-            if (ret < 0)
+-                return ret;
+         }
+-
+-        pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
+-
+-        /* Create new AVStreams for each stream in this playlist */
+-        ret = update_streams_from_subdemuxer(s, pls);
+-        if (ret < 0)
+-            return ret;
+-
+-        /*
+-         * Copy any metadata from playlist to main streams, but do not set
+-         * event flags.
+-         */
+-        if (pls->n_main_streams)
+-            av_dict_copy(&pls->main_streams[0]->metadata, pls->ctx->metadata, 0);
+-
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
+     }
+     update_noheader_flag(s);
+@@ -2299,6 +2366,15 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
+                 AVRational tb;
+                 struct segment *seg = NULL;
+                 ret = av_read_frame(pls->ctx, pls->pkt);
++                if (ret < 0 && pls->had_discontinuity) {
++                    av_log(s, AV_LOG_INFO, "Discontinuity - reopening demuxer\n");
++                    if ((ret = reopen_demux_for_playlist(s, pls)) < 0) {
++                        av_log(s, AV_LOG_ERROR, "reopen_demux_for_playlist failed\n");
++                        return ret;
++                    }
++                    pls->had_discontinuity = 0;
++                    continue;
++                }
+                 if (ret < 0) {
+                     if (!avio_feof(&pls->pb.pub) && ret != AVERROR_EOF)
+                         return ret;
+@@ -2323,6 +2399,18 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
+                     memcpy(c->crypto_ctx.key, pls->key, sizeof(pls->key));
+                     ff_hls_senc_decrypt_frame(codec_id, &c->crypto_ctx, pls->pkt);
+                 }
++                if (seg && seg->is_discontinuity) {
++                    /* some demuxers like mpegts will flush remaining data even when an error was
++                     * returned by read, so make sure we don't flag the packet too early
++                     * (for flushes, pls->had_discontinuity will be 1 and ret > 0) */
++                    if (!pls->had_discontinuity) {
++                        /* set custom flag, handled by libcoolstream */
++                        #define AV_PKT_FLAG_DISCONTINUITY 0x1000
++                        pls->pkt->flags |= AV_PKT_FLAG_DISCONTINUITY;
++                        /* only report discontinuity for first packet */
++                        seg->is_discontinuity = 0;
++                    }
++                }
+                 if (pls->seek_timestamp == AV_NOPTS_VALUE)
+                     break;
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0001-ffmpeg-nolog.patch b/archive-patches/ffmpeg-cst/6.1.4/0001-ffmpeg-nolog.patch
new file mode 100644 (file)
index 0000000..ecb6ee4
--- /dev/null
@@ -0,0 +1,24 @@
+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
+@@ -408,10 +408,6 @@ static void (*av_log_callback)(void*, int, const char*, va_list) =
+ void av_log(void* avcl, int level, const char *fmt, ...)
+ {
+-    va_list vl;
+-    va_start(vl, fmt);
+-    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/6.1.4/0002-fix-mpegts.patch b/archive-patches/ffmpeg-cst/6.1.4/0002-fix-mpegts.patch
new file mode 100644 (file)
index 0000000..3976ca7
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/mpegts.c
++++ b/libavformat/mpegts.c
+@@ -1041,10 +1041,12 @@
+     pes->buffer = NULL;
+     reset_pes_packet_state(pes);
++    /*
+     sd = av_packet_new_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, 1);
+     if (!sd)
+         return AVERROR(ENOMEM);
+     *sd = pes->stream_id;
++    */
+     return 0;
+ }
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch b/archive-patches/ffmpeg-cst/6.1.4/0003-allow-to-choose-rtmp-impl-at-runtime.patch
new file mode 100644 (file)
index 0000000..7a7c340
--- /dev/null
@@ -0,0 +1,126 @@
+--- a/configure
++++ b/configure
+@@ -3615,10 +3615,8 @@
+ # protocols
+ async_protocol_deps="threads"
+ bluray_protocol_deps="libbluray"
+-ffrtmpcrypt_protocol_conflict="librtmp_protocol"
+ ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls"
+ ffrtmpcrypt_protocol_select="tcp_protocol"
+-ffrtmphttp_protocol_conflict="librtmp_protocol"
+ ffrtmphttp_protocol_select="http_protocol"
+ ftp_protocol_select="tcp_protocol"
+ gopher_protocol_select="tcp_protocol"
+@@ -3632,20 +3630,18 @@
+ icecast_protocol_select="http_protocol"
+ mmsh_protocol_select="http_protocol"
+ mmst_protocol_select="network"
+-rtmp_protocol_conflict="librtmp_protocol"
+-rtmp_protocol_select="tcp_protocol"
+-rtmp_protocol_suggest="zlib"
+-rtmpe_protocol_select="ffrtmpcrypt_protocol"
+-rtmpe_protocol_suggest="zlib"
+-rtmps_protocol_conflict="librtmp_protocol"
+-rtmps_protocol_select="tls_protocol"
+-rtmps_protocol_suggest="zlib"
+-rtmpt_protocol_select="ffrtmphttp_protocol"
+-rtmpt_protocol_suggest="zlib"
+-rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
+-rtmpte_protocol_suggest="zlib"
+-rtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
+-rtmpts_protocol_suggest="zlib"
++ffrtmp_protocol_select="tcp_protocol"
++ffrtmp_protocol_suggest="zlib"
++ffrtmpe_protocol_select="ffrtmpcrypt_protocol"
++ffrtmpe_protocol_suggest="zlib"
++ffrtmps_protocol_select="tls_protocol"
++ffrtmps_protocol_suggest="zlib"
++ffrtmpt_protocol_select="ffrtmphttp_protocol"
++ffrtmpt_protocol_suggest="zlib"
++ffrtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
++ffrtmpte_protocol_suggest="zlib"
++ffrtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
++ffrtmpts_protocol_suggest="zlib"
+ rtp_protocol_select="udp_protocol"
+ schannel_conflict="openssl gnutls libtls mbedtls"
+ sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags"
+--- a/libavformat/Makefile
++++ b/libavformat/Makefile
+@@ -676,12 +676,12 @@
+ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o asf_tags.o
+ OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
+ OBJS-$(CONFIG_PROMPEG_PROTOCOL)          += prompeg.o
+-OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPE_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTE_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
+-OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMP_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPE_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPS_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPT_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTE_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
++OBJS-$(CONFIG_FFRTMPTS_PROTOCOL)         += rtmpproto.o rtmpdigest.o rtmppkt.o
+ OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o ip.o
+ OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
+ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
+--- a/libavformat/protocols.c
++++ b/libavformat/protocols.c
+@@ -48,12 +48,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;
+--- a/libavformat/rtmpproto.c
++++ b/libavformat/rtmpproto.c
+@@ -2627,7 +2627,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;
+@@ -2638,7 +2638,9 @@
+     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);
+@@ -3185,9 +3187,9 @@
+ #define RTMP_PROTOCOL(flavor, uppercase)         \
+     RTMP_PROTOCOL_3(flavor, CONFIG_ ## uppercase ## _PROTOCOL)
+-RTMP_PROTOCOL(rtmp,   RTMP)
+-RTMP_PROTOCOL(rtmpe,  RTMPE)
+-RTMP_PROTOCOL(rtmps,  RTMPS)
+-RTMP_PROTOCOL(rtmpt,  RTMPT)
+-RTMP_PROTOCOL(rtmpte, RTMPTE)
+-RTMP_PROTOCOL(rtmpts, RTMPTS)
++RTMP_PROTOCOL(ffrtmp,   FFRTMP)
++RTMP_PROTOCOL(ffrtmpe,  FFRTMPE)
++RTMP_PROTOCOL(ffrtmps,  FFRTMPS)
++RTMP_PROTOCOL(ffrtmpt,  FFRTMPT)
++RTMP_PROTOCOL(ffrtmpte, FFRTMPTE)
++RTMP_PROTOCOL(ffrtmpts, FFRTMPTS)
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0004-hls-replace-key-uri.patch b/archive-patches/ffmpeg-cst/6.1.4/0004-hls-replace-key-uri.patch
new file mode 100644 (file)
index 0000000..6b0fa7c
--- /dev/null
@@ -0,0 +1,47 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -226,6 +226,8 @@
+     int http_persistent;
+     int http_multiple;
+     int http_seekable;
++    char *key_uri_replace_old;
++    char *key_uri_replace_new;
+     int seg_max_retry;
+     AVIOContext *playlist_pb;
+     HLSCryptoContext  crypto_ctx;
+@@ -1295,8 +1297,16 @@
+     if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
+         if (strcmp(seg->key, pls->key_url)) {
++            char *key_url = NULL;
+             AVIOContext *pb = NULL;
+-            if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts, NULL) == 0) {
++            if (NULL != c->key_uri_replace_old && \
++                NULL != c-> key_uri_replace_new && \
++                '\0' != c->key_uri_replace_old[0]) {
++                key_url = av_strireplace(seg->key, c->key_uri_replace_old, c->key_uri_replace_new);
++            } else {
++                key_url = seg->key;
++            }
++            if (open_url(pls->parent, &pb, key_url, &c->avio_opts, opts, NULL) == 0) {
+                 ret = avio_read(pb, pls->key, sizeof(pls->key));
+                 if (ret != sizeof(pls->key)) {
+                     av_log(pls->parent, AV_LOG_ERROR, "Unable to read key file %s\n",
+@@ -1308,6 +1318,8 @@
+                        seg->key);
+             }
+             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
++            if (key_url != seg->key)
++                av_free(key_url);
+         }
+     }
+@@ -2579,6 +2591,8 @@
+         OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
+     {"seg_max_retry", "Maximum number of times to reload a segment on error.",
+      OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS},
++    { "key_uri_old", "allow to replace part of AES key uri - old", OFFSET(key_uri_replace_old), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
++    { "key_uri_new", "allow to replace part of AES key uri - new", OFFSET(key_uri_replace_new), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, FLAGS },
+     {NULL}
+ };
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0005-mips64-cpu-detection.patch b/archive-patches/ffmpeg-cst/6.1.4/0005-mips64-cpu-detection.patch
new file mode 100644 (file)
index 0000000..0a3a2ea
--- /dev/null
@@ -0,0 +1,20 @@
+--- a/configure
++++ b/configure
+@@ -6132,17 +6132,10 @@
+     # Check toolchain ISA level
+     if enabled mips64; then
+-        enabled mips64r6 && check_inline_asm mips64r6 '"dlsa $0, $0, $0, 1"' &&
+-            disable mips64r2
+-
+         enabled mips64r2 && check_inline_asm mips64r2 '"dext $0, $0, 0, 1"'
+         disable mips32r6 && disable mips32r5 && disable mips32r2
+     else
+-        enabled mips32r6 && check_inline_asm mips32r6 '"aui $0, $0, 0"' &&
+-            disable mips32r5 && disable mips32r2
+-
+-        enabled mips32r5 && check_inline_asm mips32r5 '"eretnc"'
+         enabled mips32r2 && check_inline_asm mips32r2 '"ext $0, $0, 0, 1"'
+         disable mips64r6 && disable mips64r5 && disable mips64r2
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0006-optimize-aac.patch b/archive-patches/ffmpeg-cst/6.1.4/0006-optimize-aac.patch
new file mode 100644 (file)
index 0000000..d7044a4
--- /dev/null
@@ -0,0 +1,22 @@
+--- a/libavcodec/aacdec_template.c
++++ b/libavcodec/aacdec_template.c
+@@ -2520,7 +2520,7 @@
+  * @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
++++ b/libavcodec/aacps.c
+@@ -397,7 +397,7 @@ static void map_val_20_to_34(INTFLOAT par[PS_MAX_NR_IIDICC])
+     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]);
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0007-increase-buffer-size.patch b/archive-patches/ffmpeg-cst/6.1.4/0007-increase-buffer-size.patch
new file mode 100644 (file)
index 0000000..9a6b05d
--- /dev/null
@@ -0,0 +1,31 @@
+--- a/libavformat/aviobuf.c
++++ b/libavformat/aviobuf.c
+@@ -34,7 +34,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
+@@ -580,16 +580,15 @@
+     }
+     /* make buffer smaller in case it ended up large after probing */
+-    if (s->read_packet && ctx->orig_buffer_size &&
+-        s->buffer_size > ctx->orig_buffer_size  && len >= ctx->orig_buffer_size) {
++    if (s->read_packet && s->buffer_size > max_buffer_size) {
+         if (dst == s->buffer && s->buf_ptr != dst) {
+-            int ret = set_buf_size(s, ctx->orig_buffer_size);
++            int ret = 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;
+         }
+-        len = ctx->orig_buffer_size;
++        len = max_buffer_size;
+     }
+     len = read_packet_wrapper(s, dst, len);
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0008-recheck-discard-flags.patch b/archive-patches/ffmpeg-cst/6.1.4/0008-recheck-discard-flags.patch
new file mode 100644 (file)
index 0000000..04eef54
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -2298,8 +2298,10 @@
+     HLSContext *c = s->priv_data;
+     int ret, i, minplaylist = -1;
+-    recheck_discard_flags(s, c->first_packet);
+-    c->first_packet = 0;
++    if (c->first_packet) {
++        recheck_discard_flags(s, 1);
++        c->first_packet = 0;
++    }
+     for (i = 0; i < c->n_playlists; i++) {
+         struct playlist *pls = c->playlists[i];
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch b/archive-patches/ffmpeg-cst/6.1.4/0009-ffmpeg-fix-edit-list-parsing.patch
new file mode 100644 (file)
index 0000000..2451bc0
--- /dev/null
@@ -0,0 +1,13 @@
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -3966,8 +3966,10 @@
+             if (ctts_data_old && ctts_index_old < ctts_count_old) {
+                 curr_ctts = ctts_data_old[ctts_index_old].duration;
++                /*
+                 av_log(mov->fc, AV_LOG_TRACE, "stts: %"PRId64" ctts: %"PRId64", ctts_index: %"PRId64", ctts_count: %"PRId64"\n",
+                        curr_cts, curr_ctts, ctts_index_old, ctts_count_old);
++                */
+                 curr_cts += curr_ctts;
+                 ctts_sample_old++;
+                 if (ctts_sample_old == ctts_data_old[ctts_index_old].count) {
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0010-ffmpeg-replay_dash.patch b/archive-patches/ffmpeg-cst/6.1.4/0010-ffmpeg-replay_dash.patch
new file mode 100644 (file)
index 0000000..ffb6eb4
--- /dev/null
@@ -0,0 +1,12 @@
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -1400,6 +1400,9 @@
+                 } else {
+                     num = pls->first_seq_no + (((c->publish_time - c->time_shift_buffer_depth + pls->fragment_duration) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+                 }
++            } else if (c->period_start && c->availability_start_time && pls->fragment_timescale) {
++                              num = pls->first_seq_no + ((( get_current_time_in_sec() - (c->availability_start_time + c->period_start)) * pls->fragment_timescale) - pls->fragment_duration)/ pls->fragment_duration;
++
+             } else {
+                 num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+             }
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0011-rtsp.patch b/archive-patches/ffmpeg-cst/6.1.4/0011-rtsp.patch
new file mode 100644 (file)
index 0000000..cb96bdc
--- /dev/null
@@ -0,0 +1,53 @@
+--- a/libavformat/rtsp.c
++++ b/libavformat/rtsp.c
+@@ -2392,6 +2392,8 @@ static int sdp_read_header(AVFormatContext *s)
+     int i, err;
+     char url[MAX_URL_SIZE];
+     AVBPrint bp;
++    const char *p, *sp="", *sources="", *sp2, *sources2;
++    char sources_buf[1024];
+     if (!ff_network_init())
+         return AVERROR(EIO);
+@@ -2413,6 +2415,16 @@ static int sdp_read_header(AVFormatContext *s)
+     av_bprint_finalize(&bp, NULL);
+     if (err) goto fail;
++    /* Search for sources= tag in original URL for rtp protocol only */
++    if (strncmp(s->url, "rtp://", 6) == 0) {
++        p = strchr(s->url, '?');
++        if (p && av_find_info_tag(sources_buf, sizeof(sources_buf), "sources", p)) {
++            /* av_log(s, AV_LOG_VERBOSE, "sdp_read_header found sources %s\n", sources_buf);  */
++            sp = sources_buf;
++            sources = "&sources=";
++        }
++    }
++
+     /* open each RTP stream */
+     for (i = 0; i < rt->nb_rtsp_streams; i++) {
+         char namebuf[50];
+@@ -2432,12 +2445,22 @@ static int sdp_read_header(AVFormatContext *s)
+                 av_dict_free(&opts);
+                 goto fail;
+             }
++
++            /* Prepare to add sources to the url to be opened.
++               Otherwise the join to the source specific muliticast will be missing */
++            sources2 = sources;
++            sp2 = sp;
++            /* ignore sources from original URL, when sources are already set in rtsp_st */
++            if (rtsp_st->nb_include_source_addrs > 0)
++                sources2 = sp2 = "";
++
+             ff_url_join(url, sizeof(url), "rtp", NULL,
+                         namebuf, rtsp_st->sdp_port,
+-                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d",
++                        "?localport=%d&ttl=%d&connect=%d&write_to_source=%d%s%s",
+                         rtsp_st->sdp_port, rtsp_st->sdp_ttl,
+                         rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0,
+-                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0);
++                        rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0,
++                        sources2, sp2);
+             p = strchr(s->url, '?');
+             if (p && av_find_info_tag(buf, sizeof(buf), "localaddr", p))
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0012-dxva2.patch b/archive-patches/ffmpeg-cst/6.1.4/0012-dxva2.patch
new file mode 100644 (file)
index 0000000..4e9dfe8
--- /dev/null
@@ -0,0 +1,107 @@
+--- a/libavcodec/dxva2.c
++++ b/libavcodec/dxva2.c
+@@ -777,16 +777,18 @@
+ #if CONFIG_D3D11VA
+     if (avctx->pix_fmt == AV_PIX_FMT_D3D11)
+         return (intptr_t)frame->data[1];
+-    if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) {
++    if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD && surface) {
+         D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
+         ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc);
+         return viewDesc.Texture2D.ArraySlice;
+     }
+ #endif
+ #if CONFIG_DXVA2
+-    for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) {
+-        if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD && ctx->dxva2.surface[i] == surface)
+-            return i;
++    if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
++        for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) {
++            if (ctx->dxva2.surface[i] == surface)
++                return i;
++        }
+     }
+ #endif
+--- a/libavcodec/dxva2_h264.c
++++ b/libavcodec/dxva2_h264.c
+@@ -507,6 +507,14 @@
+     if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0)
+         return -1;
++
++    // Wait for an I-frame before start decoding. Workaround for ATI UVD and UVD+ GPUs
++    if (!h->got_first_iframe) {
++        if (!(ctx_pic->pp.wBitFields & (1 << 15)))
++            return -1;
++        h->got_first_iframe = 1;
++    }
++
+     ret = ff_dxva2_common_end_frame(avctx, h->cur_pic_ptr->f,
+                                     &ctx_pic->pp, sizeof(ctx_pic->pp),
+                                     &ctx_pic->qm, sizeof(ctx_pic->qm),
+--- a/libavcodec/h264_slice.c
++++ b/libavcodec/h264_slice.c
+@@ -978,6 +978,7 @@
+     h->first_field           = 0;
+     h->prev_interlaced_frame = 1;
++    h->got_first_iframe = 0;
+     init_scan_tables(h);
+     ret = ff_h264_alloc_tables(h);
+--- a/libavcodec/h264dec.c
++++ b/libavcodec/h264dec.c
+@@ -456,6 +456,7 @@
+     h->next_outputed_poc = INT_MIN;
+     h->prev_interlaced_frame = 1;
++    h->got_first_iframe = 0;
+     idr(h);
+     h->poc.prev_frame_num = -1;
+--- a/libavcodec/h264dec.h
++++ b/libavcodec/h264dec.h
+@@ -533,6 +533,8 @@
+      * slices) anymore */
+     int setup_finished;
++    int got_first_iframe;
++
+     int cur_chroma_format_idc;
+     int cur_bit_depth_luma;
+     int16_t slice_row[MAX_SLICES]; ///< to detect when MAX_SLICES is too low
+--- a/libavcodec/vaapi_h264.c
++++ b/libavcodec/vaapi_h264.c
+@@ -314,6 +314,11 @@
+     H264SliceContext *sl = &h->slice_ctx[0];
+     int ret;
++    if (pic->nb_slices == 0) {
++        ret = AVERROR_INVALIDDATA;
++        goto finish;
++    }
++
+     ret = ff_vaapi_decode_issue(avctx, pic);
+     if (ret < 0)
+         goto finish;
+--- a/libavformat/bintext.c
++++ b/libavformat/bintext.c
+@@ -149,7 +149,7 @@
+             return AVPROBE_SCORE_EXTENSION + 1;
+         predict_width(&par, p->buf_size, got_width);
+-        if (par.width < 8)
++        if (par.width <= 0)
+             return 0;
+         calculate_height(&par, p->buf_size);
+         if (par.height <= 0)
+@@ -189,8 +189,6 @@
+             next_tag_read(s, &bin->fsize);
+         if (!bin->width) {
+             predict_width(st->codecpar, bin->fsize, got_width);
+-            if (st->codecpar->width < 8)
+-                return AVERROR_INVALIDDATA;
+             calculate_height(st->codecpar, bin->fsize);
+         }
+         avio_seek(pb, 0, SEEK_SET);
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch b/archive-patches/ffmpeg-cst/6.1.4/0013-add-av_stream_get_first_dts-for-chromium.patch
new file mode 100644 (file)
index 0000000..7ddf525
--- /dev/null
@@ -0,0 +1,29 @@
+--- a/libavformat/avformat.h
++++ b/libavformat/avformat.h
+@@ -1030,6 +1030,10 @@
+ int64_t    av_stream_get_end_pts(const AVStream *st);
+ #endif
++// Chromium: We use the internal field first_dts vvv
++int64_t    av_stream_get_first_dts(const AVStream *st);
++// Chromium: We use the internal field first_dts ^^^
++
+ #define AV_PROGRAM_RUNNING 1
+ /**
+--- a/libavformat/utils.c
++++ b/libavformat/utils.c
+@@ -100,6 +100,13 @@
+     return pkt->size > orig_size ? pkt->size - orig_size : ret;
+ }
++// Chromium: We use the internal field first_dts vvv
++int64_t av_stream_get_first_dts(const AVStream *st)
++{
++  return cffstream(st)->first_dts;
++}
++// Chromium: We use the internal field first_dts ^^^
++
+ int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
+ {
+ #if FF_API_INIT_PACKET
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0014-ffmpeg-whitelist.patch b/archive-patches/ffmpeg-cst/6.1.4/0014-ffmpeg-whitelist.patch
new file mode 100644 (file)
index 0000000..69e520a
--- /dev/null
@@ -0,0 +1,17 @@
+--- a/libavformat/avio.c
++++ b/libavformat/avio.c
+@@ -171,12 +171,12 @@
+                (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/6.1.4/0015-ffmpeg-support-kid-key.patch b/archive-patches/ffmpeg-cst/6.1.4/0015-ffmpeg-support-kid-key.patch
new file mode 100644 (file)
index 0000000..3b4a622
--- /dev/null
@@ -0,0 +1,454 @@
+--- a/doc/demuxers.texi
++++ b/doc/demuxers.texi
+@@ -281,7 +281,11 @@
+ @table @option
+ @item cenc_decryption_key
+-16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++Default 16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++
++ at item cenc_decryption_keys
++Dictionary of 16-byte key ID => 16-byte key, both in hex, to decrypt files encrypted using ISO Common Encryption
++(CENC/AES-128 CTR; ISO/IEC 23001-7).
+ @end table
+@@ -769,7 +773,11 @@
+ specify.
+ @item decryption_key
+-16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++Default 16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
++
++ at item decryption_keys
++Dictionary of 16-byte key ID => 16-byte key, both in hex, to decrypt files encrypted using ISO Common Encryption
++(CENC/AES-128 CTR; ISO/IEC 23001-7).
+ @item max_stts_delta
+ Very high sample deltas written in a trak's stts box may occasionally be intended but usually they are written in
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -153,6 +153,7 @@
+     AVDictionary *avio_opts;
+     int max_url_size;
+     char *cenc_decryption_key;
++    char *cenc_decryption_keys;
+     /* Flags for init section*/
+     int is_init_section_common_video;
+@@ -1906,6 +1907,8 @@
+     if (c->cenc_decryption_key)
+         av_dict_set(&in_fmt_opts, "decryption_key", c->cenc_decryption_key, 0);
++    if (c->cenc_decryption_keys)
++        av_dict_set(&in_fmt_opts, "decryption_keys", c->cenc_decryption_keys, 0);
+     // provide additional information from mpd if available
+     ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
+@@ -2347,7 +2350,8 @@
+         OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+         {.str = "aac,m4a,m4s,m4v,mov,mp4,webm,ts"},
+         INT_MIN, INT_MAX, FLAGS},
+-    { "cenc_decryption_key", "Media decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
++    { "cenc_decryption_key", "Media default decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
++    { "cenc_decryption_keys", "Media decryption keys by KID (hex)", OFFSET(cenc_decryption_keys), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
+     {NULL}
+ };
+--- a/libavformat/isom.h
++++ b/libavformat/isom.h
+@@ -312,8 +312,8 @@
+     void *audible_iv;
+     int audible_iv_size;
+     struct AVAES *aes_decrypt;
+-    uint8_t *decryption_key;
+-    int decryption_key_len;
++    uint8_t *decryption_default_key;
++    int decryption_default_key_len;
+     int enable_drefs;
+     int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
+     int have_read_mfra_size;
+@@ -328,6 +328,7 @@
+     } *avif_info;
+     int avif_info_size;
+     int interleaved_read;
++    AVDictionary* decryption_keys;
+ } MOVContext;
+ int ff_mp4_read_descr_len(AVIOContext *pb);
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -7130,19 +7130,62 @@
+     return 0;
+ }
++static int get_key_from_kid(uint8_t* out, int len, MOVContext *c, AVEncryptionInfo *sample) {
++    AVDictionaryEntry *key_entry_hex;
++    char kid_hex[16*2+1];
++
++    if (c->decryption_default_key && c->decryption_default_key_len != len) {
++        av_log(c->fc, AV_LOG_ERROR, "invalid default decryption key length: got %d, expected %d\n", c->decryption_default_key_len, len);
++        return -1;
++    }
++
++    if (!c->decryption_keys) {
++        av_assert0(c->decryption_default_key);
++        memcpy(out, c->decryption_default_key, len);
++        return 0;
++    }
++
++    if (sample->key_id_size != 16) {
++        av_log(c->fc, AV_LOG_ERROR, "invalid key ID size: got %u, expected 16\n", sample->key_id_size);
++        return -1;
++    }
++
++    ff_data_to_hex(kid_hex, sample->key_id, 16, 1);
++    key_entry_hex = av_dict_get(c->decryption_keys, kid_hex, NULL, AV_DICT_DONT_STRDUP_KEY|AV_DICT_DONT_STRDUP_VAL);
++    if (!key_entry_hex) {
++        if (!c->decryption_default_key) {
++            av_log(c->fc, AV_LOG_ERROR, "unable to find KID %s\n", kid_hex);
++            return -1;
++        }
++        memcpy(out, c->decryption_default_key, len);
++        return 0;
++    }
++    if (strlen(key_entry_hex->value) != len*2) {
++        return -1;
++    }
++    ff_hex_to_data(out, key_entry_hex->value);
++    return 0;
++}
++
+ static int cenc_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+ {
+     int i, ret;
+     int bytes_of_protected_data;
++    uint8_t decryption_key[AES_CTR_KEY_SIZE];
+     if (!sc->cenc.aes_ctr) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctr = av_aes_ctr_alloc();
+         if (!sc->cenc.aes_ctr) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
++        ret = av_aes_ctr_init(sc->cenc.aes_ctr, decryption_key);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7188,15 +7231,21 @@
+     int i, ret;
+     int num_of_encrypted_blocks;
+     uint8_t iv[16];
++    uint8_t decryption_key[16];
+     if (!sc->cenc.aes_ctx) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctx = av_aes_alloc();
+         if (!sc->cenc.aes_ctx) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
++        ret = av_aes_init(sc->cenc.aes_ctx, decryption_key, 16 * 8, 1);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7247,15 +7296,21 @@
+ {
+     int i, ret, rem_bytes;
+     uint8_t *data;
++    uint8_t decryption_key[AES_CTR_KEY_SIZE];
+     if (!sc->cenc.aes_ctr) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctr = av_aes_ctr_alloc();
+         if (!sc->cenc.aes_ctr) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
++        ret = av_aes_ctr_init(sc->cenc.aes_ctr, decryption_key);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7313,15 +7368,21 @@
+     int i, ret, rem_bytes;
+     uint8_t iv[16];
+     uint8_t *data;
++    uint8_t decryption_key[16];
+     if (!sc->cenc.aes_ctx) {
++        ret = get_key_from_kid(decryption_key, sizeof(decryption_key), c, sample);
++        if (ret < 0) {
++            return ret;
++        }
++
+         /* initialize the cipher */
+         sc->cenc.aes_ctx = av_aes_alloc();
+         if (!sc->cenc.aes_ctx) {
+             return AVERROR(ENOMEM);
+         }
+-        ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
++        ret = av_aes_init(sc->cenc.aes_ctx, decryption_key, 16 * 8, 1);
+         if (ret < 0) {
+             return ret;
+         }
+@@ -7464,7 +7525,7 @@
+             return AVERROR_INVALIDDATA;
+         }
+-        if (mov->decryption_key) {
++        if (mov->decryption_keys || mov->decryption_default_key) {
+             return cenc_decrypt(mov, sc, encrypted_sample, pkt->data, pkt->size);
+         } else {
+             size_t size;
+@@ -8613,12 +8674,6 @@
+     MOVAtom atom = { AV_RL32("root") };
+     int i;
+-    if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
+-        av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
+-            mov->decryption_key_len, AES_CTR_KEY_SIZE);
+-        return AVERROR(EINVAL);
+-    }
+-
+     mov->fc = s;
+     mov->trak_index = -1;
+     /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
+@@ -9317,7 +9372,8 @@
+         "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
+         AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
+         .flags = AV_OPT_FLAG_DECODING_PARAM },
+-    { "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
++    { "decryption_key", "The default media decryption key (hex)", OFFSET(decryption_default_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
++    { "decryption_keys", "The media decryption keys by KID (hex)", OFFSET(decryption_keys), AV_OPT_TYPE_DICT, .flags = AV_OPT_FLAG_DECODING_PARAM },
+     { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
+         {.i64 = 0}, 0, 1, FLAGS },
+     { "max_stts_delta", "treat offsets above this value as invalid", OFFSET(max_stts_delta), AV_OPT_TYPE_INT, {.i64 = UINT_MAX-48000*10 }, 0, UINT_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM },
+--- a/tests/fate/mov.mak
++++ b/tests/fate/mov.mak
+@@ -8,6 +8,9 @@
+            fate-mov-3elist-encrypted \
+            fate-mov-frag-encrypted \
+            fate-mov-tenc-only-encrypted \
++           fate-mov-3elist-encrypted-kid \
++           fate-mov-frag-encrypted-kid \
++           fate-mov-tenc-only-encrypted-kid \
+            fate-mov-invalid-elst-entry-count \
+            fate-mov-gpmf-remux \
+            fate-mov-440hz-10ms \
+@@ -57,6 +60,15 @@ fate-mov-frag-encrypted: CMD = framemd5 -decryption_key 123456789012345678901234
+ # Full-sample encryption and constant IV using only tenc atom (no senc/saio/saiz).
+ fate-mov-tenc-only-encrypted: CMD = framemd5 -decryption_key 12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-tenc-only-encrypted.mp4
++# Edit list with encryption, using the decryption_keys option.
++fate-mov-3elist-encrypted-kid: CMD = framemd5 -decryption_keys 12345678901234567890123456789012=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-3elist-encrypted.mov
++
++# Fragmented encryption with senc boxes in movie fragments, using the decryption_keys option.
++fate-mov-frag-encrypted-kid: CMD = framemd5 -decryption_keys abba271e8bcf552bbd2e86a434a9a5d9=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-frag-encrypted.mp4
++
++# Full-sample encryption and constant IV using only tenc atom (no senc/saio/saiz), using the decryption_keys option.
++fate-mov-tenc-only-encrypted-kid: CMD = framemd5 -decryption_keys abba271e8bcf552bbd2e86a434a9a5d9=12345678901234567890123456789012 -i $(TARGET_SAMPLES)/mov/mov-tenc-only-encrypted.mp4
++
+ # Makes sure that the CTTS is also modified when we fix avindex in mov.c while parsing edit lists.
+ fate-mov-elist-starts-ctts-2ndsample: CMD = framemd5 -i $(TARGET_SAMPLES)/mov/mov-elist-starts-ctts-2ndsample.mov
+--- /dev/null
++++ b/tests/ref/fate/mov-3elist-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 640x480
++#sar 0: 0/1
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,   460800, 80fbbdec589e15e6c493b44d243f92a9
++0,          1,          1,        1,   460800, f4b23293bb2ecf69cc3570853d8c56a1
++0,          2,          2,        1,   460800, 0c03ce2c1c6ec405d7455465ecd559a3
++0,          3,          3,        1,   460800, 7921791695537fba2c3c123da4834cb9
++0,          4,          4,        1,   460800, 30c8e2903a561b84d4cbaf95c668d236
++0,          5,          5,        1,   460800, 7ff42e998217c17592ddf6b584f26cef
++0,          6,          6,        1,   460800, 5e402c48bf097db2d31b82bb4194a382
++0,          7,          7,        1,   460800, 824c49e92c8ae6d99a0207b514dd756c
++0,          8,          8,        1,   460800, 24f189216a1d9cf2313b2d6dbe3dbdd3
++0,          9,          9,        1,   460800, 519179a8e74275d26b183374637e003f
++0,         10,         10,        1,   460800, f18331ddcef0adf5b069bfa98baf8db4
++0,         11,         11,        1,   460800, 081f61688690d47dbdddd5384e5d5a70
++0,         12,         12,        1,   460800, 90dbf019b9035433371a8df41a9268b7
++0,         13,         13,        1,   460800, bb5adfb9c66732898b34186eca1667ba
++0,         14,         14,        1,   460800, cc08cfd64f37783ecddaf143f6ad78bc
++0,         15,         15,        1,   460800, b8ae21d024fe4df903d56f4521993c72
++0,         16,         16,        1,   460800, b45a99907f045dcadf0a2befc11555e3
++0,         17,         17,        1,   460800, 603ba935845e65ab6cccbbec88bbf60d
++0,         18,         18,        1,   460800, df80c8d3e6a77258a306903f17995a18
++0,         19,         19,        1,   460800, 4b7e90c0a5fd0e0cd958d47f0afac636
++0,         20,         20,        1,   460800, 9feb6e36182f1745be6387edea240eb6
++0,         21,         21,        1,   460800, 86e6de4bd0a5ff7558f4cf6c1ec3930d
++0,         22,         22,        1,   460800, 726b69df77edbe7b503d4698656d1320
++0,         23,         23,        1,   460800, d282fb7a953ac205b0a43d00c2d60a33
++0,         24,         24,        1,   460800, eece3daa70cc20208dd75d91ac84c8fd
++0,         25,         25,        1,   460800, c86d23e73bcce351fc315fb1f13348da
++0,         26,         26,        1,   460800, 93497b4f7c5ad9d61212239b7c9d2770
++0,         27,         27,        1,   460800, eb217d2c12de67903835a8c58f620488
++0,         28,         28,        1,   460800, d966480867bb54c8cd044f18388ed486
++0,         29,         29,        1,   460800, 3ea6207942b3181fdd8e8aa6cae1062a
++0,         30,         30,        1,   460800, 2620df54aca086ec0fb9527c6e6f5135
++0,         31,         31,        1,   460800, 43bb7320f0bb583188dc965ddbfade90
++0,         32,         32,        1,   460800, 0cddaa04645f804e02f65b0836412113
++0,         33,         33,        1,   460800, 83b2dc95807289d7f4a4632bf18c2e97
++0,         34,         34,        1,   460800, 98134d0e41e6dd12827049ccf33b4669
++0,         35,         35,        1,   460800, 56f55631731fa39c7acbab0afeb2eb1b
++0,         36,         36,        1,   460800, 379c1105be09d836a515dc909455ddf4
++0,         37,         37,        1,   460800, 1df87c47e9d98731faf1c3885b77e5da
++0,         38,         38,        1,   460800, 9a8734bcbfdb4d97e530683b8b556a26
++0,         39,         39,        1,   460800, c7a7990d0cddc5adfbe27da7a42e025e
++0,         40,         40,        1,   460800, 0c81e46011e03be410feaf056207fd55
++0,         41,         41,        1,   460800, ca76e4e63016ff29d8aeeb9cb053bb6c
++0,         42,         42,        1,   460800, cebfbe299c17c1f8fc1e6b189555c3c2
++0,         43,         43,        1,   460800, 4f002c5feca5e75f07089e0df47507dd
++0,         44,         44,        1,   460800, c5fd83fc4a745abee9b3d9a6eec9dd3e
++0,         45,         45,        1,   460800, 57d9bad9b45aa2746de5d8bdc2c24969
++0,         46,         46,        1,   460800, 9831673ad7dec167af4a959f64258949
++0,         47,         47,        1,   460800, 77a1cb208f70f51bcb01e28d8cba73b4
+--- /dev/null
++++ b/tests/ref/fate/mov-frag-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 120x52
++#sar 0: 544/545
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,     9360, 920bdc277a6a31c1daed9aca44b10caf
++0,          1,          1,        1,     9360, f1c0b61fef593de57cb97be7fa846569
++0,          2,          2,        1,     9360, 6ef32d9d4398355aebf6d3fb11d51d3f
++0,          3,          3,        1,     9360, d38fd3ef1e5a92fc109b8dd9eb6dadeb
++0,          4,          4,        1,     9360, 54cc0c8a25d2f14f32663837d5e646f1
++0,          5,          5,        1,     9360, b4b6829726dc3decb8b80ba0c35bcf30
++0,          6,          6,        1,     9360, fca3f941e60a2f0a4ce30d5e0efbec3c
++0,          7,          7,        1,     9360, cda6e26b6c1039ff3d229b262c9210c3
++0,          8,          8,        1,     9360, f0d69255e3a27a8b4ae8a4b7b210929d
++0,          9,          9,        1,     9360, 12cb23dd4e32af9c3b35f943714e3fdd
++0,         10,         10,        1,     9360, 082aaf3216124ddcecb422fe5c832e82
++0,         11,         11,        1,     9360, ff37bb8cd6bd0412a3b3cb45db54afc9
++0,         12,         12,        1,     9360, dfb9085441575732844b6c2f05d5f542
++0,         13,         13,        1,     9360, 0017100feaaa9fc7eacd2447d50d7542
++0,         14,         14,        1,     9360, 4e2f1b8c4e04c59934c2f58541e62613
++0,         15,         15,        1,     9360, 27a44dfea7cd2d30e488194c34ab473c
++0,         16,         16,        1,     9360, fc7b56bd95e990a33cf575d1ef820902
++0,         17,         17,        1,     9360, fa2d1609e69714dffc410e65f3c8b755
++0,         18,         18,        1,     9360, 705d7429f447cb13febe202d567795f2
++0,         19,         19,        1,     9360, 234802ce86e868faaf2cd40a286846ea
++0,         20,         20,        1,     9360, 2f0354b40d211d0a4ade4568bea4f85e
++0,         21,         21,        1,     9360, e96af3b6c0cc931463ca77d6be0f1148
++0,         22,         22,        1,     9360, 04a904d798361959971361401879c7e4
++0,         23,         23,        1,     9360, 2f119642340df6d25362b5590ded46b7
++0,         24,         24,        1,     9360, 5993fca2e60050706f857ac76e48f386
++0,         25,         25,        1,     9360, 2ff3b5775fed3d527bfbbeea786787fe
++0,         26,         26,        1,     9360, 42024dbe23d3fb5b0d8987ae1ce390a8
++0,         27,         27,        1,     9360, d804204f0bd9db5f6a758e2c934d9e38
++0,         28,         28,        1,     9360, e322712e6e34c58ec1a2ab5e2c1e3bfe
++0,         29,         29,        1,     9360, 3975bd1a5f6a6b6260276777f9de611e
++0,         30,         30,        1,     9360, 4388f0412efc6310706a7cdedc859ea9
++0,         31,         31,        1,     9360, b4b9a11b0b86635267345a569640e8d4
++0,         32,         32,        1,     9360, 31879c7b8d6b67a4209ffde786bb8cb4
++0,         33,         33,        1,     9360, 4b6dc02d7c889fe4abd4e013b25f585a
++0,         34,         34,        1,     9360, dc73aae82bd39a1220d1106c8d3e8252
++0,         35,         35,        1,     9360, 54c7dfbd49f312806f6c1a89f7c2c36f
++0,         36,         36,        1,     9360, 150abc64f8994d444a521ea90570443c
++0,         37,         37,        1,     9360, d277cdc7dcadbe0016f2e950459e7ebf
++0,         38,         38,        1,     9360, 2196bf338ead90ea54687b85c73c8229
++0,         39,         39,        1,     9360, 53ce5da5365abc0bd3217dd98e7c465d
++0,         40,         40,        1,     9360, 34ee9832aea55c0c4e6f4381c413c10e
++0,         41,         41,        1,     9360, 1769c7b5849e4681119067a06ac29a4f
++0,         42,         42,        1,     9360, 71f53df739ef283a5184c91ef4b158e8
++0,         43,         43,        1,     9360, d2d394739e9a59c06f0354c16843cb63
++0,         44,         44,        1,     9360, d8e458e92ae29344505a24a3059fc584
++0,         45,         45,        1,     9360, 0f1b11a09911851b798df2ef76253a7f
++0,         46,         46,        1,     9360, 5c4a9f22baecf4e749c0d5c65a4f1007
++0,         47,         47,        1,     9360, 3e2b7e7262fdca08d9d1ef6070125c4b
+--- /dev/null
++++ b/tests/ref/fate/mov-tenc-only-encrypted-kid
+@@ -0,0 +1,57 @@
++#format: frame checksums
++#version: 2
++#hash: MD5
++#tb 0: 1/24
++#media_type 0: video
++#codec_id 0: rawvideo
++#dimensions 0: 1024x436
++#sar 0: 1/1
++#stream#, dts,        pts, duration,     size, hash
++0,          0,          0,        1,   669696, f48f296a85eda5ba069dc851a3228bef
++0,          1,          1,        1,   669696, a50c5f69bfa3387d49b5bdf738e6529c
++0,          2,          2,        1,   669696, 05061299003760f6a4795b408f72aa31
++0,          3,          3,        1,   669696, 2572119f0b0cdd83f8a7e06252cecd3b
++0,          4,          4,        1,   669696, 29fe6a6bdb4a69018e318886a297f07e
++0,          5,          5,        1,   669696, e8233c7fbaecfbff965c7dfdd3982b1b
++0,          6,          6,        1,   669696, d9259df9880ff5d4a4b38282e67f407b
++0,          7,          7,        1,   669696, 3e8d795195038993503ea9ab6984c915
++0,          8,          8,        1,   669696, bc4e2d253b715a34f85aae1b080e3460
++0,          9,          9,        1,   669696, 09aba8b3a96f53f9268e7420a10bfab6
++0,         10,         10,        1,   669696, 179447977dd580da8b35fb5310a809ca
++0,         11,         11,        1,   669696, 7a0eea9d54577990345f5705ab9882be
++0,         12,         12,        1,   669696, 5bb96eb76f461825740e5938456df759
++0,         13,         13,        1,   669696, bd4ac4a760ead774b9422a27dc071964
++0,         14,         14,        1,   669696, 1cc05f760a9b751fc89e77f2bcc97259
++0,         15,         15,        1,   669696, 825d0dee6f0174ba7102892c7de30b4d
++0,         16,         16,        1,   669696, d26a2ef5267f6bb03c4e1d8514eee0df
++0,         17,         17,        1,   669696, c916ffdeadca76596a8f7fd47914b5ef
++0,         18,         18,        1,   669696, 6e085acfa7fee0658ea0ae6188274c17
++0,         19,         19,        1,   669696, 1e95fa5b3561283f05bf0bd44cb91721
++0,         20,         20,        1,   669696, 37e3d135aba9dfb8b87e441753115374
++0,         21,         21,        1,   669696, 9c398310e8564491de624393c16265ce
++0,         22,         22,        1,   669696, c87209e4d2617bc2ab40a75f455f09da
++0,         23,         23,        1,   669696, 2679c2f8d1d1af21982e245945c1ee60
++0,         24,         24,        1,   669696, 6151ab4781f31c5beb66b356ad547122
++0,         25,         25,        1,   669696, f7ef6293bfb3a6a329061cb6a5ed5a38
++0,         26,         26,        1,   669696, 2f6e666d14dfc407ca0c0f347b13eb08
++0,         27,         27,        1,   669696, 3454fa1730d79b1aa8dbbc865dc150f4
++0,         28,         28,        1,   669696, e93dc683e2453419a0419ab9af0f8f95
++0,         29,         29,        1,   669696, 031eb3154f7f83cf86d42bee66be9cf7
++0,         30,         30,        1,   669696, 1205c36723e88811206c68892d3aaed6
++0,         31,         31,        1,   669696, 7dd7a8a19dcd73b31ddc6a6d0c597a42
++0,         32,         32,        1,   669696, 7c91115368ea2531262a1197468bc3f4
++0,         33,         33,        1,   669696, 3cf6d9ba385e0fff76da33299ed5380c
++0,         34,         34,        1,   669696, 859fc8c3ef049e3c1175a85fb0a90a3d
++0,         35,         35,        1,   669696, 1d09ce6c7027103d99a4d5799f6e72ab
++0,         36,         36,        1,   669696, 3dcb8357408ac88abd734128d8f5dd6f
++0,         37,         37,        1,   669696, 4dafce137a0a5178f6efaec878e64d36
++0,         38,         38,        1,   669696, 44c478f29a1399ed03275a7357f57d48
++0,         39,         39,        1,   669696, 6e9edaac7414c0e14591ac3d4d0b1ac4
++0,         40,         40,        1,   669696, 522e4aaeea0825da27f631a9e690d654
++0,         41,         41,        1,   669696, 85f2502a718440834c40051d30f8a65e
++0,         42,         42,        1,   669696, ae8816f7bd4645ef1a17ee6d09b4c8d2
++0,         43,         43,        1,   669696, 914b006fa92f1eb3e590245749f6810d
++0,         44,         44,        1,   669696, 9406901542e94c429dff46108782ed69
++0,         45,         45,        1,   669696, 324c13641c39eef5c476023e358c0391
++0,         46,         46,        1,   669696, 4058e886e17c22e4eb9da1dd0d6ad891
++0,         47,         47,        1,   669696, 9edf9cd15eea985b42fd1f5035b1d693
+-- 
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0016-fixparentheses-warning.patch b/archive-patches/ffmpeg-cst/6.1.4/0016-fixparentheses-warning.patch
new file mode 100644 (file)
index 0000000..c69ab5e
--- /dev/null
@@ -0,0 +1,14 @@
+diff --git a/libavutil/common.h b/libavutil/common.h
+index fd1404be6c..0bb8650e83 100644
+--- a/libavutil/common.h
++++ b/libavutil/common.h
+@@ -351,7 +351,8 @@ static av_always_inline int64_t av_sat_add64_c(int64_t a, int64_t b) {
+     return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
+ #else
+     int64_t s = a+(uint64_t)b;
+-    if ((int64_t)(a^b | ~s^b) >= 0)
++    // PATCH: GCC 4.9.4 complains about missing parentheses when combining bitwise operations.
++    if ((int64_t)((a^b) | (~s^b)) >= 0)
+         return INT64_MAX ^ (b >> 63);
+     return s;
+ #endif
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0020-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch b/archive-patches/ffmpeg-cst/6.1.4/0020-cst-add-ASF-VC1-Annex-G-and-RCV-bitstream-filters.patch
new file mode 100644 (file)
index 0000000..86bdb91
--- /dev/null
@@ -0,0 +1,333 @@
+diff --git a/libavcodec/Makefile b/libavcodec/Makefile
+index 457ec58377..49bf260764 100644
+--- a/libavcodec/Makefile
++++ b/libavcodec/Makefile
+@@ -1209,6 +1209,8 @@ OBJS-$(CONFIG_SETTS_BSF)                  += setts_bsf.o
+ OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
+ OBJS-$(CONFIG_TRACE_HEADERS_BSF)          += trace_headers_bsf.o
+ OBJS-$(CONFIG_TRUEHD_CORE_BSF)            += truehd_core_bsf.o mlp_parse.o mlp.o
++OBJS-$(CONFIG_VC1_ASFTORCV_BSF)           += vc1_asftorcv_bsf.o
++OBJS-$(CONFIG_VC1_ASFTOANNEXG_BSF)        += vc1_asftoannexg_bsf.o vc1.o
+ OBJS-$(CONFIG_VP9_METADATA_BSF)           += vp9_metadata_bsf.o
+ OBJS-$(CONFIG_VP9_RAW_REORDER_BSF)        += vp9_raw_reorder_bsf.o
+ OBJS-$(CONFIG_VP9_SUPERFRAME_BSF)         += vp9_superframe_bsf.o
+diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
+index 444423ae93..482d3473b2 100644
+--- a/libavcodec/bitstream_filters.c
++++ b/libavcodec/bitstream_filters.c
+@@ -59,6 +59,8 @@ extern const FFBitStreamFilter ff_setts_bsf;
+ extern const FFBitStreamFilter ff_text2movsub_bsf;
+ extern const FFBitStreamFilter ff_trace_headers_bsf;
+ extern const FFBitStreamFilter ff_truehd_core_bsf;
++extern const FFBitStreamFilter ff_vc1_asftoannexg_bsf;
++extern const FFBitStreamFilter ff_vc1_asftorcv_bsf;
+ extern const FFBitStreamFilter ff_vp9_metadata_bsf;
+ extern const FFBitStreamFilter ff_vp9_raw_reorder_bsf;
+ extern const FFBitStreamFilter ff_vp9_superframe_bsf;
+diff --git a/libavcodec/vc1_asftoannexg_bsf.c b/libavcodec/vc1_asftoannexg_bsf.c
+new file mode 100644
+index 0000000000..2901f84d9c
+--- /dev/null
++++ b/libavcodec/vc1_asftoannexg_bsf.c
+@@ -0,0 +1,190 @@
++/*
++ * 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"
++#include "bsf_internal.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,
++};
++
++FFBitStreamFilter ff_vc1_asftoannexg_bsf = {
++    .p.name         = "vc1_asftoannexg",
++    .priv_data_size = sizeof(ASFTOANNEXGBSFContext),
++    .filter         = asftoannexg_filter,
++    .close          = asftoannexg_close,
++    .p.codec_ids    = codec_ids,
++};
+diff --git a/libavcodec/vc1_asftorcv_bsf.c b/libavcodec/vc1_asftorcv_bsf.c
+new file mode 100644
+index 0000000000..02e510b4b6
+--- /dev/null
++++ b/libavcodec/vc1_asftorcv_bsf.c
+@@ -0,0 +1,105 @@
++/*
++ * 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"
++#include "bsf_internal.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,
++};
++
++FFBitStreamFilter ff_vc1_asftorcv_bsf = {
++    .p.name       = "vc1_asftorcv",
++    .priv_data_size = sizeof(ASFTORCVBSFContext),
++    .filter         = asftorcv_filter,
++    .p.codec_ids    = codec_ids,
++};
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0021-cst-fix-h264-sps-pps.patch b/archive-patches/ffmpeg-cst/6.1.4/0021-cst-fix-h264-sps-pps.patch
new file mode 100644 (file)
index 0000000..338ff1b
--- /dev/null
@@ -0,0 +1,27 @@
+# Test case: The.Purge.2013.1080p.BluRay.x264.YIFY.mp4
+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 2822644b10..d57647d25c 100644
+--- a/libavcodec/h264_mp4toannexb_bsf.c
++++ b/libavcodec/h264_mp4toannexb_bsf.c
+@@ -242,6 +242,11 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
+             if (!new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80))
+                 new_idr = 1;
+
++            if (s->new_idr && unit_type == H264_NAL_SEI && 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 (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) {
+                 if (ctx->par_out->extradata)
diff --git a/archive-patches/ffmpeg-cst/6.1.4/0022-ffmpeg61-hls-discontinuity.patch b/archive-patches/ffmpeg-cst/6.1.4/0022-ffmpeg61-hls-discontinuity.patch
new file mode 100755 (executable)
index 0000000..3d6c929
--- /dev/null
@@ -0,0 +1,491 @@
+--- a/libavformat/hls.c
++++ b/libavformat/hls.c
+@@ -83,6 +83,7 @@ struct segment {
+     uint8_t iv[16];
+     /* associated Media Initialization Section, treated as a segment */
+     struct segment *init_section;
++    int is_discontinuity;
+ };
+ struct rendition;
+@@ -111,6 +112,7 @@ struct playlist {
+     AVFormatContext *ctx;
+     AVPacket *pkt;
+     int has_noheader_flag;
++    int had_discontinuity;
+     /* main demuxer streams associated with this playlist
+      * indexed by the subdemuxer stream indexes */
+@@ -785,6 +787,7 @@ static int parse_playlist(HLSContext *c,
+     char line[MAX_URL_SIZE];
+     const char *ptr;
+     int close_in = 0;
++    int is_discontinuity = 0;
+     int64_t seg_offset = 0;
+     int64_t seg_size = -1;
+     uint8_t *new_url = NULL;
+@@ -969,6 +972,8 @@ static int parse_playlist(HLSContext *c,
+             ptr = strchr(ptr, '@');
+             if (ptr)
+                 seg_offset = strtoll(ptr+1, NULL, 10);
++        } else if (!strcmp(line, "#EXT-X-DISCONTINUITY")) {
++            is_discontinuity = 1;
+         } else if (av_strstart(line, "#", NULL)) {
+             av_log(c->ctx, AV_LOG_INFO, "Skip ('%s')\n", line);
+             continue;
+@@ -1060,6 +1065,9 @@ static int parse_playlist(HLSContext *c,
+                 }
+                 seg->init_section = cur_init_section;
++
++                seg->is_discontinuity = is_discontinuity;
++                is_discontinuity = 0;
+             }
+         }
+     }
+@@ -1658,7 +1666,8 @@ reload:
+     seg = next_segment(v);
+     if (c->http_multiple == 1 && !v->input_next_requested &&
+-        seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
++        seg && seg->key_type == KEY_NONE && !seg->is_discontinuity &&
++        av_strstart(seg->url, "http", NULL)) {
+         ret = open_input(c, v, seg, &v->input_next);
+         if (ret < 0) {
+             if (ff_check_interrupt(c->interrupt_callback))
+@@ -1700,6 +1709,13 @@ reload:
+     c->cur_seq_no = v->cur_seq_no;
++    /* If a discontinuity was encountered, the caller should re-open the demuxer. */
++    seg = current_segment(v);
++    if (seg && seg->is_discontinuity) {
++        v->had_discontinuity = 1;
++        return AVERROR(EAGAIN);
++    }
++
+     goto restart;
+ }
+@@ -1987,6 +2003,203 @@ static int hls_close(AVFormatContext *s)
+     return 0;
+ }
++static int reopen_demux_for_playlist(AVFormatContext *s, struct playlist *pls)
++{
++    HLSContext *c = s->priv_data;
++    const AVInputFormat *in_fmt = NULL;
++    char *url;
++    AVDictionary *options = NULL;
++    struct segment *seg = NULL;
++    int is_reopen = 0;
++    int ret = 0;
++
++    if (pls->ctx) {
++        is_reopen = 1;
++        av_freep(&pls->pb.pub.buffer);
++        memset(&pls->pb, 0, sizeof(pls->pb));
++        pls->ctx->pb = NULL;
++        avformat_close_input(&pls->ctx);
++
++        /* in case of a discontinuity, forget about all current streams */
++        for (unsigned i = 0; i < s->nb_streams; i++)
++            ff_free_stream(&s->streams[i]);
++        s->nb_streams = 0;
++
++        av_freep(&pls->main_streams);
++        pls->n_main_streams = 0;
++
++        /* duration and start_time will be set based on the new streams */
++        // s->duration = s->start_time = AV_NOPTS_VALUE;
++    }
++
++    if (ff_check_interrupt(&s->interrupt_callback)) {
++        ret = AVERROR_EXIT;
++        goto fail;
++    }
++
++    if (!(pls->ctx = avformat_alloc_context())) {
++        ret = AVERROR(ENOMEM);
++        goto fail;
++    }
++
++    pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
++    if (!pls->read_buffer) {
++        ret = AVERROR(ENOMEM);
++        avformat_free_context(pls->ctx);
++        pls->ctx = NULL;
++        goto fail;
++    }
++
++    ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
++                      read_data, NULL, NULL);
++
++    /*
++     * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
++     * external audio track that contains audio setup information
++     */
++    seg = current_segment(pls);
++    if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
++        pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
++        uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
++        if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
++            /* Fail if error was not end of file */
++            if (ret != AVERROR_EOF) {
++                avformat_free_context(pls->ctx);
++                pls->ctx = NULL;
++                return ret;
++            }
++        }
++        ret = 0;
++        /* Reset reading */
++        ff_format_io_close(pls->parent, &pls->input);
++        pls->input = NULL;
++        pls->input_read_done = 0;
++        ff_format_io_close(pls->parent, &pls->input_next);
++        pls->input_next = NULL;
++        pls->input_next_requested = 0;
++        pls->cur_seg_offset = 0;
++        pls->cur_init_section = NULL;
++        /* Reset EOF flag */
++        pls->pb.pub.eof_reached = 0;
++        /* Clear any buffered data */
++        pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer;
++        /* Reset the position */
++        pls->pb.pub.pos = 0;
++    }
++
++    /*
++     * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
++     * use that information to find the media format, otherwise probe input data
++     */
++    if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
++        pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
++        void *iter = NULL;
++        while ((in_fmt = av_demuxer_iterate(&iter)))
++            if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id)
++                break;
++    } else {
++        pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
++        pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
++        pls->ctx->interrupt_callback = s->interrupt_callback;
++        url = av_strdup(pls->segments[0]->url);
++        ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
++        if (ret < 0) {
++            /* Free the ctx - it isn't initialized properly at this point,
++             * so avformat_close_input shouldn't be called. If
++             * avformat_open_input fails below, it frees and zeros the
++             * context, so it doesn't need any special treatment like this. */
++            av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url);
++            avformat_free_context(pls->ctx);
++            pls->ctx = NULL;
++            av_free(url);
++            return ret;
++        }
++        av_free(url);
++    }
++
++    if (seg && seg->key_type == KEY_SAMPLE_AES) {
++        if (strstr(in_fmt->name, "mov")) {
++            char key[33];
++            ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
++            av_dict_set(&options, "decryption_key", key, 0);
++        } else if (!c->crypto_ctx.aes_ctx) {
++            c->crypto_ctx.aes_ctx = av_aes_alloc();
++            if (!c->crypto_ctx.aes_ctx) {
++                avformat_free_context(pls->ctx);
++                pls->ctx = NULL;
++                return AVERROR(ENOMEM);
++            }
++        }
++    }
++
++    pls->ctx->pb       = &pls->pb.pub;
++    pls->ctx->io_open  = nested_io_open;
++    pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
++
++    if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
++        return ret;
++
++    av_dict_copy(&options, c->seg_format_opts, 0);
++
++    ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options);
++    av_dict_free(&options);
++    if (ret < 0)
++        return ret;
++
++    if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
++        ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra);
++        avformat_queue_attached_pictures(pls->ctx);
++        ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra);
++        ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
++    }
++
++    if (pls->is_id3_timestamped == -1)
++        av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
++
++    /*
++     * For ID3 timestamped raw audio streams we need to detect the packet
++     * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
++     * but for other streams we can rely on our user calling avformat_find_stream_info()
++     * on us if they want to.
++     */
++    if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
++        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
++            pls->ctx->nb_streams == 1)
++            ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
++        else
++            ret = avformat_find_stream_info(pls->ctx, NULL);
++
++        if (ret < 0)
++            return ret;
++    }
++
++    pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
++
++    if (is_reopen) {
++        /* Probe streams so correct timings are available for PTS computation (e.g., aac in mpegts) */
++        avformat_find_stream_info(pls->ctx, NULL);
++    }
++
++    /* Create new AVStreams for each stream in this playlist */
++    ret = update_streams_from_subdemuxer(s, pls);
++    if (ret < 0)
++        return ret;
++
++    /*
++     * Copy any metadata from playlist to main streams, but do not set
++     * event flags.
++     */
++    if (pls->n_main_streams)
++        av_dict_copy(&pls->main_streams[0]->metadata, pls->ctx->metadata, 0);
++
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
++    add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
++
++fail:
++    return ret;
++}
++
+ static int hls_read_header(AVFormatContext *s)
+ {
+     HLSContext *c = s->priv_data;
+@@ -2084,16 +2297,13 @@ static int hls_read_header(AVFormatConte
+     /* Open the demuxer for each playlist */
+     for (i = 0; i < c->n_playlists; i++) {
+         struct playlist *pls = c->playlists[i];
+-        const AVInputFormat *in_fmt = NULL;
+-        char *url;
+-        AVDictionary *options = NULL;
+-        struct segment *seg = NULL;
+-
+-        if (!(pls->ctx = avformat_alloc_context()))
+-            return AVERROR(ENOMEM);
+-        if (pls->n_segments == 0)
++        if (pls->n_segments == 0) {
++            /* Assign an empty context anyway */
++            if (!(pls->ctx = avformat_alloc_context()))
++                return AVERROR(ENOMEM);
+             continue;
++        }
+         pls->index  = i;
+         pls->needed = 1;
+@@ -2111,158 +2321,10 @@ static int hls_read_header(AVFormatConte
+             pls->cur_seq_no = highest_cur_seq_no;
+         }
+-        pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
+-        if (!pls->read_buffer){
+-            avformat_free_context(pls->ctx);
+-            pls->ctx = NULL;
+-            return AVERROR(ENOMEM);
+-        }
+-
+-        ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
+-                          read_data, NULL, NULL);
+-
+-        /*
+-         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
+-         * external audio track that contains audio setup information
+-         */
+-        seg = current_segment(pls);
+-        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
+-            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
+-            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
+-            if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
+-                /* Fail if error was not end of file */
+-                if (ret != AVERROR_EOF) {
+-                    avformat_free_context(pls->ctx);
+-                    pls->ctx = NULL;
+-                    return ret;
+-                }
+-            }
+-            ret = 0;
+-            /* Reset reading */
+-            ff_format_io_close(pls->parent, &pls->input);
+-            pls->input = NULL;
+-            pls->input_read_done = 0;
+-            ff_format_io_close(pls->parent, &pls->input_next);
+-            pls->input_next = NULL;
+-            pls->input_next_requested = 0;
+-            pls->cur_seg_offset = 0;
+-            pls->cur_init_section = NULL;
+-            /* Reset EOF flag */
+-            pls->pb.pub.eof_reached = 0;
+-            /* Clear any buffered data */
+-            pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer;
+-            /* Reset the position */
+-            pls->pb.pub.pos = 0;
+-        }
+-
+-        /*
+-         * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
+-         * use that information to find the media format, otherwise probe input data
+-         */
+-        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
+-            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
+-            void *iter = NULL;
+-            while ((in_fmt = av_demuxer_iterate(&iter)))
+-                if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id)
+-                    break;
+-        } else {
+-            pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
+-            pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
+-            pls->ctx->interrupt_callback = s->interrupt_callback;
+-            url = av_strdup(pls->segments[0]->url);
+-            ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
+-
+-            for (int n = 0; n < pls->n_segments; n++)
+-                if (ret >= 0)
+-                    ret = test_segment(s, in_fmt, pls, pls->segments[n]);
+-
+-            if (ret < 0) {
+-                /* Free the ctx - it isn't initialized properly at this point,
+-                * so avformat_close_input shouldn't be called. If
+-                * avformat_open_input fails below, it frees and zeros the
+-                * context, so it doesn't need any special treatment like this. */
+-                av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url);
+-                avformat_free_context(pls->ctx);
+-                pls->ctx = NULL;
+-                av_free(url);
+-                return ret;
+-            }
+-            av_free(url);
+-        }
+-
+-        if (seg && seg->key_type == KEY_SAMPLE_AES) {
+-            if (strstr(in_fmt->name, "mov")) {
+-                char key[33];
+-                ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
+-                av_dict_set(&options, "decryption_key", key, 0);
+-            } else if (!c->crypto_ctx.aes_ctx) {
+-                c->crypto_ctx.aes_ctx = av_aes_alloc();
+-                if (!c->crypto_ctx.aes_ctx) {
+-                    avformat_free_context(pls->ctx);
+-                    pls->ctx = NULL;
+-                    return AVERROR(ENOMEM);
+-                }
+-            }
+-        }
+-
+-        pls->ctx->pb       = &pls->pb.pub;
+-        pls->ctx->io_open  = nested_io_open;
+-        pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
+-
+-        if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+-            return ret;
+-
+-        av_dict_copy(&options, c->seg_format_opts, 0);
+-
+-        ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options);
+-        av_dict_free(&options);
+-        if (ret < 0)
++        ret = reopen_demux_for_playlist(s, pls);
++        if (ret < 0) {
+             return ret;
+-
+-        if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
+-            ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra);
+-            avformat_queue_attached_pictures(pls->ctx);
+-            ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra);
+-            ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
+         }
+-
+-        if (pls->is_id3_timestamped == -1)
+-            av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
+-
+-        /*
+-         * For ID3 timestamped raw audio streams we need to detect the packet
+-         * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
+-         * but for other streams we can rely on our user calling avformat_find_stream_info()
+-         * on us if they want to.
+-         */
+-        if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
+-            if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
+-                pls->ctx->nb_streams == 1)
+-                ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
+-            else
+-                ret = avformat_find_stream_info(pls->ctx, NULL);
+-
+-            if (ret < 0)
+-                return ret;
+-        }
+-
+-        pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
+-
+-        /* Create new AVStreams for each stream in this playlist */
+-        ret = update_streams_from_subdemuxer(s, pls);
+-        if (ret < 0)
+-            return ret;
+-
+-        /*
+-         * Copy any metadata from playlist to main streams, but do not set
+-         * event flags.
+-         */
+-        if (pls->n_main_streams)
+-            av_dict_copy(&pls->main_streams[0]->metadata, pls->ctx->metadata, 0);
+-
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
+-        add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
+     }
+     update_noheader_flag(s);
+@@ -2372,6 +2434,15 @@ static int hls_read_packet(AVFormatConte
+                 AVRational tb;
+                 struct segment *seg = NULL;
+                 ret = av_read_frame(pls->ctx, pls->pkt);
++                if (ret < 0 && pls->had_discontinuity) {
++                    av_log(s, AV_LOG_INFO, "Discontinuity - reopening demuxer\n");
++                    if ((ret = reopen_demux_for_playlist(s, pls)) < 0) {
++                        av_log(s, AV_LOG_ERROR, "reopen_demux_for_playlist failed\n");
++                        return ret;
++                    }
++                    pls->had_discontinuity = 0;
++                    continue;
++                }
+                 if (ret < 0) {
+                     if (!avio_feof(&pls->pb.pub) && ret != AVERROR_EOF)
+                         return ret;
+@@ -2396,6 +2467,18 @@ static int hls_read_packet(AVFormatConte
+                     memcpy(c->crypto_ctx.key, pls->key, sizeof(pls->key));
+                     ff_hls_senc_decrypt_frame(codec_id, &c->crypto_ctx, pls->pkt);
+                 }
++                if (seg && seg->is_discontinuity) {
++                    /* some demuxers like mpegts will flush remaining data even when an error was
++                     * returned by read, so make sure we don't flag the packet too early
++                     * (for flushes, pls->had_discontinuity will be 1 and ret > 0) */
++                    if (!pls->had_discontinuity) {
++                        /* set custom flag, handled by libcoolstream */
++                        #define AV_PKT_FLAG_DISCONTINUITY 0x1000
++                        pls->pkt->flags |= AV_PKT_FLAG_DISCONTINUITY;
++                        /* only report discontinuity for first packet */
++                        seg->is_discontinuity = 0;
++                    }
++                }
+                 if (pls->seek_timestamp == AV_NOPTS_VALUE)
+                     break;
index 6f7ba905e93f092dc028976e30450072d7bd74ff..e2510258a24629f6f493145162d939121e5068b4 100644 (file)
@@ -79,11 +79,11 @@ FBSHOT_VER = 0.3
 # FFMPEG | A complete, cross-platform solution to record, convert and stream audio and video
 ifeq ($(BOXTYPE), armbox)
 #FFMPEG_VER = 5.1.2
-FFMPEG_VER = 6.1.2
+FFMPEG_VER = 6.1.4
 else ifeq ($(BOXTYPE), coolstream)
 #FFMPEG_GIT = 2ba896f
 #FFMPEG_VER = 3.3
-FFMPEG_VER = 6.1.2
+FFMPEG_VER = 6.1.4
 endif
 
 # FLAC stands for Free Lossless Audio Codec, an audio format similar to MP3, but lossless, meaning that audio is compressed in FLAC without any loss in quality