core: add Previous Frame
As requested by @jbk during last VDD, here the "BE PRAGMATIC" solution for the previous frame.
Add decoder_prevframe.c that handle most of the previous-frame logic: trigger a seek to current_frame - n (n is 10 at the start).
Store all decoded pictures in a lifo until we reach the current frame. Return pictures in previous-frame order (lifo), trigger another seek when empty. In the unlikely case, that the seek missed the current_frame, increase n, and trigger another seek.
Few tricks were needed between decoder.c and es_out.c: if the seek request is too far behind the displayed pts, the input thread needs to continue to demux. This is handled in vlc_input_decoder_IsEmpty(). The addition in this function could trigger a decoder fifo full error. Therefore, pacing was added, in case of previous-frame, in vlc_input_decoder_DecodeWithStatus(). It is safe to pace here, only because the vout won't wait for a new picture (no picture_pool_Wait() in case of previous-frame).
I added ES_OUT_PRIV_RESET_PCR_FRAME_PREV private control, this will be triggering from decoders when doing a seek request to search for a previous frame. This new control won't reset the next/previous frame context, and it will ensure the es_out has enough buffering to reach the previous frame. It will increase the pts_jitter accordingly. This will only be done when search failed a first time with the default seek-back value of 1 frame.
When switching from previous-frame to normal playback or next frame, a last seek to the current position is required, since we drop all frames past the displayed one. This is handled in the new function: vlc_input_decoder_StopFrameNext().
This solution doesn't require more memory, it is able to play backward "in real-time" (when pressing the previous-frame key) most of 1080p videos.
It is also able to play 4K 60FPS videos with few slow-downs (when seeking).
One improvement is possible to make it even faster (and maybe be able to play 4K60fps backward in real-time on recent devices). Double the decoder fifo and decoder_prevframe struct to seek in advance.
DEMO
4K60FPS:
1080p with timestamps displayed:
As requested by @jbk during the tech meeting in FODSEM, here is the simple and "I don't care if you burn CPU and IO" approach :fire::fire::fire:.
When the previous-frame is requested, the input seek back and tell the vout to discard all pictures (except the previous one) until it reach the current displayed one. In that case, it will then display the picture just before. In case of failure (the first picture is after the displayed one), it will send a callback to the input that will try to seek again.
See the new debug log when using Previous Frame:
[00005605af07a420] main input debug: 'previous-frame' took 1 seek requests and 24649 us
Here is a small benchmark when playing only local files:
4K60fps.mp4: between 50ms and 400ms720p.mp4: 30ms4K60fps.mkv: between 200ms and 1second !
The request duration is totally depending on the input type.
TODO:
Fix the video blocked after more than 2 previous frame request and a resume (not systematic).Implement seek strategy using POSITION (if TIME is not available)
Fixes #10252 (closed)