diff mbox series

[FFmpeg-devel,v2,13/13] avformat/flvdec: support all multi-track modes

Message ID 20240522182750.17340-1-timo@rothenpieler.org
State New
Headers show
Series None | expand

Commit Message

Timo Rothenpieler May 22, 2024, 6:27 p.m. UTC
---
 libavformat/flvdec.c | 574 +++++++++++++++++++++++--------------------
 1 file changed, 311 insertions(+), 263 deletions(-)
diff mbox series

Patch

diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index 4f98ff348c..fe5a44a715 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -1273,6 +1273,7 @@  static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     FLVContext *flv = s->priv_data;
     int ret, i, size, flags;
+    int res = 0;
     enum FlvTagType type;
     int stream_type=-1;
     int64_t next, pos, meta_pos;
@@ -1287,6 +1288,7 @@  static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
     int pkt_type = 0;
     uint8_t track_idx = 0;
     uint32_t codec_id = 0;
+    int multitrack_type = MultitrackTypeOneTrack;
 
 retry:
     /* pkt size is repeated at end. skip it */
@@ -1337,14 +1339,9 @@  retry:
 
             if (pkt_type == AudioPacketTypeMultitrack) {
                 uint8_t types = avio_r8(s->pb);
-                int multitrack_type = types >> 4;
+                multitrack_type = types & 0xF0;
                 pkt_type = types & 0xF;
 
-                if (multitrack_type != MultitrackTypeOneTrack) {
-                    av_log(s, AV_LOG_ERROR, "Audio multitrack types other than MultitrackTypeOneTrack are unsupported!\n");
-                    return AVERROR_PATCHWELCOME;
-                }
-
                 multitrack = 1;
                 size--;
             }
@@ -1371,14 +1368,9 @@  retry:
 
         if (pkt_type == PacketTypeMultitrack) {
             uint8_t types = avio_r8(s->pb);
-            int multitrack_type = types >> 4;
+            multitrack_type = types & 0xF0;
             pkt_type = types & 0xF;
 
-            if (multitrack_type != MultitrackTypeOneTrack) {
-                av_log(s, AV_LOG_ERROR, "Multitrack types other than MultitrackTypeOneTrack are unsupported!\n");
-                return AVERROR_PATCHWELCOME;
-            }
-
             multitrack = 1;
             size--;
         }
@@ -1447,293 +1439,349 @@  skip:
         goto leave;
     }
 
-    /* now find stream */
-    for (i = 0; i < s->nb_streams; i++) {
-        st = s->streams[i];
-        if (stream_type == FLV_STREAM_TYPE_AUDIO) {
-            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
-                (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags, codec_id)) &&
-                st->id == track_idx)
-                break;
-        } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
-            if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
-                (s->video_codec_id || flv_same_video_codec(st->codecpar, codec_id)) &&
-                st->id == track_idx)
-                break;
-        } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
-            if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
-                break;
-        } else if (stream_type == FLV_STREAM_TYPE_DATA) {
-            if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
-                break;
+    for (;;) {
+        int track_size = size;
+
+        if (multitrack_type != MultitrackTypeOneTrack) {
+            track_size = avio_rb24(s->pb);
+            size -= 3;
         }
-    }
-    if (i == s->nb_streams) {
-        static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
-        st = create_stream(s, stream_types[stream_type], track_idx);
-        if (!st)
-            return AVERROR(ENOMEM);
-    }
-    av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
 
-    if (flv->time_pos <= pos) {
-        dts += flv->time_offset;
-    }
+        /* now find stream */
+        for (i = 0; i < s->nb_streams; i++) {
+            st = s->streams[i];
+            if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+                    (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags, codec_id)) &&
+                    st->id == track_idx)
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+                    (s->video_codec_id || flv_same_video_codec(st->codecpar, codec_id)) &&
+                    st->id == track_idx)
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+                    break;
+            }
+        }
+        if (i == s->nb_streams) {
+            static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
+            st = create_stream(s, stream_types[stream_type], track_idx);
+            if (!st)
+                return AVERROR(ENOMEM);
+        }
+        av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
 
-    if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
-        ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
-         stream_type == FLV_STREAM_TYPE_AUDIO))
-        av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
+        if (flv->time_pos <= pos) {
+            dts += flv->time_offset;
+        }
 
