Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Zulko/moviepy
Browse files Browse the repository at this point in the history
  • Loading branch information
bearney74 committed Mar 31, 2017
2 parents 29089b2 + c611107 commit f54556c
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 14 deletions.
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 requirements_docs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Sphinx>=1.5.2
sphinx_rtd_theme>=0.1.10b0
numpydoc>=0.6.0
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()

0 comments on commit f54556c

Please sign in to comment.