7

I am trying to measure some event in an input video file: "test.mp4". This is done by processing the video in several steps, where each step performs some operations on the video data and writes the intermediate results to a new video file.

The fps of the input video is: 29.42346629489295 fps

Below I have written a script to test the problem. When I write a new file using this script the fps gets rounded in the outputfile to 29.0 fps, and this is the problem.

import cv2
import sys

inputfilepath = "test.mp4"
outputfilepath = "testFps.mp4"

video = cv2.VideoCapture(inputfilepath)

fps = video.get(cv2.CAP_PROP_FPS)
framecount = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))

print("original fps: ", fps)
# fps = 29.4
print("writing fps: ", fps)

writer = cv2.VideoWriter(outputfilepath, 0x00000020, fps, (width, height))

for i in range(framecount):
    success, image = video.read()
    if not success:
        break

    writer.write(image)

writer.release()
video.release()

video = cv2.VideoCapture(outputfilepath)
fps = video.get(cv2.CAP_PROP_FPS)

# the next line will not print the same fps as I passed to cv2.VideoWriter(...) above.
print("output fps: ", fps)
video.release()

I have tried to hardcode different values for fps. It seems like everything below 29.5 fps is rounded to zero decimals (29 fps), and everything above gets round to one decimal (29.x fps)

So my questions are:

Is it possible to get any fps with the mp4 format?

Which fps is actually used in the output file?

How can I get the correct fps in the output file?

Additional info

I tried many different values ranging from 28 fps to 31 fps, and plotted the actual output file framerate vs the expected. This displays some kind of fractral behavior, maybe this hint will inspire some math wizard in here :)

expected vs actual framerate

expected vs actual framerate zoomed

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
smerlung
  • 1,459
  • 1
  • 13
  • 32

2 Answers2

0

OpenCV uses some toolkit to do the writing. In my case, on iOS, OpenCV uses the native AVFoundation library. It seems AVFoundation (or the OpenCV api) can't handle well an fps value with many significant digits, like 29.7787878779, and something was being rounded incorrectly either in OpenCV's api or AVFoundation.

To fix the issue, I rounded off some of the significant digits before calling VideoWriter::open

normalizedFPS = round(1000.0 * normalizedFPS) / 1000.0;

Hope it works for you also!

I've seen 30,000 used as a timescale recommendation, so perhaps test out 1000.0 vs 30,000.0

Danny
  • 397
  • 3
  • 9
0

Unfortunately there is a bug inside OpenCV. I read and write an 46 minutes movie and 2 seconds are missing (same number of frames but a diferent FPS written in the header of the file) For me is a big problem as I tried to join the audio information in another editor and you can see the 2 missing seconds The original movie is fps = (60/1.001) = 59.94005994... and OpenCv is rounding this fps to 60 no matter if I wrote 59.94005994 in 2 places

  1. videoWriter = new VideoWriter( full_file_name, fourCC, fps, scaled_size, true);
  2. videoWriter.set(CAP_PROP_FPS, fps);

BAD NEWS - I found this code inside opencv source code

outfps = cvRound(fps);

bool AVIWriteContainer::initContainer(const String& filename, double fps, Size size, bool iscolor){
    outfps = cvRound(fps);
    width = size.width;
    height = size.height;
    channels = iscolor ? 3 : 1;
    moviPointer = 0;
    bool result = strm->open(filename);
    return result;
}

We have to escalate this bug toward OpenCv team (I use opencv-460.jar) I will try to manualy change the header using other programs - this could save the day

the math computation that explain the missing seconds excel to find the missing seconds missing 2 seconds

Dorin
  • 37
  • 7
  • workaround - I will generate my source file at 60 fps (even if my camera is generating 59.94). I will check the quality of the movie. And then the java transformation will be smooth 60 --> 60. (I will move from 59.94 to 60 with ffmpeg or a classic video editor) – Dorin Nov 22 '22 at 04:18