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

Add fps_source option for #404 #516

Merged
merged 6 commits into from
Mar 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions moviepy/video/io/VideoFileClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class VideoFileClip(VideoClip):
The algorithm used for resizing. Default: "bicubic", other popular
options include "bilinear" and "fast_bilinear". For more information, see
https://ffmpeg.org/ffmpeg-scaler.html

fps_source:
The fps value to collect from the metadata. Set by default to 'tbr', but
can be set to 'fps', which may be helpful if importing slow-motion videos
that get messed up otherwise.


Attributes
Expand All @@ -61,7 +66,8 @@ class VideoFileClip(VideoClip):
def __init__(self, filename, has_mask=False,
audio=True, audio_buffersize = 200000,
target_resolution=None, resize_algorithm='bicubic',
audio_fps=44100, audio_nbytes=2, verbose=False):
audio_fps=44100, audio_nbytes=2, verbose=False,
fps_source='tbr'):

VideoClip.__init__(self)

Expand All @@ -70,7 +76,8 @@ def __init__(self, filename, has_mask=False,
self.reader = None # need this just in case FFMPEG has issues (__del__ complains)
self.reader = FFMPEG_VideoReader(filename, pix_fmt=pix_fmt,
target_resolution=target_resolution,
resize_algo=resize_algorithm)
resize_algo=resize_algorithm,
fps_source=fps_source)

# Make some of the reader's attributes accessible from the clip
self.duration = self.reader.duration
Expand Down
43 changes: 31 additions & 12 deletions moviepy/video/io/ffmpeg_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ class FFMPEG_VideoReader:

def __init__(self, filename, print_infos=False, bufsize = None,
pix_fmt="rgb24", check_duration=True,
target_resolution=None, resize_algo='bicubic'):
target_resolution=None, resize_algo='bicubic',
fps_source='tbr'):

self.filename = filename
infos = ffmpeg_parse_infos(filename, print_infos, check_duration)
infos = ffmpeg_parse_infos(filename, print_infos, check_duration,
fps_source)
self.fps = infos['video_fps']
self.size = infos['video_size']

Expand Down Expand Up @@ -221,7 +223,9 @@ def ffmpeg_read_image(filename, with_mask=True):
del reader
return im

def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True):

def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True,
fps_source='tbr'):
"""Get file infos using ffmpeg.

Returns a dictionnary with the fields:
Expand Down Expand Up @@ -301,26 +305,41 @@ def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True):
"Here are the file infos returned by ffmpeg:\n\n%s")%(
filename, infos))


# get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes
# 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).
# Current policy: Trust tbr first, then fps unless fps_source is
# specified as 'fps' in which case try fps then tbr

try:
# 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).

def get_tbr():
match = re.search("( [0-9]*.| )[0-9]* tbr", line)

# Sometimes comes as e.g. 12k. We need to replace that with 12000.
s_tbr = line[match.start():match.end()].split(' ')[1]
if "k" in s_tbr:
tbr = float(s_tbr.replace("k", "")) * 1000
else:
tbr = float(s_tbr)
return tbr

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

fps = float(line[match.start():match.end()].split(' ')[1])
return fps

if fps_source == 'tbr':
try:
result['video_fps'] = get_tbr()
except:
result['video_fps'] = get_fps()

elif fps_source == 'fps':
try:
result['video_fps'] = get_fps()
except:
result['video_fps'] = get_tbr()

# It is known that a fps of 24 is often written as 24000/1001
# but then ffmpeg nicely rounds it to 23.98, which we hate.
Expand Down
3 changes: 3 additions & 0 deletions tests/download_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ def download():

download_url("https://github.com/earney/moviepy_media/raw/master/tests/images/pigs_in_a_polka.gif",
"media/pigs_in_a_polka.gif")

download_url("https://github.com/earney/moviepy_media/raw/master/tests/videos/fire2.mp4",
"media/fire2.mp4")
8 changes: 8 additions & 0 deletions tests/test_PR.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,13 @@ def test_PR_458():
clip = ColorClip([1000, 600], color=(60, 60, 60), duration=10)
clip.write_videofile("test.mp4", progress_bar=False, fps=30)


def test_PR_515():
# Won't actually work until video is in download_media
clip = VideoFileClip("media/fire2.mp4", fps_source='tbr')
assert clip.fps == 90000
clip = VideoFileClip("media/fire2.mp4", fps_source='fps')
assert clip.fps == 10.51

if __name__ == '__main__':
pytest.main()
1 change: 1 addition & 0 deletions tests/test_VideoFileClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ def test_ffmpeg_resizing():
frame = video.get_frame(0)
assert frame.shape[1] == target_resolution[1]


if __name__ == '__main__':
pytest.main()