-    if ((st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || stream_type == FLV_STREAM_TYPE_AUDIO)) ||
-        (st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && stream_type == FLV_STREAM_TYPE_VIDEO)) ||
-         st->discard >= AVDISCARD_ALL) {
-        avio_seek(s->pb, next, SEEK_SET);
-        ret = FFERROR_REDO;
-        goto leave;
-    }
+        if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+            ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+             stream_type == FLV_STREAM_TYPE_AUDIO))
+            av_add_index_entry(st, pos, dts, track_size, 0, AVINDEX_KEYFRAME);
+
+        if ((st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || stream_type == FLV_STREAM_TYPE_AUDIO)) ||
+            (st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && stream_type == FLV_STREAM_TYPE_VIDEO)) ||
+             st->discard >= AVDISCARD_ALL) {
+            avio_seek(s->pb, next, SEEK_SET);
+            ret = FFERROR_REDO;
+            goto leave;
+        }
 
-    // if not streamed and no duration from metadata then seek to end to find
-    // the duration from the timestamps
-    if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
-        (!s->duration || s->duration == AV_NOPTS_VALUE) &&
-        !flv->searched_for_end) {
-        int final_size;
-        const int64_t pos   = avio_tell(s->pb);
-        // Read the last 4 bytes of the file, this should be the size of the
-        // previous FLV tag. Use the timestamp of its payload as duration.
-        int64_t fsize       = avio_size(s->pb);
+        // if not streamed and no duration from metadata then seek to end to find
+        // the duration from the timestamps
+        if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+            (!s->duration || s->duration == AV_NOPTS_VALUE) &&
+            !flv->searched_for_end) {
+            int final_size;
+            const int64_t pos   = avio_tell(s->pb);
+            // Read the last 4 bytes of the file, this should be the size of the
+            // previous FLV tag. Use the timestamp of its payload as duration.
+            int64_t fsize       = avio_size(s->pb);
 retry_duration:
-        avio_seek(s->pb, fsize - 4, SEEK_SET);
-        final_size = avio_rb32(s->pb);
-        if (final_size > 0 && final_size < fsize) {
-            // Seek to the start of the last FLV tag at position (fsize - 4 - final_size)
-            // but skip the byte indicating the type.
-            avio_seek(s->pb, fsize - 3 - final_size, SEEK_SET);
-            if (final_size == avio_rb24(s->pb) + 11) {
-                uint32_t ts = avio_rb24(s->pb);
-                ts         |= (unsigned)avio_r8(s->pb) << 24;
-                if (ts)
-                    s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
-                else if (fsize >= 8 && fsize - 8 >= final_size) {
-                    fsize -= final_size+4;
-                    goto retry_duration;
+            avio_seek(s->pb, fsize - 4, SEEK_SET);
+            final_size = avio_rb32(s->pb);
+            if (final_size > 0 && final_size < fsize) {
+                // Seek to the start of the last FLV tag at position (fsize - 4 - final_size)
+                // but skip the byte indicating the type.
+                avio_seek(s->pb, fsize - 3 - final_size, SEEK_SET);
+                if (final_size == avio_rb24(s->pb) + 11) {
+                    uint32_t ts = avio_rb24(s->pb);
+                    ts         |= (unsigned)avio_r8(s->pb) << 24;
+                    if (ts)
+                        s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
+                    else if (fsize >= 8 && fsize - 8 >= final_size) {
+                        fsize -= final_size+4;
+                        goto retry_duration;
+                    }
                 }
             }
+
+            avio_seek(s->pb, pos, SEEK_SET);
+            flv->searched_for_end = 1;
         }
 
-        avio_seek(s->pb, pos, SEEK_SET);
-        flv->searched_for_end = 1;
-    }
+        if (stream_type == FLV_STREAM_TYPE_AUDIO && !enhanced_flv) {
+            int bits_per_coded_sample;
+            channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
+            sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
+                                    FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
+            bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
+            if (!av_channel_layout_check(&st->codecpar->ch_layout) ||
+                !st->codecpar->sample_rate ||
+                !st->codecpar->bits_per_coded_sample) {
+                av_channel_layout_default(&st->codecpar->ch_layout, channels);
+                st->codecpar->sample_rate           = sample_rate;
+                st->codecpar->bits_per_coded_sample = bits_per_coded_sample;
+            }
+            if (!st->codecpar->codec_id) {
+                flv_set_audio_codec(s, st, st->codecpar,
+                                    flags & FLV_AUDIO_CODECID_MASK);
+                flv->last_sample_rate =
+                sample_rate           = st->codecpar->sample_rate;
+                flv->last_channels    =
+                channels              = st->codecpar->ch_layout.nb_channels;
+            } else {
+                AVCodecParameters *par = avcodec_parameters_alloc();
+                if (!par) {
+                    ret = AVERROR(ENOMEM);
+                    goto leave;
+                }
+                par->sample_rate = sample_rate;
+                par->bits_per_coded_sample = bits_per_coded_sample;
+                flv_set_audio_codec(s, st, par, flags & FLV_AUDIO_CODECID_MASK);
+                sample_rate = par->sample_rate;
+                avcodec_parameters_free(&par);
+            }
+        } else if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+            if (!st->codecpar->codec_id)
+                flv_set_audio_codec(s, st, st->codecpar,
+                                    codec_id ? codec_id : (flags & FLV_AUDIO_CODECID_MASK));
+
+            // These are not signalled in the flags anymore
+            channels = 0;
+            sample_rate = 0;
+
+            if (pkt_type == AudioPacketTypeMultichannelConfig) {
+                int channel_order = avio_r8(s->pb);
+                channels = avio_r8(s->pb);
+                size -= 2;
+                track_size -= 2;
+
+                av_channel_layout_uninit(&st->codecpar->ch_layout);
+
+                if (channel_order == AudioChannelOrderCustom) {
+                    ret = av_channel_layout_custom_init(&st->codecpar->ch_layout, channels);
+                    if (ret < 0)
+                        return ret;
+
+                    for (i = 0; i < channels; i++) {
+                        uint8_t id = avio_r8(s->pb);
+                        size--;
+
+                        if (id < 18)
+                            st->codecpar->ch_layout.u.map[i].id = id;
+                        else if (id >= 18 && id <= 23)
+                            st->codecpar->ch_layout.u.map[i].id = id - 18 + AV_CHAN_LOW_FREQUENCY_2;
+                        else if (id == 0xFE)
+                            st->codecpar->ch_layout.u.map[i].id = AV_CHAN_UNUSED;
+                        else
+                            st->codecpar->ch_layout.u.map[i].id = AV_CHAN_UNKNOWN;
+                    }
+                } else if (channel_order == AudioChannelOrderNative) {
+                    uint64_t mask = avio_rb32(s->pb);
+                    size -= 4;
+                    track_size -= 4;
+
+                    // The first 18 entries in the mask match ours, but the remaining 6 entries start at AV_CHAN_LOW_FREQUENCY_2
+                    mask = (mask & 0x3FFFF) | ((mask & 0xFC0000) << (AV_CHAN_LOW_FREQUENCY_2 - 18));
+                    ret = av_channel_layout_from_mask(&st->codecpar->ch_layout, mask);
+                    if (ret < 0)
+                        return ret;
+                } else {
+                    av_channel_layout_default(&st->codecpar->ch_layout, channels);
+                }
+
+                av_log(s, AV_LOG_DEBUG, "Set channel data from MultiChannel info.\n");
 
-    if (stream_type == FLV_STREAM_TYPE_AUDIO && !enhanced_flv) {
-        int bits_per_coded_sample;
-        channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
-        sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
-                                FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
-        bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
-        if (!av_channel_layout_check(&st->codecpar->ch_layout) ||
-            !st->codecpar->sample_rate ||
-            !st->codecpar->bits_per_coded_sample) {
-            av_channel_layout_default(&st->codecpar->ch_layout, channels);
-            st->codecpar->sample_rate           = sample_rate;
-            st->codecpar->bits_per_coded_sample = bits_per_coded_sample;
-        }
-        if (!st->codecpar->codec_id) {
-            flv_set_audio_codec(s, st, st->codecpar,
-                                flags & FLV_AUDIO_CODECID_MASK);
-            flv->last_sample_rate =
-            sample_rate           = st->codecpar->sample_rate;
-            flv->last_channels    =
-            channels              = st->codecpar->ch_layout.nb_channels;
-        } else {
-            AVCodecParameters *par = avcodec_parameters_alloc();
-            if (!par) {
-                ret = AVERROR(ENOMEM);
                 goto leave;
             }
-            par->sample_rate = sample_rate;
-            par->bits_per_coded_sample = bits_per_coded_sample;
-            flv_set_audio_codec(s, st, par, flags & FLV_AUDIO_CODECID_MASK);
-            sample_rate = par->sample_rate;
-            avcodec_parameters_free(&par);
+        } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+            int sret = flv_set_video_codec(s, st, codec_id, 1);
+            if (sret < 0)
+                return sret;
+            size -= sret;
+            track_size -= sret;
+        } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
+            st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+        } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+            st->codecpar->codec_id = AV_CODEC_ID_NONE; // Opaque AMF data
         }
-    } else if (stream_type == FLV_STREAM_TYPE_AUDIO) {
-        if (!st->codecpar->codec_id)
-            flv_set_audio_codec(s, st, st->codecpar,
-                                codec_id ? codec_id : (flags & FLV_AUDIO_CODECID_MASK));
-
-        // These are not signalled in the flags anymore
-        channels = 0;
-        sample_rate = 0;
 
-        if (pkt_type == AudioPacketTypeMultichannelConfig) {
-            int channel_order = avio_r8(s->pb);
-            channels = avio_r8(s->pb);
-            size -= 2;
+        if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+            st->codecpar->codec_id == AV_CODEC_ID_OPUS ||
+            st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
+            st->codecpar->codec_id == AV_CODEC_ID_H264 ||
+            st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
+            st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
+            st->codecpar->codec_id == AV_CODEC_ID_AV1 ||
+            st->codecpar->codec_id == AV_CODEC_ID_VP9) {
+            int type = 0;
+            if (enhanced_flv) {
+                type = pkt_type;
+            } else {
+                type = avio_r8(s->pb);
+                size--;
+                track_size--;
+            }
 
-            av_channel_layout_uninit(&st->codecpar->ch_layout);
+            if (size < 0 || track_size < 0) {
+                ret = AVERROR_INVALIDDATA;
+                goto leave;
+            }
 
-            if (channel_order == AudioChannelOrderCustom) {
-                ret = av_channel_layout_custom_init(&st->codecpar->ch_layout, channels);
-                if (ret < 0)
-                    return ret;
+            if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO && flv->meta_color_info_flag) {
+                flv_update_video_color_info(s, st); // update av packet side data
+                flv->meta_color_info_flag = 0;
+            }
 
-                for (i = 0; i < channels; i++) {
-                    uint8_t id = avio_r8(s->pb);
-                    size--;
-
-                    if (id < 18)
-                        st->codecpar->ch_layout.u.map[i].id = id;
-                    else if (id >= 18 && id <= 23)
-                        st->codecpar->ch_layout.u.map[i].id = id - 18 + AV_CHAN_LOW_FREQUENCY_2;
-                    else if (id == 0xFE)
-                        st->codecpar->ch_layout.u.map[i].id = AV_CHAN_UNUSED;
-                    else
-                        st->codecpar->ch_layout.u.map[i].id = AV_CHAN_UNKNOWN;
+            if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
+                (st->codecpar->codec_id == AV_CODEC_ID_H264 && (!enhanced_flv || type == PacketTypeCodedFrames)) ||
+                (st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) {
+                // sign extension
+                int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
+                pts = av_sat_add64(dts, cts);
+                if (cts < 0) { // dts might be wrong
+                    if (!flv->wrong_dts)
+                        av_log(s, AV_LOG_WARNING,
+                            "Negative cts, previous timestamps might be wrong.\n");
+                    flv->wrong_dts = 1;
+                } else if (FFABS(dts - pts) > 1000*60*15) {
+                    av_log(s, AV_LOG_WARNING,
+                           "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);
+                    dts = pts = AV_NOPTS_VALUE;
                 }
-            } else if (channel_order == AudioChannelOrderNative) {
-                uint64_t mask = avio_rb32(s->pb);
-                size -= 4;
-
-                // The first 18 entries in the mask match ours, but the remaining 6 entries start at AV_CHAN_LOW_FREQUENCY_2
-                mask = (mask & 0x3FFFF) | ((mask & 0xFC0000) << (AV_CHAN_LOW_FREQUENCY_2 - 18));
-                ret = av_channel_layout_from_mask(&st->codecpar->ch_layout, mask);
-                if (ret < 0)
-                    return ret;
-            } else {
-                av_channel_layout_default(&st->codecpar->ch_layout, channels);
+                size -= 3;
+                track_size -= 3;
             }
+            if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+                st->codecpar->codec_id == AV_CODEC_ID_OPUS || st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
+                st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
+                st->codecpar->codec_id == AV_CODEC_ID_AV1 || st->codecpar->codec_id == AV_CODEC_ID_VP9)) {
+                AVDictionaryEntry *t;
+
+                if (st->codecpar->extradata) {
+                    if ((ret = flv_queue_extradata(flv, s->pb, multitrack ? track_idx : stream_type, track_size, multitrack)) < 0)
+                        return ret;
+                    ret = FFERROR_REDO;
+                    goto leave;
+                }
+                if ((ret = flv_get_extradata(s, st, track_size)) < 0)
+                    return ret;
 
-            av_log(s, AV_LOG_DEBUG, "Set channel data from MultiChannel info.\n");
-
-            goto leave;
-        }
-    } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
-        int sret = flv_set_video_codec(s, st, codec_id, 1);
-        if (sret < 0)
-            return sret;
-        size -= sret;
-    } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
-        st->codecpar->codec_id = AV_CODEC_ID_TEXT;
-    } else if (stream_type == FLV_STREAM_TYPE_DATA) {
-        st->codecpar->codec_id = AV_CODEC_ID_NONE; // Opaque AMF data
-    }
+                /* Workaround for buggy Omnia A/XE encoder */
+                t = av_dict_get(s->metadata, "Encoder", NULL, 0);
+                if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
+                    st->codecpar->extradata_size = 2;
 
-    if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
-        st->codecpar->codec_id == AV_CODEC_ID_OPUS ||
-        st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
-        st->codecpar->codec_id == AV_CODEC_ID_H264 ||
-        st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
-        st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
-        st->codecpar->codec_id == AV_CODEC_ID_AV1 ||
-        st->codecpar->codec_id == AV_CODEC_ID_VP9) {
-        int type = 0;
-        if (enhanced_flv) {
-            type = pkt_type;
-        } else {
-            type = avio_r8(s->pb);
-            size--;
+                ret = FFERROR_REDO;
+                goto leave;
+            }
         }
 
-        if (size < 0) {
-            ret = AVERROR_INVALIDDATA;
+        /* skip empty or broken data packets */
+        if (size <= 0 || track_size < 0) {
+            ret = FFERROR_REDO;
             goto leave;
         }
 
-        if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO && flv->meta_color_info_flag) {
-            flv_update_video_color_info(s, st); // update av packet side data
-            flv->meta_color_info_flag = 0;
+        /* skip empty data track */
+        if (!track_size)
+            goto next_track;
+
+        ret = av_get_packet(s->pb, pkt, track_size);
+        if (ret < 0)
+            return ret;
+
+        track_size -= ret;
+        size -= ret;
+
+        pkt->dts          = dts;
+        pkt->pts          = pts == AV_NOPTS_VALUE ? dts : pts;
+        pkt->stream_index = st->index;
+        pkt->pos          = pos;
+        if (!multitrack && flv->new_extradata[stream_type]) {
+            ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+                                          flv->new_extradata[stream_type],
+                                          flv->new_extradata_size[stream_type]);
+            if (ret >= 0) {
+                flv->new_extradata[stream_type]      = NULL;
+                flv->new_extradata_size[stream_type] = 0;
+            }
+        } else if (multitrack
+                   && flv->mt_extradata_cnt > track_idx
+                   && flv->mt_extradata[track_idx]) {
+            ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+                                          flv->mt_extradata[track_idx],
+                                          flv->mt_extradata_sz[track_idx]);
+            if (ret >= 0) {
+                flv->mt_extradata[track_idx]      = NULL;
+                flv->mt_extradata_sz[track_idx] = 0;
+            }
+        }
+        if (stream_type == FLV_STREAM_TYPE_AUDIO && !enhanced_flv &&
+                        (sample_rate != flv->last_sample_rate ||
+                         channels    != flv->last_channels)) {
+            flv->last_sample_rate = sample_rate;
+            flv->last_channels    = channels;
+            ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
         }
 
