--- /dev/null
+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;
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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 },
--- /dev/null
+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",
--- /dev/null
+--- 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];
--- /dev/null
+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))
--- /dev/null
+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
+
--- /dev/null
+From 2c6b3f357331e203ad87214984661c40704aceb7 Mon Sep 17 00:00:00 2001
+From: Rainer Hochecker <fernetmenta@online.de>
+Date: Sat, 26 Jan 2019 19:48:35 +0100
+Subject: [PATCH] avcodec/vaapi_h264: skip decode if pic has no slices
+
+This fixes / workarounds https://bugs.freedesktop.org/show_bug.cgi?id=105368.
+It was hit frequently when watching h264 channels received via DVB-X.
+Corresponding kodi bug: https://github.com/xbmc/xbmc/issues/15704
+
+Downloaded from Kodi ffmpeg repo:
+https://github.com/xbmc/FFmpeg/commit/2c6b3f357331e203ad87214984661c40704aceb7
+
+Patch was sent upstream:
+http://ffmpeg.org/pipermail/ffmpeg-devel/2019-March/240863.html
+
+Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
+---
+ libavcodec/vaapi_h264.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/libavcodec/vaapi_h264.c b/libavcodec/vaapi_h264.c
+index dd2a6571604..e521a05c4ff 100644
+--- a/libavcodec/vaapi_h264.c
++++ b/libavcodec/vaapi_h264.c
+@@ -314,6 +314,11 @@ static int vaapi_h264_end_frame(AVCodecContext *avctx)
+ 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;
--- /dev/null
+From 0c288853630b7b4e004774c39945d4a804afcfa8 Mon Sep 17 00:00:00 2001
+From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Date: Fri, 6 Aug 2021 09:17:20 +0200
+Subject: [PATCH] configure: add extralibs to extralibs_xxx
+
+Add extralibs to extralibs_xxx (e.g. extralibs_avformat) to allow
+applications such as motion to retrieve ffmpeg dependencies such as
+-latomic through pkg-config
+
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+[Upstream status: not upstreamable]
+---
+ configure | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/configure b/configure
+index 0bb3a7cf2b..3bda99e415 100755
+--- a/configure
++++ b/configure
+@@ -7918,14 +7918,14 @@
+ source_path=${source_path}
+ LIBPREF=${LIBPREF}
+ LIBSUF=${LIBSUF}
+-extralibs_avutil="$avutil_extralibs"
+-extralibs_avcodec="$avcodec_extralibs"
+-extralibs_avformat="$avformat_extralibs"
+-extralibs_avdevice="$avdevice_extralibs"
+-extralibs_avfilter="$avfilter_extralibs"
+-extralibs_postproc="$postproc_extralibs"
+-extralibs_swscale="$swscale_extralibs"
+-extralibs_swresample="$swresample_extralibs"
++extralibs_avutil="$avutil_extralibs $extralibs"
++extralibs_avcodec="$avcodec_extralibs $extralibs"
++extralibs_avformat="$avformat_extralibs $extralibs"
++extralibs_avdevice="$avdevice_extralibs $extralibs"
++extralibs_avfilter="$avfilter_extralibs $extralibs"
++extralibs_postproc="$postproc_extralibs $extralibs"
++extralibs_swscale="$swscale_extralibs $extralibs"
++extralibs_swresample="$swresample_extralibs $extralibs"
+ EOF
+
+ for lib in $LIBRARY_LIST; do
+--
+2.30.2
+
--- /dev/null
+--- a/libavformat/mov.c
++++ b/libavformat/mov.c
+@@ -3771,8 +3771,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) {
--- /dev/null
+--- a/libavformat/mpegts.c
++++ b/libavformat/mpegts.c
+@@ -1018,10 +1018,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;
+ }
--- /dev/null
+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
+@@ -406,10 +406,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, ...)
--- /dev/null
+--- a/libavformat/dashdec.c
++++ b/libavformat/dashdec.c
+@@ -1401,6 +1401,8 @@ static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
+ } 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;
+ }
# FFMPEG | A complete, cross-platform solution to record, convert and stream audio and video
ifeq ($(BOXTYPE), armbox)
-FFMPEG_VER = 4.4.2
+FFMPEG_VER = 5.1.2
else ifeq ($(BOXTYPE), coolstream)
#FFMPEG_GIT = 2ba896f
#FFMPEG_VER = 3.3