Skip to content

Conversation

@arch1t3cht
Copy link
Contributor

See #456 for samples and discussion, as well as the commit message.

Also, while I'm at it, remove the workaround for the h264 decoder bug again, since FFMS2 requires FFmpeg 7.1 now, which includes the fix.

01cae89 added the AllowHidden parameter
to FrameFromPTS/Pos to be able to recognize the current packet in
DecodePacket, but kept it set to false in the other invocations to
change as little existing behavior as possible.

However, it looks like it's actually correct to also allow hidden frames
in the other invocations in GetFrame, since those call FrameFromPTS to
find out what packet the demuxer ended up at after a seek.

FFMS#456 shows a file where this makes a difference, since its
first few frames are marked as hidden.

Setting AllowHidden to true everywhere in the decoding loop (just not in
GetFrameByTime) also causes no regressions on the Doom9 seeking sample
collection
(https://forum.doom9.org/showthread.php?p=1992736#post1992736).
FFMS2 requires ffmpeg 7.1 now, which fixes this bug.
@arch1t3cht
Copy link
Contributor Author

Welp, this breaks the CI, I'll look into it.

@arch1t3cht arch1t3cht marked this pull request as draft August 29, 2025 11:14
@dwbuiten
Copy link
Member

Looks like qrvideo_24fps_2elist_elist1_ends_bframe.mov broke, yeah. Edit lists are fun.

[  FAILED  ] 2 tests, listed below:
[  FAILED  ] ValidateIndexer/IndexerTest.ReverseAccessingFrame/6, where GetParam() = 24-byte object <10-D4 F2-51 B8-55 00-00 A0-C0 F6-51 B8-55 00-00 2A-00 00-00 00-00 00-00>
[  FAILED  ] ValidateIndexer/IndexerTest.RandomAccessingFrame/6, where GetParam() = 24-byte object <10-D4 F2-51 B8-55 00-00 A0-C0 F6-51 B8-55 00-00 2A-00 00-00 00-00 00-00>

@arch1t3cht
Copy link
Contributor Author

arch1t3cht commented Aug 31, 2025

Funnily enough, only the combination of these two commits breaks the test, each one individually is "fine". But that's just a coincidence.

Here's my understanding of what's happening with this file:

We've got an mp4 file with an edit list (fun) skipping a small part of the video. When demuxing, ffmpeg outputs the following packets (listed in decoding order):

Frame 0 PTS=0 DTS=-1024 OriginalPTS=0 FilePos=36 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 1 PTS=1536 DTS=-512 OriginalPTS=0 FilePos=3267 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 2 PTS=1024 DTS=0 OriginalPTS=0 FilePos=4789 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 3 PTS=512 DTS=512 OriginalPTS=0 FilePos=5160 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 4 PTS=3072 DTS=1024 OriginalPTS=0 FilePos=5514 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 5 PTS=2560 DTS=1536 OriginalPTS=0 FilePos=6704 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 6 PTS=2048 DTS=2048 OriginalPTS=0 FilePos=7221 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 7 PTS=4608 DTS=2560 OriginalPTS=0 FilePos=7559 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 8 PTS=4096 DTS=3072 OriginalPTS=0 FilePos=8232 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 9 PTS=3584 DTS=3584 OriginalPTS=0 FilePos=8588 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 10 PTS=5632 DTS=4096 OriginalPTS=0 FilePos=8907 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 11 PTS=5120 DTS=4608 OriginalPTS=0 FilePos=9782 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 12 PTS=6144 DTS=5120 OriginalPTS=0 FilePos=10157 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 13 PTS=7680 DTS=5632 OriginalPTS=0 FilePos=12807 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 14 PTS=7168 DTS=6144 OriginalPTS=0 FilePos=13300 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 15 PTS=6656 DTS=6656 OriginalPTS=0 FilePos=13570 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 16 PTS=9216 DTS=7168 OriginalPTS=0 FilePos=13849 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 17 PTS=8704 DTS=7680 OriginalPTS=0 FilePos=14128 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 18 PTS=8192 DTS=8192 OriginalPTS=0 FilePos=14376 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 19 PTS=10752 DTS=8704 OriginalPTS=0 FilePos=14594 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 20 PTS=10240 DTS=9216 OriginalPTS=0 FilePos=14885 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 21 PTS=9728 DTS=9728 OriginalPTS=0 FilePos=15088 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 22 PTS=11776 DTS=10240 OriginalPTS=0 FilePos=15291 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 23 PTS=11264 DTS=10752 OriginalPTS=0 FilePos=15513 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 24 PTS=12288 DTS=11264 OriginalPTS=0 FilePos=15724 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 25 PTS=13824 DTS=11776 OriginalPTS=0 FilePos=18359 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 26 PTS=13312 DTS=12288 OriginalPTS=0 FilePos=18903 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 27 PTS=12800 DTS=12800 OriginalPTS=0 FilePos=19241 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 28 PTS=15360 DTS=13312 OriginalPTS=0 FilePos=19514 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 29 PTS=14848 DTS=13824 OriginalPTS=0 FilePos=19946 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 30 PTS=14336 DTS=14336 OriginalPTS=0 FilePos=20192 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 31 PTS=16896 DTS=14848 OriginalPTS=0 FilePos=20421 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 32 PTS=16384 DTS=15360 OriginalPTS=0 FilePos=20754 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 33 PTS=15872 DTS=15872 OriginalPTS=0 FilePos=20977 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 34 PTS=17920 DTS=16384 OriginalPTS=0 FilePos=21232 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 35 PTS=17408 DTS=16896 OriginalPTS=0 FilePos=21531 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 36 PTS=18432 DTS=17408 OriginalPTS=0 FilePos=21832 MarkedHidden=1 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 37 PTS=9216 DTS=8192 OriginalPTS=0 FilePos=15724 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 38 PTS=10752 DTS=8704 OriginalPTS=0 FilePos=18359 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 39 PTS=10240 DTS=9216 OriginalPTS=0 FilePos=18903 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 40 PTS=9728 DTS=9728 OriginalPTS=0 FilePos=19241 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 41 PTS=12288 DTS=10240 OriginalPTS=0 FilePos=19514 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 42 PTS=11776 DTS=10752 OriginalPTS=0 FilePos=19946 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 43 PTS=11264 DTS=11264 OriginalPTS=0 FilePos=20192 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 44 PTS=13824 DTS=11776 OriginalPTS=0 FilePos=20421 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 45 PTS=13312 DTS=12288 OriginalPTS=0 FilePos=20754 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 46 PTS=12800 DTS=12800 OriginalPTS=0 FilePos=20977 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 47 PTS=14848 DTS=13312 OriginalPTS=0 FilePos=21232 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 48 PTS=14336 DTS=13824 OriginalPTS=0 FilePos=21531 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 49 PTS=15360 DTS=14336 OriginalPTS=0 FilePos=21832 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=1 Type=1
Frame 50 PTS=16896 DTS=14848 OriginalPTS=0 FilePos=24569 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 51 PTS=16384 DTS=15360 OriginalPTS=0 FilePos=25225 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 52 PTS=15872 DTS=15872 OriginalPTS=0 FilePos=25539 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 53 PTS=18432 DTS=16384 OriginalPTS=0 FilePos=26789 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 54 PTS=17920 DTS=16896 OriginalPTS=0 FilePos=27295 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 55 PTS=17408 DTS=17408 OriginalPTS=0 FilePos=27576 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 56 PTS=19968 DTS=17920 OriginalPTS=0 FilePos=27912 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 57 PTS=19456 DTS=18432 OriginalPTS=0 FilePos=28285 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 58 PTS=18944 DTS=18944 OriginalPTS=0 FilePos=28555 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3
Frame 59 PTS=20992 DTS=19456 OriginalPTS=0 FilePos=28828 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=2
Frame 60 PTS=20480 DTS=19968 OriginalPTS=0 FilePos=29123 MarkedHidden=0 SecondField=0 RepeatPict=1 Key=0 Type=3

The first edit list entry goes up to PTS 8704, but we get a bunch of additional packets after that marked with AV_PKT_FLAG_DISCARD (I'm guessing to fully flush the reorder buffer, I didn't look too closely into the internals). Then the second edit list entry starts at PTS 9216. Due to this, we have two packets with PTS 9216 - one hidden, and one not (which also have different FilePos values and different data - they just have the same PTS due to edit list shenanigans).

When trying to access a frame from the second section (say frame 59 in 0-indexed decoding order) we seek to the second-to-last keyframe, frame 37 in decoding order. Lavf's seek does correctly put us there and the first packet we decode is the keyframe with PTS 9216 (the second one of the two; frame 37 in decoding order). But when trying to figure out what frame we ended up at based on the PTS, we call FrameFromPTS, which now gives us the first frame with PTS 9216; frame 16 in decoding order. This didn't happen before this PR because the first such frame is marked with AV_PKT_FLAG_DISCARD.

So I understand now why you'd want to exclude hidden frames in the FrameFromPTS call in GetFrame. But since it's also possible for the keyframe we want to seek to to be hidden, we probably can't rely on just the hidden flag to find packets.

Since real goal of the FrameFromPTS/Pos calls (both in GetFrame and DecodePacket) are to try and find the position of a given packet in our index, I feel like the proper solution to this is to make this detection more robust, e.g. using a FindPacket function that takes an entire AVPacket * and tries to find it in the index by comparing all available values (PTS, Pos, flags, etc.). Probably with some sensible fallback sequence in case a full match isn't found.

I'll try writing something like this, but I thought I'd write out my thoughts here first. You probably know a lot more than me about all the various edge cases involved here. For example, is it possible for a packet's FilePos to differ between reads, depending what kinds of seeks came before? And similar questions for the PTS, flags, etc.

@arch1t3cht
Copy link
Contributor Author

#468

@arch1t3cht arch1t3cht closed this Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants