-
I want to get a particular video frame using pyav. I found that Perhaps, I used this method to get the required frame: container.seek(0, whence='time', backward=True) # seek to the starting
frame_num = 30 # the frame I want to seek
time.sleep(0.2) # limit cpu usage
for _ in range(frame_num):
frame = next(container.decode(video=0))
return frame This method works properly but it is too slow while seeking the last frames of any long video file. (with frames>1000) I tried to solve this issue by modifying the seek method : frame_num = 30
framerate = container.streams.video[0].average_rate # get the frame rate
sec = int(frame_num/framerate) # timestamp (sec) for that frame_num
container.seek(sec*1000000, whence='time', backward=True) # seek to that nearest timestamp
sec_frame = sec * framerate # get the key frame number of that timestamp
for _ in range(sec_frame, frame_num):
frame = next(container.decode(video=0))
return frame This reduces the time while seeking any frame of the video, but it doesn't return the proper frame because Is there any other way to get the required frame with pyav? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 13 replies
-
So, I somehow managed to fix the lag issue: frame_num = 1000 # the frame I want
framerate = container.streams.video[0].average_rate # get the frame rate
time_base = container.streams.video[0].time_base # get the time base
sec = int(frame_num/framerate) # timestamp for that frame_num
container.seek(sec*1000000, whence='time', backward=True) # seek to that nearest timestamp
frame = next(container.decode(video=0)) # get the next available frame
sec_frame = int(frame.pts * time_base * framerate) # get the proper key frame number of that timestamp
for _ in range(sec_frame, frame_num):
frame = next(container.decode(video=0))
return frame Now it returns the required frame in less than a second. |
Beta Was this translation helpful? Give feedback.
-
A more reliable but more complex way consists in extracting the position of the keyframes: def _extract_key_frames(av_stream: av.video.stream.VideoStream):
av_stream.container.seek(0, backward=True, any_frame=False, stream=av_stream)
av_stream.codec_context.skip_frame = "NONKEY"
yield from av_stream.container.decode(av_stream)
av_stream.container.seek(0, backward=True, any_frame=False, stream=av_stream)
av_stream.codec_context.skip_frame = "DEFAULT"
key_times: np.ndarray[Fraction] = np.fromiter(
(frame_dates(frame)[0] for frame in _extract_key_frames(stream)),
dtype=object,
) You seek to the previous keyframe (accurate), then you decode each frame until reaching the write timestamp. from cutcutcodec.core.io import read
with read("my_video_file.mp4") as container:
stream = container.out_select("video")[0]
frame = stream.snapshot(120, (stream.height, stream.width)) # seek and get frame at time 2 min In addition to seeingker directly on keyframes, the proposed code performs verifications to ensure that the position after seek is correct. If it isn't, it seeks further back until it does. It's up to you to take inspiration from the source code and adapt it to your own more specific project. |
Beta Was this translation helpful? Give feedback.
So, I somehow managed to fix the lag issue:
Now it returns the required frame in less than a se…