FFMPEG frame seek bug fix
re: Issue T72347
Blender was using two slightly different strategies to load a video frame depending on if we were advancing to nearby frames or seeking from far away.
Fix:
- Make scanning work the same as forward stepping (by looking ahead).
- Notify the user if we detect a frame synchronization problem. Try to offer a helpful message.
Replicating:
-see T72347
Investigate the problem using a breakpoint:
Checkout 39e37c9db56 "Consistency check for frame scanned pts" in this diff.
Set a breakpoint in source/blender/imbuf/intern/anim_movie.c at the line containing "av_log(anim->pFormatCtx, AV_LOG_ERROR, "PTS for frame differs from previous decode" which should complain on frame 60 in the last step above. If not check preferences->system->sequencer cache limit and make sure it's still 1 !!
I expect to do more testing, specifically related to how it may help ( or not ) audio sync in video editing and work with previous saved blender projects ( including those with video proxies ). Any help here would be appreciated ( recommend users to test, supply files to test etc. ).
Background:
Spoiler: What it all boils down to is that we can only rely on the decoded pts timestamps.
When decoding compressed video frames we have very few "knowns" unless we want to get into working with specific compression codecs individually. We are "flying by wire" to allow the codecs the freedom to prioritize compression/speed/access as they see fit.
What we don't know:
- The packets before decoding are not necessarily in the same order as the decoded frames coming out.
- The presentation timestamp ( PTS) on the encoded packets is not necessarily accurate or present. We can only trust the PTS from the decoded frame ( and remember it does not correlate with the order of the compressed packets going in ).
So all we can do reliably is search forward from an iframe to find a desired pts. This was already being done.
Some timeframes might have 0,1 or multiple images associated with them, therefore the only way to know if we have the proper image is to decode until we decode a PTS meant for the next timeframe.
Strategy:
As currently done, we need to search forward from the nearest i-frame to find the frame we are looking for.
As currently done, we decode as we search forward in order to get an accurate pts.
New: Look ahead. If we search forward and stop when we find a pts for the next frame.
Compatibility with current Proxy timecode files:
The frame finding is done so that the pts_to_search value is an inclusive top end so that exact values will match the current frame ( and thus we can use pts values from the existing indexing methods - and should load old indexes properly ).
Code changes ( all within source/blender/imbuf/intern/anim_movie.c and IMB_movie.h ):
Main goal: We needed to look at the pts for the next frame during scanning in order to find wanted pts. Before this we were only doing this when stepping forward a frame at a time ( and in some cases assuming there was a no missing frame ).
- Change 1) Add in a way to verify we were looking for the same pts as the last time this frame was shown: use arrayFrame2PTS[] to keep track of former search pts results.
- Change 2) Search for PTS based on timecode calculation only.
- Change 3) "Double buffer" ffmpeg_decode_video_frame() so we can look forward at the next pts within ffmpeg_decode_video_frame() for all situations instead of only in forward step searches occuring in ffmpeg_fetchibuf()
- Change 4) Use ffmpeg_decode_video_frame_scan() in the aforementioned forward step search to catch any skipped frames.
- Change 5) Empty double buffer at EOF ( now a bit more complicated, now have anim->pCurrFrameComplete and anim->pNextFrameComplete )
- Change 6) reinitialize arrayFrame2PTS ( and honour numframes from proxied indexing ) if user changes indexing method ( but leave any tracking already done, for better or worse. Handle this better?? ).
Notes:
I originally started coding a fast container dts based index to map video frames to blender frames ( focused on motion matching: ignore timing until later ) .. but decided the way forward was to use the timestamps instead of optimizing on frames. Utilizing every frame independent of timing can still be done with a proxy index although this will lead to audio synchronization issues.
I commented out a line in indexer.c to allow me to build a tc proxy without image proxies during testing. I've left the change in for now but it's not necessary as part of this bug fix. Should this change be left in?
This patch keeps two decode buffers available and uses FFMPEG's reference counting to let the codec decide if it needs to copy the buffer or not. This may affect performance slightly depending on the codec implementation. From my experience this is not an issue.
H264 SPS/PPS issue - iframes are not necessarily independent! H264 AnnexB is the solution.