-        if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
-            (st->codecpar->codec_id == AV_CODEC_ID_H264 && (!enhanced_flv || type == PacketTypeCodedFrames)) ||
-            (st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) {
-            // sign extension
-            int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
-            pts = av_sat_add64(dts, cts);
-            if (cts < 0) { // dts might be wrong
-                if (!flv->wrong_dts)
-                    av_log(s, AV_LOG_WARNING,
-                        "Negative cts, previous timestamps might be wrong.\n");
-                flv->wrong_dts = 1;
-            } else if (FFABS(dts - pts) > 1000*60*15) {
-                av_log(s, AV_LOG_WARNING,
-                       "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);
-                dts = pts = AV_NOPTS_VALUE;
-            }
-            size -= 3;
+        if (stream_type == FLV_STREAM_TYPE_AUDIO ||
+            (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+            stream_type == FLV_STREAM_TYPE_SUBTITLE ||
+            stream_type == FLV_STREAM_TYPE_DATA)
+            pkt->flags |= AV_PKT_FLAG_KEY;
+
+        ret = ff_buffer_packet(s, pkt);
+        if (ret < 0)
+            return ret;
+        res = FFERROR_REDO;
+
+        if (track_size) {
+            av_log(s, AV_LOG_WARNING, "Track size mismatch: %d!\n", track_size);
+            avio_skip(s->pb, track_size);
+            size -= track_size;
         }
-        if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
-            st->codecpar->codec_id == AV_CODEC_ID_OPUS || st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
-            st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC ||
-            st->codecpar->codec_id == AV_CODEC_ID_AV1 || st->codecpar->codec_id == AV_CODEC_ID_VP9)) {
-            AVDictionaryEntry *t;
-
-            if (st->codecpar->extradata) {
-                if ((ret = flv_queue_extradata(flv, s->pb, multitrack ? track_idx : stream_type, size, multitrack)) < 0)
-                    return ret;
-                ret = FFERROR_REDO;
-                goto leave;
-            }
-            if ((ret = flv_get_extradata(s, st, size)) < 0)
-                return ret;
 
-            /* Workaround for buggy Omnia A/XE encoder */
-            t = av_dict_get(s->metadata, "Encoder", NULL, 0);
-            if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
-                st->codecpar->extradata_size = 2;
+        if (!size)
+            break;
 
-            ret = FFERROR_REDO;
-            goto leave;
+next_track:
+        if (multitrack_type == MultitrackTypeOneTrack) {
+            av_log(s, AV_LOG_ERROR, "Attempted to read next track in single-track mode.\n");
+            return AVERROR_BUG;
         }
-    }
-
-    /* skip empty data packets */
-    if (!size) {
-        ret = FFERROR_REDO;
-        goto leave;
-    }
 
-    ret = av_get_packet(s->pb, pkt, size);
-    if (ret < 0)
-        return ret;
-    pkt->dts          = dts;
-    pkt->pts          = pts == AV_NOPTS_VALUE ? dts : pts;
-    pkt->stream_index = st->index;
-    pkt->pos          = pos;
-    if (!multitrack && flv->new_extradata[stream_type]) {
-        int sret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
-                                           flv->new_extradata[stream_type],
-                                           flv->new_extradata_size[stream_type]);
-        if (sret >= 0) {
-            flv->new_extradata[stream_type]      = NULL;
-            flv->new_extradata_size[stream_type] = 0;
+        if (multitrack_type == MultitrackTypeManyTracksManyCodecs) {
+            codec_id = avio_rb32(s->pb);
+            size -= 4;
         }
-    } else if (multitrack
-               && flv->mt_extradata_cnt > track_idx
-               && flv->mt_extradata[track_idx]) {
-        int sret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
-                                           flv->mt_extradata[track_idx],
-                                           flv->mt_extradata_sz[track_idx]);
-        if (sret >= 0) {
-            flv->mt_extradata[track_idx]      = NULL;
-            flv->mt_extradata_sz[track_idx] = 0;
+
+        track_idx = avio_r8(s->pb);
+        size--;
+
+        if (avio_feof(s->pb)) {
+            av_log(s, AV_LOG_WARNING, "premature EOF\n");
+            /* return REDO so that any potentially queued up packages can be drained first */
+            return FFERROR_REDO;
         }
     }
-    if (stream_type == FLV_STREAM_TYPE_AUDIO && !enhanced_flv &&
-                    (sample_rate != flv->last_sample_rate ||
-                     channels    != flv->last_channels)) {
-        flv->last_sample_rate = sample_rate;
-        flv->last_channels    = channels;
-        ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
-    }
-
-    if (stream_type == FLV_STREAM_TYPE_AUDIO ||
-        (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
-        stream_type == FLV_STREAM_TYPE_SUBTITLE ||
-        stream_type == FLV_STREAM_TYPE_DATA)
-        pkt->flags |= AV_PKT_FLAG_KEY;
 
 leave:
     last = avio_rb32(s->pb);
@@ -1755,7 +1803,7 @@  leave:
     if (ret >= 0)
         flv->last_ts = pkt->dts;
 
-    return ret;
+    return ret ? ret : res;
 }
 
 static int flv_read_seek(AVFormatContext *s, int stream_index,