From 86d9deaee1fc48f8bd4a1a39fa8eaa758fdc51de Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 14 Oct 2024 11:07:09 +0200 Subject: [PATCH] handle seek overshoot --- src/napari_pyav/_reader.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/napari_pyav/_reader.py b/src/napari_pyav/_reader.py index 951e5b1..a39d609 100644 --- a/src/napari_pyav/_reader.py +++ b/src/napari_pyav/_reader.py @@ -82,7 +82,7 @@ def __init__(self, filename, read_format='rgb24', threading=True, thread_count=0 if self.container.format.variable_fps: warn_or_raise(f'Variable frame rate video detected. Seeking will likely be unrealiable.', self.forgiving) if self.stream.codec_context.has_b_frames: - warn_or_raise(f'B-frames detected. Seeking will likely be unrealiable.', self.forgiving) + warn_or_raise(f'B-frames detected. Seeking may be unrealiable.', self.forgiving) def read(self): ''' Read the next frame in the specified format. ''' @@ -114,10 +114,17 @@ def read_frame(self, frame_idx): self.container.seek(target_pts-self.stream.start_time, backward=True, stream=self.container.streams.video[0]) self.framegenerator = self.container.decode(video=0) frame_obj = next(self.framegenerator) + if frame_obj.pts > target_pts: #detecting overshoot (may happen due to variable frame rate) + n_back = 100 + warn_or_raise(f'Seek overshoot ({frame_obj.pts} > {target_pts}). Backtracking by {n_back} frames...', forgiving=True) + self.container.seek(self._frame_to_pts(frame_idx-n_back)-self.stream.start_time, backward=True, stream=self.container.streams.video[0]) + self.framegenerator = self.container.decode(video=0) + frame_obj = next(self.framegenerator) while frame_obj.pts < target_pts: frame_obj = next(self.framegenerator) - if frame_obj.pts > target_pts: - warn_or_raise(f'Seek problem / pts mismatch: {frame_obj.pts} > {target_pts}.', self.forgiving) + # frame_obj.pts should now be equal to target_pts + if frame_obj.pts != target_pts: + warn_or_raise(f'Seek problem / pts mismatch: {frame_obj.pts} != {target_pts}.', self.forgiving) frame = frame_obj.to_ndarray(format=self.read_format) self.last_pts = frame_obj.pts return frame @@ -198,8 +205,8 @@ def static_shape(filename): return shape -def warn_or_raise(msg, forgiving=False): - msg = msg + f' Consider transcoding. Example command: \nffmpeg -y -i "input.mp4" -c:v libx264 -pix_fmt yuv420p -preset superfast -crf 23 "output.mp4"\n' +def warn_or_raise(msg, forgiving=True): + msg = msg + f' Consider transcoding (ffmpeg -y -i "input.mp4" -c:v libx264 -pix_fmt yuv420p -preset superfast -crf 23 "output.mp4").' if forgiving: warnings.warn(msg) else: