Skip to content

Commit

Permalink
common/lwlibav_video.c: fix incorrect seek for some mkv files
Browse files Browse the repository at this point in the history
The issue is that somehow newer mkvmerge generates way less cue points
than ffmpeg and older versions of mkvmerge. This makes av_seek_frame
much less reliable. For the specific test case mentioned in #14, rap
for frame 32 is frame 24, however, the best av_seek_frame can do is
just seek to the very beginning of the video track.

lsmas does not depend on fully reliable av_seek_frame, however, in this
particular cases, because the mkv is using PTS as seeking method, lsmas
will not attempt to call correct_current_frame_number to correct for the
mis-seek, which means it will assume the current position (frame 0) as
actually the rap (frame 24), then it follows that it will return frame
8 as frame 32.

To fix this, we must call correct_current_frame_number even for SEEK_PTS_BASED
formats. This fixes much of the issues, except for the first few frames.

Then there is another issue lurking in correct_current_frame_number:
when it finds that the current packet has a dts of AV_NOPTS_VALUE, it
will always abort. Unfortunately, as frame 0 typically has such a dts,
it will not correct the current frame number when the actual current
is frame 0. We fix this by disregarding pkt->dts == AV_NOPTS_VALUE
when the seek mode is not just SEEK_DTS_BASED (note that mkv has a
seek mode of SEEK_DTS_BASED | SEEK_POS_CORRECTION, which means that
we can reliably use the pkt->pos field to determine the current frame.)

Fixes #14.

Signed-off-by: akarin <[email protected]>
  • Loading branch information
AkarinVS committed Sep 26, 2021
1 parent 4684147 commit 95de1ab
Showing 1 changed file with 9 additions and 2 deletions.
11 changes: 9 additions & 2 deletions common/lwlibav_video.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,14 @@ static uint32_t correct_current_frame_number
order_converter_t *oc = vdhp->order_converter;
video_frame_info_t *info = vdhp->frame_list;
uint32_t p = oc ? oc[i].decoding_to_presentation : i;
if( pkt->dts == AV_NOPTS_VALUE || MATCH_DTS( p ) || MATCH_POS( p ) )
// It is possible that the first frame has dts == AV_NOPTS_VALUE and we happen to seek to frame 0,
// even when rap is strictly > 0. This happens with recent mkvmerge versions where the #cuepoints
// are dramatically cut down and thus av_seek_frame is much less accurate and might give us a (much)
// earlier frame.
// Therefore, we should not just give up when dts == AV_NOPTS_VALUE, and we have to also check some
// others fields, especially when lw_seek_flags is more than just SEEK_DTS_BASED.
if( (pkt->dts == AV_NOPTS_VALUE && ((vdhp->lw_seek_flags & ~SEEK_DTS_BASED) == 0))
|| MATCH_DTS( p ) || MATCH_POS( p ) )
return i;
if( pkt->dts > info[p].dts )
{
Expand Down Expand Up @@ -487,7 +494,7 @@ static int decode_video_picture
return ret;
/* Correct the current picture number in order to match DTS since libavformat might have sought wrong position. */
uint32_t correction_distance = 0;
if( picture_number == rap_number && (vdhp->lw_seek_flags & SEEK_DTS_BASED) )
if( picture_number == rap_number && (vdhp->lw_seek_flags & (SEEK_DTS_BASED | SEEK_PTS_BASED)) )
{
picture_number = correct_current_frame_number( vdhp, pkt, picture_number, goal );
if( picture_number == 0
Expand Down

0 comments on commit 95de1ab

Please sign in to comment.