Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow motion video massively sped up #404

Closed
tburrows13 opened this issue Feb 12, 2017 · 7 comments
Closed

Slow motion video massively sped up #404

tburrows13 opened this issue Feb 12, 2017 · 7 comments

Comments

@tburrows13
Copy link
Collaborator

I am using Python 3 and I have a few videos that are taken in slow motion. When imported into moviepy, then written to a video file, they are massively sped up (i.e. 1 minutes worth of video in 2 seconds), and then sit on the last frame for the rest of their duration. Note that the videos are supposed to be normal speed for the first couple and last couple of seconds, then slow in the middle.

Unfortunately, I cannot provide the actual video for you to test with, but here is the relevant metadata (fetched with the command ffprobe filename.mov)

Stream #0:1(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720, 7138 kb/s, 42.54 fps, 240 tbr, 2400 tbn, 4800 tbc (default)

And from a video taken (I think) taken on the same phone but without the slo-mo setting for comparison:

Stream #0:1(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720, 10656 kb/s, 30.01 fps, 30 tbr, 600 tbn, 1200 tbc (default)

If I run

>>> clip = VideoFileClip("path/to/file.mp4")
>>> print(clip.fps)

then the first video prints 2400 (not a typo from me!), and the second 30. Why might this be? It is supposed to read tbr first (see below) but it doesn't.

Here is the moviepy code (in moviepy/video/io/ffmpeg_reader.py at line 293) that gets the fps for reference:

# Get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes
# tbc, and sometimes tbc/2...
# Current policy: Trust tbr first, then fps. If result is near from x*1000/1001
# where x is 23,24,25,50, replace by x*1000/1001 (very common case for the fps).

try:
    match = re.search("( [0-9]*.| )[0-9]* tbr", line)
    tbr = float(line[match.start():match.end()].split(' ')[1])
    result['video_fps'] = tbr

except:
    match = re.search("( [0-9]*.| )[0-9]* fps", line)
    result['video_fps'] = float(line[match.start():match.end()].split(' ')[1])

Does anyone know what is happening?

@tburrows13
Copy link
Collaborator Author

tburrows13 commented Feb 28, 2017

I've just worked out that if I just let it read the fps metadata, then everything works fine, I can later change clip.fps and it all works. However, if it reads the tbr metadata (which it does by default) then even if I reassign clip.fps later it is still messed up. Can we look into this please?

So I have changed in ffmpeg_reader.py from

# Get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes
# tbc, and sometimes tbc/2...
# Current policy: Trust tbr first, then fps. If result is near from x*1000/1001
# where x is 23,24,25,50, replace by x*1000/1001 (very common case for the fps).

try:
    match = re.search("( [0-9]*.| )[0-9]* tbr", line)
    tbr = float(line[match.start():match.end()].split(' ')[1])
    result['video_fps'] = tbr

except:
    match = re.search("( [0-9]*.| )[0-9]* fps", line)
    result['video_fps'] = float(line[match.start():match.end()].split(' ')[1])

to

# tbc, and sometimes tbc/2...
# Current policy: Trust tbr first, then fps. If result is near from x*1000/1001
# where x is 23,24,25,50, replace by x*1000/1001 (very common case for the fps).

match = re.search("( [0-9]*.| )[0-9]* fps", line)
result['video_fps'] = float(line[match.start():match.end()].split(' ')[1])

for my personal use. This feels a bit hacky, and I'm sure there is a better way.

@tburrows13
Copy link
Collaborator Author

Reply from Zulko on Gitter:

That problem is actually a nightmare, and I think my solution is the one with the highest success rate (i may be wrong... but that issue is relatively rare with the current setup). One solution to it would be to keep the default as it actually is, but include a parameter in VideoFileClip called 'fps_source' which would be set to 'tbr' by default but could be changed to 'fps' so that people in your situation have a way out (they would have to find that they can do that in the docs though)

@ghost
Copy link

ghost commented Mar 29, 2017

@Gloin1313 if you can clone and add one of your slow motion videos to moviepy_media/tests/videos/, I can merge that to the repo, and we can add that url to moviepy for testing purposes. I doubt the movie clip would need to be more than 4 or 5 seconds in length. but you can check the fps and tbr with ffmpeg -i to see that it contains the values you expect.

tburrows13 pushed a commit that referenced this issue Mar 30, 2017
@tburrows13
Copy link
Collaborator Author

#516 adds the parameter 'fps_source' that can be set to 'fps' to prevent this issue.

@yeonghoey
Copy link

yeonghoey commented Jun 27, 2020

I ran into this issue while using moivepy on video files recorded by Windows Game DVR capturing feature.

When I insepected the files in question using ffprove, it reported 34.35 fps, 240 tbr, where 240 would probably be the refresh rate of my monitor. After taking a lot of time researching and troubleshooting, I finally ended up here and fixed it by setting fps_source='fps'.

I couldn't found any official information explaining clearly on the differences and best practices when to use fps, tbr, but according to some dicussions on the Internet, it seems that tbr is just the best guess of the video's framerate when fps is not available.

I understand that it needs to be somewhat conservative when it comes to changing default behavior, but shouldn't we at least consider to set fps_source='fps' by default, so that our future users don't waste their time on this issue like me? I can see there's already a fallback routine and it seems it won't be that destructive..

@tburrows13
Copy link
Collaborator Author

Hello, I’ve been considering this issue again recently, and I’ve also come to the conclusion that we should use fps_source=“fps” by default in the future. I’d expect that I’ll implement along with some other ffmpeg duration/fps fixes that I’m planning for v2.0.

@yeonghoey
Copy link

Wow, thanks for the quick update! Cool, I'll look forward to it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants