2
votes

I have a .webm file of a recording of a game at 16fps. However, upon trying to process the video with OpenCV, it seems the video is recorded with a variable framerate, so when I try to use OpenCV to get a frame every second by getting the every 16th frame, it won't work since the video stream will end prematurely.

Therefore, I'm trying to convert a variable-frame .webm video, which claims it has a framerate of 16 fps, to a video with a constant frame, so I can extract one frame for every second. I've tried the following ffmpeg command from https://ffmpeg.zeranoe.com/forum/viewtopic.php?t=5518:

ffmpeg -i input.webm -c:v copy -b:v copy -r 16 output.webm

However, the following error will occur:

[NULL @ 00000272ccbc0c40] [Eval @ 000000bc11bfe2f0] Undefined constant or missing '(' in 'copy'
[NULL @ 00000272ccbc0c40] Unable to parse option value "copy"
[NULL @ 00000272ccbc0c40] Error setting option b to value copy.

Error setting up codec context options.

Here's is the code I'm trying to use to process a frame every second:

video = cv2.VideoCapture(test_mp4_vod_path)
print("Opened ", test_mp4_vod_path)
print("Processing MP4 frame by frame")

# forward over to the frames you want to start reading from.
# manually set this, fps * time in seconds you wanna start from
video.set(1, 0)
success, frame = video.read()
#fps = int(video.get(cv2.CAP_PROP_FPS))  # this will return 0!
fps = 16  # hardcode fps
total_frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
print("Loading video %d seconds long with FPS %d and total frame count %d " % (total_frame_count/fps, fps, total_frame_count))

count = 1
while video.isOpened():
    success, frame = video.read()
    if not success:
        break

    if count % fps == 0:
        print("%dth frame is %d seconds on video"%(count, count/fps))
    count += 1

The code will finish before it gets near the end of the video, since the video isn't at a constant FPS. How can I convert a variable-FPS video to a constant FPS video?

2

2 Answers

3
votes

Possible solution (the 2M is a testing value, adjust for your video) :

ffmpeg -i input.we -c:v libvpx-vp9 -minrate 2M -maxrate 2M -b:v 2M -pix_fmt yuv420p -r 16 output.webm
1
votes

First of all, the other answer from VC.One, is very much the answer you need. It is not an exact answer to your question, however.

Your command has a small mistake, which is why the error is thrown. -b:v tells ffmpeg it should set the video bitrate to a given value. In your input, you set it to copy. That isn't a valid value for this option. The bitrate options expect a number and possibly an order of magnitude like 320k or 320000.

Either the intention was to copy the audio codec, in which case it should be -c:a copy, or the intention was to copy the video bitrate. For the latter, just remove the parameter altogether; -c:v copy produces an exact copy of the (selected part of the) video stream, which includes the bitrate, framecount, framerate and timestamps as well as all other video data.

To set up output to have the same video bitrate as the input without copying, use ffprobe to check for the streams bitrate first.