def test_ffmpeg_parse_infos():
    d=ffmpeg_parse_infos("media/big_buck_bunny_432_433.webm")
    assert d['duration'] == 1.0

    d=ffmpeg_parse_infos("media/pigs_in_a_polka.gif")
    assert d['video_size'] == [314, 273]
    assert d['duration'] == 3.0
Beispiel #2
0
def test_ffmpeg_parse_infos():
    d=ffmpeg_parse_infos("media/big_buck_bunny_432_433.webm")
    assert d['duration'] == 1.0

    d=ffmpeg_parse_infos("media/pigs_in_a_polka.gif")
    assert d['video_size'] == [314, 273]
    assert d['duration'] == 3.0
    assert not d['audio_found']

    d=ffmpeg_parse_infos("media/video_with_failing_audio.mp4")
    assert d['audio_found']
    assert d['audio_fps'] == 44100

    d=ffmpeg_parse_infos("media/crunching.mp3")
    assert d['audio_found']
    assert d['audio_fps'] == 48000
def test_ffmpeg_parse_infos_decode_file(decode_file, expected_duration):
    """Test `decode_file` argument of `ffmpeg_parse_infos` function."""
    d = ffmpeg_parse_infos("media/big_buck_bunny_0_30.webm", decode_file=decode_file)
    assert d["duration"] == expected_duration

    # check metadata is fine
    assert len(d["metadata"]) == 1

    # check input
    assert len(d["inputs"]) == 1

    # check streams
    streams = d["inputs"][0]["streams"]
    assert len(streams) == 2
    assert streams[0]["stream_type"] == "video"
    assert streams[0]["stream_number"] == 0
    assert streams[0]["fps"] == 24
    assert streams[0]["size"] == [1280, 720]
    assert streams[0]["default"] is True
    assert streams[0]["language"] is None

    assert streams[1]["stream_type"] == "audio"
    assert streams[1]["stream_number"] == 1
    assert streams[1]["fps"] == 44100
    assert streams[1]["default"] is True
    assert streams[1]["language"] is None
Beispiel #4
0
def test_ffmpeg_parse_infos():
    d=ffmpeg_parse_infos("media/big_buck_bunny_432_433.webm")
    assert d['duration'] == 1.0

    d=ffmpeg_parse_infos("media/pigs_in_a_polka.gif")
    assert d['video_size'] == [314, 273]
    assert d['duration'] == 3.0
    assert not d['audio_found']

    d=ffmpeg_parse_infos("media/video_with_failing_audio.mp4")
    assert d['audio_found']
    assert d['audio_fps'] == 44100

    d=ffmpeg_parse_infos("media/crunching.mp3")
    assert d['audio_found']
    assert d['audio_fps'] == 48000
Beispiel #5
0
def copy(video, output, fps, verbose):
    """Create a copy of a video with different settings.

    Currently, this can be used to change the frame rate, but hopefully this
    will later support other tasks like changing the resolution."""
    import subprocess
    from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos
    from moviepy.config import get_setting

    if verbose:
        logging.getLogger().setLevel(logging.INFO)

    if fps is None:
        raise click.BadParameter(
            'copy currently only supports changing frame rate.')
    original_fps = ffmpeg_parse_infos(video)['video_fps']
    fps_scale = original_fps / fps
    cmd = [
        get_setting("FFMPEG_BINARY"), "-i",
        str(video), "-vf", 'setpts={}*PTS'.format(fps_scale), '-r',
        str(fps),
        str(output)
    ]
    logging.info('Running command: {}'.format(' '.join(cmd)))

    try:
        output = subprocess.check_output(cmd, stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as e:
        logging.exception('[vid] Command returned an error: ')
        logging.fatal(e.decode('utf8'))
        return
Beispiel #6
0
    def __init__(self,
                 filename,
                 buffersize,
                 print_infos=False,
                 fps=44100,
                 nbytes=2,
                 nchannels=2):

        self.filename = filename
        self.nbytes = nbytes
        self.fps = fps
        self.f = "s%dle" % (8 * nbytes)
        self.acodec = "pcm_s%dle" % (8 * nbytes)
        self.nchannels = nchannels
        infos = ffmpeg_parse_infos(filename)
        self.duration = infos["duration"]
        if "video_duration" in infos:
            self.duration = infos["video_duration"]
        else:
            self.duration = infos["duration"]
        self.bitrate = infos["audio_bitrate"]
        self.infos = infos
        self.proc = None

        self.nframes = int(self.fps * self.duration)
        self.buffersize = min(self.nframes + 1, buffersize)
        self.buffer = None
        self.buffer_startframe = 1
        self.initialize()
        self.buffer_around(1)
Beispiel #7
0
 def get_sampling_rate(filename):
     ''' Use moviepy/FFMPEG to get the sampling rate '''
     infos = ffmpeg_parse_infos(filename)
     fps = infos.get('audio_fps', 44100)
     if fps == 'unknown':
         fps = 44100
     return fps
Beispiel #8
0
    def __init__(
        self,
        filename,
        buffersize,
        decode_file=False,
        print_infos=False,
        fps=44100,
        nbytes=2,
        nchannels=2,
    ):
        # TODO bring FFMPEG_AudioReader more in line with FFMPEG_VideoReader
        # E.g. here self.pos is still 1-indexed.
        # (or have them inherit from a shared parent class)
        self.filename = filename
        self.nbytes = nbytes
        self.fps = fps
        self.format = "s%dle" % (8 * nbytes)
        self.codec = "pcm_s%dle" % (8 * nbytes)
        self.nchannels = nchannels
        infos = ffmpeg_parse_infos(filename, decode_file=decode_file)
        self.duration = infos["duration"]
        self.bitrate = infos["audio_bitrate"]
        self.infos = infos
        self.proc = None

        self.n_frames = int(self.fps * self.duration)
        self.buffersize = min(self.n_frames + 1, buffersize)
        self.buffer = None
        self.buffer_startframe = 1
        self.initialize()
        self.buffer_around(1)
Beispiel #9
0
 def get_sampling_rate(filename):
     ''' Use moviepy/FFMPEG to get the sampling rate '''
     infos = ffmpeg_parse_infos(filename)
     fps = infos.get('audio_fps', 44100)
     if fps == 'unknown':
         fps = 44100
     return fps
Beispiel #10
0
    def __init__(self,
                 filename,
                 buffersize,
                 print_infos=False,
                 fps=44100,
                 nbytes=2,
                 nchannels=2):

        self.filename = filename
        self.nbytes = nbytes
        self.fps = fps
        self.f = 's%dle' % (8 * nbytes)
        self.acodec = 'pcm_s%dle' % (8 * nbytes)
        self.nchannels = nchannels
        infos = ffmpeg_parse_infos(filename)
        self.duration = infos['duration']
        if 'video_duration' in infos:
            self.duration = infos['video_duration']
        else:
            self.duration = infos['duration']
        self.infos = infos
        self.proc = None

        self.nframes = int(self.fps * self.duration)
        self.buffersize = min(self.nframes + 1, buffersize)
        self.buffer = None
        self.buffer_startframe = 1
        self.initialize()
        self.buffer_around(1)
Beispiel #11
0
def dump_frames(video_path, output_directory, frames_per_second,
                file_logger_name):
    """Dump frames at frames_per_second from a video to output_directory.

    If frames_per_second is None, the clip's fps attribute is used instead."""
    if not os.path.isdir(output_directory):
        os.mkdir(output_directory)

    file_logger = logging.getLogger(file_logger_name)

    try:
        video_info = ffmpeg_parse_infos(video_path)
        video_fps = video_info['video_fps']
        video_duration = video_info['duration']
    except OSError as e:
        logging.error('Unable to open video (%s), skipping.' % video_path)
        logging.exception('Exception:')
        return
    info_path = '{}/info.json'.format(output_directory)
    name_format = '{}/frame%04d.png'.format(output_directory)

    extract_all_frames = frames_per_second is None
    if extract_all_frames:
        frames_per_second = video_fps

    frames_already_dumped_helper = lambda log_reason: \
        frames_already_dumped(video_path, output_directory,
                              frames_per_second, info_path,
                              name_format, video_duration, log_reason)

    if frames_already_dumped_helper(False):
        file_logger.info('Frames for {} exist, skipping...'.format(video_path))
        return

    successfully_wrote_images = False
    try:
        if extract_all_frames:
            cmd = ['ffmpeg', '-i', video_path, name_format]
        else:
            cmd = ['ffmpeg', '-i', video_path, '-vf',
                   'fps={}'.format(frames_per_second), name_format]
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        successfully_wrote_images = True
    except subprocess.CalledProcessError as e:
        logging.error("Failed to dump images for %s", video_path)
        logging.error(e)
        logging.error(e.output.decode('utf-8'))

    if successfully_wrote_images:
        info = {'frames_per_second': frames_per_second,
                'input_video_path': os.path.abspath(video_path)}
        with open(info_path, 'w') as info_file:
            json.dump(info, info_file)

        if not frames_already_dumped_helper(True):
            logging.error(
                "Images for {} don't seem to be dumped properly!".format(
                    video_path))
Beispiel #12
0
def get_duration_definition(video_name):
    """
    获取视频时长与清晰度
    :param video_name:
    :return:
    """
    video_info = ffmpeg_parse_infos(video_name)
    duration = int(video_info['duration'])  # unit second
    video_size = video_info['video_size']
    return duration, video_size
Beispiel #13
0
def video_info(video):
    from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos
    if isinstance(video, Path):
        video = str(video)

    info = ffmpeg_parse_infos(video)
    return {
        'duration': info['duration'],
        'fps': info['video_fps'],
        'size': info['video_size']  # (width, height)
    }
Beispiel #14
0
def test_ffmpeg_parse_infos():
    d = ffmpeg_parse_infos("media/big_buck_bunny_432_433.webm")
    assert d["duration"] == 1.0

    d = ffmpeg_parse_infos("media/pigs_in_a_polka.gif")
    assert d["video_size"] == [314, 273]
    assert d["duration"] == 3.0
    assert not d["audio_found"]

    d = ffmpeg_parse_infos("media/video_with_failing_audio.mp4")
    assert d["audio_found"]
    assert d["audio_fps"] == 44100

    d = ffmpeg_parse_infos("media/crunching.mp3")
    assert d["audio_found"]
    assert d["audio_fps"] == 48000

    d = ffmpeg_parse_infos("tests/resource/sintel_with_15_chapters.mp4")
    assert d["audio_bitrate"]
    assert d["video_bitrate"]
Beispiel #15
0
def test_correct_video_rotation(util):
    """See https://github.com/Zulko/moviepy/pull/577"""
    clip = VideoFileClip("media/rotated-90-degrees.mp4").subclip(0.2, 0.4)

    corrected_rotation_filename = os.path.join(
        util.TMP_DIR,
        "correct_video_rotation.mp4",
    )
    clip.write_videofile(corrected_rotation_filename)

    d = ffmpeg_parse_infos(corrected_rotation_filename)
    assert "video_rotation" not in d
    assert d["video_size"] == [1080, 1920]
Beispiel #16
0
def test_ffmpeg_parse_infos_chapters():
    """Check that `ffmpeg_parse_infos` can parse chapters with their metadata."""
    d = ffmpeg_parse_infos("media/sintel_with_14_chapters.mp4")

    chapters = d["inputs"][0]["chapters"]

    num_chapters_expected = 14

    assert len(chapters) == num_chapters_expected
    for num in range(0, len(chapters)):
        assert chapters[num]["chapter_number"] == num
        assert chapters[num]["end"] == (num + 1) / 10
        assert chapters[num]["start"] == num / 10
        assert chapters[num]["metadata"]["title"]
        assert isinstance(chapters[num]["metadata"]["title"], str)
Beispiel #17
0
def info(video):
    """Report video duration, fps, and resolution."""
    from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos
    info = ffmpeg_parse_infos(video)
    output = {
        'Path': Path(video).resolve(),
        'Duration': info['duration'],
        'FPS': info['video_fps'],
        'Resolution':
            '{w}x{h}'.format(w=info["video_size"][0], h=info["video_size"][1])
    }
    max_width = max(len(x) for x in info)
    for key, value in output.items():
        # Right align all names for pretty output.
        key_pretty = key.rjust(max_width)
        print(f"{key_pretty}: {value}")
Beispiel #18
0
def test_ffmpeg_parse_infos_metadata_with_attached_pic():
    """Check that the parser can parse audios with attached pictures.

    Currently, does not distinguish if the video found is an attached picture,
    this test serves mainly to ensure that #1487 issue does not happen again:
    """
    d = ffmpeg_parse_infos("media/with-attached-pic.mp3")

    assert d["audio_bitrate"] == 320
    assert d["audio_found"]
    assert d["audio_fps"] == 44100

    assert len(d["inputs"]) == 1
    streams = d["inputs"][0]["streams"]
    assert len(streams) == 2
    assert streams[0]["stream_type"] == "audio"
    assert streams[1]["stream_type"] == "video"

    assert len(d["metadata"].keys()) == 7
Beispiel #19
0
 def __init__(self, filename, buffersize, print_infos=False,
              fps=44100, nbytes=2, nchannels=2):
     
     self.filename = filename
     self.nbytes = nbytes
     self.fps = fps
     self.f = 's%dle'%(8*nbytes)
     self.acodec = 'pcm_s%dle'%(8*nbytes)
     self.nchannels = nchannels
     infos = ffmpeg_parse_infos(filename)
     self.duration = infos['duration']
     if 'video_duration' in infos:
         self.duration = infos['video_duration']
     else:
         self.duration = infos['duration']
     self.infos = infos
     self.proc = None
     
     self.buffersize=buffersize
     self.buffer= None
     self.buffer_startframe = 1
     self.initialize()
     self.buffer_around(1)
Beispiel #20
0
    def __init__(self, filename, buffersize, print_infos=False, fps=44100, nbytes=2, nchannels=2):

        self.filename = filename
        self.nbytes = nbytes
        self.fps = fps
        self.f = "s%dle" % (8 * nbytes)
        self.acodec = "pcm_s%dle" % (8 * nbytes)
        self.nchannels = nchannels
        infos = ffmpeg_parse_infos(filename)
        self.duration = infos["duration"]
        if "video_duration" in infos:
            self.duration = infos["video_duration"]
        else:
            self.duration = infos["duration"]
        self.infos = infos
        self.proc = None

        self.nframes = int(self.fps * self.duration)
        self.buffersize = min(self.nframes + 1, buffersize)
        self.buffer = None
        self.buffer_startframe = 1
        self.initialize()
        self.buffer_around(1)
Beispiel #21
0
    def __init__(self, video_file, width):
        '''
            Creates the video object.
        '''

        video_info = ffmpeg_parse_infos(video_file)

        # Full path the video.
        self.file = video_file

        # Moviepy VideoFileClip object.
        self.video = VideoFileClip(video_file).resize(width=width)

        # Length in seconds of the video.
        self.runtime = video_info['duration']

        # Get the video's file name.
        file_name, file_extension = os.path.splitext(video_file)
        self.video_name = file_name.split('/')[-1].replace(' ', '').translate(None, '[]')

        # The video's resized height and width.
        self.width = width
        self.height = float(video_info['video_size'][1]) / float(video_info['video_size'][0]) * width
Beispiel #22
0
def test_ffmpeg_parse_infos_for_i926():
    d = ffmpeg_parse_infos("media/sintel_with_15_chapters.mp4")
    assert d["audio_found"]
Beispiel #23
0
def test_ffmpeg_parse_infos_multiple_audio_streams():
    """Check that ``ffmpeg_parse_infos`` can parse multiple audio streams."""
    # Create two mono audio files
    clip_440_filepath = os.path.join(
        TMP_DIR, "ffmpeg_parse_infos_multiple_streams_440.mp3"
    )
    clip_880_filepath = os.path.join(
        TMP_DIR, "ffmpeg_parse_infos_multiple_streams_880.mp3"
    )
    multiple_streams_filepath = os.path.join(
        TMP_DIR, "ffmpeg_parse_infos_multiple_streams.mp4"
    )

    make_frame_440 = lambda t: np.array(
        [
            np.sin(440 * 2 * np.pi * t),
        ]
    )
    make_frame_880 = lambda t: np.array(
        [
            np.sin(880 * 2 * np.pi * t),
        ]
    )

    clip_440 = AudioClip(make_frame_440, fps=22050, duration=0.01)
    clip_880 = AudioClip(make_frame_880, fps=22050, duration=0.01)
    clip_440.write_audiofile(clip_440_filepath)
    clip_880.write_audiofile(clip_880_filepath)

    # create a MP4 file with multiple streams
    cmd = [
        FFMPEG_BINARY,
        "-y",
        "-i",
        clip_440_filepath,
        "-i",
        clip_880_filepath,
        "-map",
        "0:a:0",
        "-map",
        "0:a:0",
        multiple_streams_filepath,
    ]
    with open(os.devnull, "w") as stderr:
        subprocess.check_call(cmd, stderr=stderr)

    # check that `ffmpeg_parse_infos` can parse all the streams data
    d = ffmpeg_parse_infos(multiple_streams_filepath)

    # number of inputs and streams
    assert len(d["inputs"]) == 1
    assert len(d["inputs"][0]["streams"]) == 2

    default_stream = d["inputs"][0]["streams"][0]
    ignored_stream = d["inputs"][0]["streams"][1]

    # default, only the first
    assert default_stream["default"]
    assert not ignored_stream["default"]

    # streams and inputs numbers
    assert default_stream["stream_number"] == 0
    assert ignored_stream["stream_number"] == 1
    assert default_stream["input_number"] == 0
    assert ignored_stream["input_number"] == 0

    # stream type
    assert default_stream["stream_type"] == "audio"
    assert ignored_stream["stream_type"] == "audio"

    # cleanup
    for filepath in [clip_440_filepath, clip_880_filepath, multiple_streams_filepath]:
        os.remove(filepath)
    close_all_clips(locals())
Beispiel #24
0
def test_ffmpeg_parse_infos_for_i926():
    d = ffmpeg_parse_infos("tests/resource/sintel_with_15_chapters.mp4")
    assert d['audio_found']
Beispiel #25
0
def test_ffmpeg_parse_infos_video_nframes():
    d = ffmpeg_parse_infos("media/big_buck_bunny_0_30.webm")
    assert d["video_n_frames"] == 720

    d = ffmpeg_parse_infos("media/bitmap.mp4")
    assert d["video_n_frames"] == 5
Beispiel #26
0
def test_ffmpeg_parse_infos_for_i926():
    d = ffmpeg_parse_infos("tests/resource/sintel_with_15_chapters.mp4")
    assert d['audio_found']
Beispiel #27
0
def test_ffmpeg_parse_infos_metadata():
    """Check that `ffmpeg_parse_infos` is able to retrieve metadata from files."""
    filepath = os.path.join(TMP_DIR, "ffmpeg_parse_infos_metadata.mkv")
    if os.path.isfile(filepath):
        os.remove(filepath)

    # create video with 2 streams, video and audio
    audioclip = AudioClip(
        lambda t: np.sin(440 * 2 * np.pi * t), fps=22050
    ).with_duration(1)
    videoclip = BitmapClip([["RGB"]], fps=1).with_duration(1).with_audio(audioclip)

    # build metadata key-value pairs which will be passed to ``ffmpeg_params``
    # argument of ``videoclip.write_videofile``
    metadata = {
        "file": {
            "title": "Fóò",
            "comment": "bar",
            "description": "BAZ",
            "synopsis": "Testing",
        },
        "video": {
            "author": "Querty",
            "title": "hello",
            "description": "asdf",
        },
        "audio": {"track": "1", "title": "wtr", "genre": "lilihop"},
    }

    ffmpeg_params = []
    for metadata_type, data in metadata.items():
        option = "-metadata"
        if metadata_type in ["video", "audio"]:
            option += ":s:%s:0" % ("v" if metadata_type == "video" else "a")
        for field, value in data.items():
            ffmpeg_params.extend([option, f"{field}={value}"])

    languages = {
        "audio": "eng",
        "video": "spa",
    }
    ffmpeg_params.extend(
        [
            "-metadata:s:a:0",
            "language=" + languages["audio"],
            "-metadata:s:v:0",
            "language=" + languages["video"],
        ]
    )

    # create file with metadata included
    videoclip.write_videofile(filepath, codec="libx264", ffmpeg_params=ffmpeg_params)

    # get information about created file
    d = ffmpeg_parse_infos(filepath)

    def get_value_from_dict_using_lower_key(field, dictionary):
        """Obtains a value from a dictionary using a key, no matter if the key
        is uppercased in the dictionary. This function is needed because
        some media containers convert to uppercase metadata field names.
        """
        value = None
        for d_field, d_value in dictionary.items():
            if str(d_field).lower() == field:
                value = d_value
                break
        return value

    # assert file metadata
    for field, value in metadata["file"].items():
        assert get_value_from_dict_using_lower_key(field, d["metadata"]) == value

    # assert streams metadata
    streams = {"audio": None, "video": None}
    for stream in d["inputs"][0]["streams"]:
        streams[stream["stream_type"]] = stream

    for stream_type, stream in streams.items():
        for field, value in metadata[stream_type].items():
            assert (
                get_value_from_dict_using_lower_key(field, stream["metadata"]) == value
            )

    # assert stream languages
    for stream_type, stream in streams.items():
        assert stream["language"] == languages[stream_type]

    os.remove(filepath)

    close_all_clips(locals())
Beispiel #28
0
def dump_frames(video_path, output_dir, fps, logger_name=None):
    """Dump frames at frames_per_second from a video to output_dir.

    If frames_per_second is None, the clip's fps attribute is used instead."""
    output_dir.mkdir(exist_ok=True, parents=True)

    if logger_name:
        file_logger = logging.getLogger(logger_name)
    else:
        file_logger = logging.root

    try:
        video_info = ffmpeg_parse_infos(video_path)
        video_fps = video_info['video_fps']
    except OSError:
        logging.error('Unable to open video (%s), skipping.' % video_path)
        logging.exception('Exception:')
        return
    except KeyError:
        logging.error(
            'Unable to extract metadata about video (%s), skipping.' %
            video_path)
        logging.exception('Exception:')
        return
    info_path = '{}/info.json'.format(output_dir)
    name_format = '{}/frame%04d.png'.format(output_dir)

    if fps is None or fps == 0:
        fps = video_fps  # Extract all frames

    are_frames_dumped_wrapper = functools.partial(
        are_frames_dumped,
        video_path=video_path,
        output_dir=output_dir,
        expected_fps=fps,
        expected_info_path=info_path,
        expected_name_format=name_format)

    if are_frames_dumped_wrapper(log_reason=False):
        file_logger.info('Frames for {} exist, skipping...'.format(video_path))
        return

    successfully_wrote_images = False
    try:
        if fps == video_fps:
            cmd = ['ffmpeg', '-i', video_path, name_format]
        else:
            cmd = [
                'ffmpeg', '-i', video_path, '-vf', 'fps={}'.format(fps),
                name_format
            ]
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        successfully_wrote_images = True
    except subprocess.CalledProcessError as e:
        logging.error("Failed to dump images for %s", video_path)
        logging.error(e)
        logging.error(e.output.decode('utf-8'))

    if successfully_wrote_images:
        info = {
            'frames_per_second': fps,
            'input_video_path': os.path.abspath(video_path)
        }
        with open(info_path, 'w') as info_file:
            json.dump(info, info_file)

        if not are_frames_dumped_wrapper(log_reason=True):
            logging.error(
                "Images for {} don't seem to be dumped properly!".format(
                    video_path))
Beispiel #29
0
def dump_frames(video_path, output_dir, fps, extension='.jpg', jpeg_qscale=2):
    """Dump frames at frames_per_second from a video to output_dir.

    If frames_per_second is None, the clip's fps attribute is used instead."""
    output_dir.mkdir(exist_ok=True, parents=True)

    if extension[0] != '.':
        extension = f'.{extension}'

    try:
        video_info = ffmpeg_parse_infos(str(video_path))
        video_fps = video_info['video_fps']
    except OSError:
        logging.exception('Unable to open video (%s), skipping.' % video_path)
        raise
    except KeyError:
        logging.error(
            'Unable to extract metadata about video (%s), skipping.' %
            video_path)
        logging.exception('Exception:')
        return
    info_path = '{}/info.json'.format(output_dir)
    name_format = '{}/frame%04d{}'.format(output_dir, extension)

    if fps is None or fps == 0:
        fps = video_fps  # Extract all frames

    are_frames_dumped_wrapper = functools.partial(
        are_frames_dumped,
        video_path=video_path,
        output_dir=output_dir,
        expected_fps=fps,
        expected_info_path=info_path,
        expected_name_format=name_format)

    if extension.lower() in ('.jpg', '.jpeg'):
        qscale = ['-qscale:v', str(jpeg_qscale)]
    else:
        qscale = []

    if are_frames_dumped_wrapper(log_reason=False):
        return

    successfully_wrote_images = False
    try:
        if fps == video_fps:
            cmd = ['ffmpeg', '-i', str(video_path)] + qscale + [name_format]
        else:
            cmd = ['ffmpeg', '-i', str(video_path)
                   ] + qscale + ['-vf', 'fps={}'.format(fps), name_format]
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        successfully_wrote_images = True
    except subprocess.CalledProcessError as e:
        logging.exception("Failed to dump images for %s", video_path)
        logging.error(e.output.decode('utf-8'))
        raise

    if successfully_wrote_images:
        info = {
            'frames_per_second': fps,
            'input_video_path': os.path.abspath(video_path)
        }
        with open(info_path, 'w') as info_file:
            json.dump(info, info_file)

        if not are_frames_dumped_wrapper(log_reason=True):
            logging.warning(
                "Images for {} don't seem to be dumped properly!".format(
                    video_path))
Beispiel #30
0
def test_ffmpeg_parse_infos_duration():
    infos = ffmpeg_parse_infos("media/big_buck_bunny_0_30.webm")
    assert infos["video_nframes"] == 720

    infos = ffmpeg_parse_infos("media/bitmap.mp4")
    assert infos["video_nframes"] == 5
Beispiel #31
0
def test_ffmpeg_parse_video_rotation():
    d = ffmpeg_parse_infos("media/rotated-90-degrees.mp4")
    assert d["video_rotation"] == 90
    assert d["video_size"] == [1920, 1080]
Beispiel #32
0
def html_embed(clip,
               filetype=None,
               maxduration=60,
               rd_kwargs=None,
               center=True,
               **html_kwargs):
    """Returns HTML5 code embedding the clip.

    Parameters
    ----------

    clip : moviepy.Clip.Clip
      Either a file name, or a clip to preview.
      Either an image, a sound or a video. Clips will actually be
      written to a file and embedded as if a filename was provided.

    filetype : str, optional
      One of 'video','image','audio'. If None is given, it is determined
      based on the extension of ``filename``, but this can bug.

    maxduration : float, optional
      An error will be raised if the clip's duration is more than the indicated
      value (in seconds), to avoid spoiling the browser's cache and the RAM.

    rd_kwargs : dict, optional
      Keyword arguments for the rendering, like ``dict(fps=15, bitrate="50k")``.
      Allow you to give some options to the render process. You can, for
      example, disable the logger bar passing ``dict(logger=None)``.

    center : bool, optional
      If true (default), the content will be wrapped in a
      ``<div align=middle>`` HTML container, so the content will be displayed
      at the center.

    html_kwargs
      Allow you to give some options, like ``width=260``, ``autoplay=True``,
      ``loop=1`` etc.

    Examples
    --------

    >>> from moviepy.editor import *
    >>> # later ...
    >>> html_embed(clip, width=360)
    >>> html_embed(clip.audio)

    >>> clip.write_gif("test.gif")
    >>> html_embed('test.gif')

    >>> clip.save_frame("first_frame.jpeg")
    >>> html_embed("first_frame.jpeg")
    """
    if rd_kwargs is None:  # pragma: no cover
        rd_kwargs = {}

    if "Clip" in str(clip.__class__):
        TEMP_PREFIX = "__temp__"
        if isinstance(clip, ImageClip):
            filename = TEMP_PREFIX + ".png"
            kwargs = {"filename": filename, "with_mask": True}
            argnames = inspect.getfullargspec(clip.save_frame).args
            kwargs.update({
                key: value
                for key, value in rd_kwargs.items() if key in argnames
            })
            clip.save_frame(**kwargs)
        elif isinstance(clip, VideoClip):
            filename = TEMP_PREFIX + ".mp4"
            kwargs = {"filename": filename, "preset": "ultrafast"}
            kwargs.update(rd_kwargs)
            clip.write_videofile(**kwargs)
        elif isinstance(clip, AudioClip):
            filename = TEMP_PREFIX + ".mp3"
            kwargs = {"filename": filename}
            kwargs.update(rd_kwargs)
            clip.write_audiofile(**kwargs)
        else:
            raise ValueError(
                "Unknown class for the clip. Cannot embed and preview.")

        return html_embed(
            filename,
            maxduration=maxduration,
            rd_kwargs=rd_kwargs,
            center=center,
            **html_kwargs,
        )

    filename = clip
    options = " ".join(
        ["%s='%s'" % (str(k), str(v)) for k, v in html_kwargs.items()])
    name, ext = os.path.splitext(filename)
    ext = ext[1:]

    if filetype is None:
        ext = filename.split(".")[-1].lower()
        if ext == "gif":
            filetype = "image"
        elif ext in extensions_dict:
            filetype = extensions_dict[ext]["type"]
        else:
            raise ValueError(
                "No file type is known for the provided file. Please provide "
                "argument `filetype` (one of 'image', 'video', 'sound') to the "
                "ipython display function.")

    if filetype == "video":
        # The next lines set the HTML5-cvompatible extension and check that the
        # extension is HTML5-valid
        exts_htmltype = {"mp4": "mp4", "webm": "webm", "ogv": "ogg"}
        allowed_exts = " ".join(exts_htmltype.keys())
        try:
            ext = exts_htmltype[ext]
        except Exception:
            raise ValueError("This video extension cannot be displayed in the "
                             "IPython Notebook. Allowed extensions: " +
                             allowed_exts)

    if filetype in ["audio", "video"]:
        duration = ffmpeg_parse_infos(filename, decode_file=True)["duration"]
        if duration > maxduration:
            raise ValueError((
                "The duration of video %s (%.1f) exceeds the 'maxduration'"
                " attribute. You can increase 'maxduration', by passing"
                " 'maxduration' parameter to ipython_display function."
                " But note that embedding large videos may take all the memory"
                " away!") % (filename, duration))

    with open(filename, "rb") as file:
        data = b64encode(file.read()).decode("utf-8")

    template = templates[filetype]

    result = template % {"data": data, "options": options, "ext": ext}
    if center:
        result = r"<div align=middle>%s</div>" % result

    return result
Beispiel #33
0
def html_embed(clip,
               filetype=None,
               maxduration=60,
               rd_kwargs=None,
               center=True,
               **html_kwargs):
    """Returns HTML5 code embedding the clip

    clip
      Either a file name, or a clip to preview.
      Either an image, a sound or a video. Clips will actually be
      written to a file and embedded as if a filename was provided.


    filetype
      One of 'video','image','audio'. If None is given, it is determined
      based on the extension of ``filename``, but this can bug.

    rd_kwargs
      Keyword arguments for the rendering, like ``{'fps':15, 'bitrate':'50k'}``


    html_kwargs
      Allow you to give some options, like ``width=260``, ``autoplay=True``,
      ``loop=1`` etc.

    Examples
    --------
    TODO Create example based on ipython_display examples
    """
    if rd_kwargs is None:
        rd_kwargs = {}

    if "Clip" in str(clip.__class__):
        TEMP_PREFIX = "__temp__"
        if isinstance(clip, ImageClip):
            filename = TEMP_PREFIX + ".png"
            kwargs = {"filename": filename, "with_mask": True}
            kwargs.update(rd_kwargs)
            clip.save_frame(**kwargs)
        elif isinstance(clip, VideoClip):
            filename = TEMP_PREFIX + ".mp4"
            kwargs = {"filename": filename, "preset": "ultrafast"}
            kwargs.update(rd_kwargs)
            clip.write_videofile(**kwargs)
        elif isinstance(clip, AudioClip):
            filename = TEMP_PREFIX + ".mp3"
            kwargs = {"filename": filename}
            kwargs.update(rd_kwargs)
            clip.write_audiofile(**kwargs)
        else:
            raise ValueError(
                "Unknown class for the clip. Cannot embed and preview.")

        return html_embed(
            filename,
            maxduration=maxduration,
            rd_kwargs=rd_kwargs,
            center=center,
            **html_kwargs,
        )

    filename = clip
    options = " ".join(
        ["%s='%s'" % (str(k), str(v)) for k, v in html_kwargs.items()])
    name, ext = os.path.splitext(filename)
    ext = ext[1:]

    if filetype is None:
        ext = filename.split(".")[-1].lower()
        if ext == "gif":
            filetype = "image"
        elif ext in extensions_dict:
            filetype = extensions_dict[ext]["type"]
        else:
            raise ValueError(
                "No file type is known for the provided file. Please provide "
                "argument `filetype` (one of 'image', 'video', 'sound') to the "
                "ipython display function.")

    if filetype == "video":
        # The next lines set the HTML5-cvompatible extension and check that the
        # extension is HTML5-valid
        exts_htmltype = {"mp4": "mp4", "webm": "webm", "ogv": "ogg"}
        allowed_exts = " ".join(exts_htmltype.keys())
        try:
            ext = exts_htmltype[ext]
        except Exception:
            raise ValueError("This video extension cannot be displayed in the "
                             "IPython Notebook. Allowed extensions: " +
                             allowed_exts)

    if filetype in ["audio", "video"]:
        duration = ffmpeg_parse_infos(filename, decode_file=True)["duration"]
        if duration > maxduration:
            raise ValueError((
                "The duration of video %s (%.1f) exceeds the 'maxduration'"
                " attribute. You can increase 'maxduration', by passing"
                " 'maxduration' parameter to ipython_display function."
                " But note that embedding large videos may take all the memory"
                " away!") % (filename, duration))

    with open(filename, "rb") as file:
        data = b64encode(file.read()).decode("utf-8")

    template = templates[filetype]

    result = template % {"data": data, "options": options, "ext": ext}
    if center:
        result = r"<div align=middle>%s</div>" % result

    return result
    def write_video(self, input_filename, output_filename, silence_list,
                    progress_bar_logger):
        self.video_generation_progress.emit(0)

        videoclip = VideoFileClip(input_filename)

        # Choose output audio/video bitrate based on values for input video
        # (Note : in fact specifying bitrate is only relevant for some codecs, e.g. libx264)
        output_video_bitrate = '24000k'
        output_audio_bitrate = '190k'

        tmpfilename = "tmpfile.out"
        os.system("ffmpeg -i \"%s\" 2> %s" % (input_filename, tmpfilename))
        tmpfile = open(tmpfilename, 'r')
        lines = tmpfile.readlines()
        tmpfile.close()
        os.remove(tmpfilename)

        for line in lines:
            line = line.strip()
            if line.startswith('Stream #0:0'):
                search = re.search('(\d+ kb/s)', line)
                if search is not None:
                    output_video_bitrate = search.group(1)
                    output_video_bitrate = output_video_bitrate[:-3]
                else:
                    print("Note: took default video bitrate")
            if line.startswith('Stream #0:1'):
                search = re.search('(\d+ kb/s)', line)
                if search is not None:
                    output_audio_bitrate = search.group(1)
                    output_audio_bitrate = output_audio_bitrate[:-3]
                else:
                    print("Note: took default audio bitrate")

        # Choose output audio fps based on value for input video
        output_audio_fps = 48000
        infos = ffmpeg_parse_infos(input_filename)
        if 'audio_fps' in infos:
            output_audio_fps = infos['audio_fps']

        # Remove pseudo-silences
        clean_silence_list = []
        for silence in silence_list:
            if silence.start_cut() < silence.stop_cut():
                clean_silence_list.append(silence)

        n_sil = len(clean_silence_list)

        # Subclip before the first silence (if exist)
        subclips = []
        start_time = 0
        if n_sil > 0:
            stop_time = clean_silence_list[0].start_cut()
        else:
            stop_time = videoclip.duration

        if start_time < stop_time:
            subclips.append(videoclip.subclip(start_time, stop_time))

        # Subclips between silences
        for i in range(n_sil - 1):
            start_time = clean_silence_list[i].stop_cut()
            stop_time = clean_silence_list[i + 1].start_cut()
            subclips.append(videoclip.subclip(start_time, stop_time))
            val = i / (n_sil - 1) * 11
            self.video_generation_progress.emit(val)

        # Subclip after last silence (if exist)
        if n_sil > 0:
            start_time = clean_silence_list[n_sil - 1].stop_cut()
            stop_time = videoclip.duration
            if start_time != stop_time:
                subclips.append(videoclip.subclip(start_time, stop_time))

        self.video_generation_progress.emit(11)

        # Create final clip
        final_clip = concatenate_videoclips(subclips)

        final_clip.write_videofile(
            output_filename,
            fps=videoclip.fps,
            audio_fps=output_audio_fps,
            bitrate=output_video_bitrate,
            audio_bitrate=output_audio_bitrate,
            preset='ultrafast',  # 'placebo' = very slow but smaller file size,
            # 'ultrafast' = a lot faster but silghtly
            #        higher file size (and same quality)
            logger=progress_bar_logger)

        self.video_generation_progress.emit(99)
        videoclip.close()
        self.video_generation_progress.emit(100)
Beispiel #35
0
def dump_frames(video_path, output_directory, frames_per_second,
                file_logger_name):
    """Dump frames at frames_per_second from a video to output_directory.

    If frames_per_second is None, the clip's fps attribute is used instead."""
    if not os.path.isdir(output_directory):
        os.mkdir(output_directory)

    file_logger = logging.getLogger(file_logger_name)

    try:
        video_info = ffmpeg_parse_infos(video_path)
        video_fps = video_info['video_fps']
        video_duration = video_info['duration']
    except OSError as e:
        logging.error('Unable to open video (%s), skipping.' % video_path)
        logging.exception('Exception:')
        return
    except KeyError as e:
        logging.error('Unable to extract metadata about video (%s), skipping.'
                      % video_path)
        logging.exception('Exception:')
        return
    info_path = '{}/info.json'.format(output_directory)
    name_format = '{}/frame%04d.png'.format(output_directory)

    extract_all_frames = frames_per_second is None
    if extract_all_frames:
        frames_per_second = video_fps

    frames_already_dumped_helper = lambda log_reason: \
        frames_already_dumped(video_path, output_directory,
                              frames_per_second, info_path,
                              name_format, log_reason)

    if frames_already_dumped_helper(False):
        file_logger.info('Frames for {} exist, skipping...'.format(video_path))
        return

    successfully_wrote_images = False
    try:
        if extract_all_frames:
            cmd = ['ffmpeg', '-i', video_path, name_format]
        else:
            cmd = ['ffmpeg', '-i', video_path, '-vf',
                   'fps={}'.format(frames_per_second), name_format]
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        successfully_wrote_images = True
    except subprocess.CalledProcessError as e:
        logging.error("Failed to dump images for %s", video_path)
        logging.error(e)
        logging.error(e.output.decode('utf-8'))

    if successfully_wrote_images:
        info = {'frames_per_second': frames_per_second,
                'input_video_path': os.path.abspath(video_path)}
        with open(info_path, 'w') as info_file:
            json.dump(info, info_file)

        if not frames_already_dumped_helper(True):
            logging.error(
                "Images for {} don't seem to be dumped properly!".format(
                    video_path))
Beispiel #36
0
 def get_sampling_rate(filename):
     ''' Use moviepy/FFMPEG to get the sampling rate '''
     infos = ffmpeg_parse_infos(filename)
     return infos.get('audio_fps', 44100)
Beispiel #37
0
def main():
    """
    The main function of the application.
     Check the input file for correctness.
     Creates the main window, the thread pool, etc.
     Connects all handlers
    :return: None
    """
    if len(sys.argv) != 2:
        sys.exit("Usage: ./main.py <video file>")
    if not os.path.isfile(sys.argv[1]):
        sys.exit("Video file is not found")

    orig_filename = os.path.abspath(sys.argv[1])
    splitted = os.path.splitext(orig_filename)
    result_filename = splitted[0] + "_edited" + splitted[1]

    try:
        ffmpeg_parse_infos(orig_filename)
    except IOError:
        sys.exit("Can't detect valid video in specified file")

    app = QtGui.QApplication([])
    app.setApplicationName("Title machine")
    window = QtGui.QMainWindow()

    ui = Ui_MainWindow()
    ui.setupUi(window)

    ui.video_seek = SeekSlider(ui.layoutWidget)
    ui.video_seek.setIconVisible(False)
    ui.video_seek.setPageStep(1000)
    ui.video_seek.setSingleStep(200)
    ui.v_layout_1.addWidget(ui.video_seek)

    ui.video_canvas = VideoCanvas()
    ui.video_canvas.set_source(orig_filename)
    ui.video_seek.setMediaObject(ui.video_canvas.media)
    ui.v_layout_1.insertWidget(0, ui.video_canvas)

    pool = Pool(1)
    log_watcher = QtCore.QFileSystemWatcher([os.path.dirname(result_filename)])

    ui.button_process.clicked.connect(
        on_process_clicked(ui, orig_filename, result_filename, pool))
    ui.button_add_caption.clicked.connect(on_add_caption_clicked(ui))
    ui.button_remove_caption.clicked.connect(on_remove_caption_clicked(ui))
    ui.table_captions.cellChanged.connect(on_tablewidget_cell_changed(ui))

    ui.button_play_pause.clicked.connect(on_button_play_pause_clicked(ui))
    ui.video_canvas.media.stateChanged.connect(on_video_state_changed(ui))
    ui.video_canvas.media.tick.connect(on_video_tick(ui))
    ui.video_canvas.media.totalTimeChanged.connect(on_video_total_time_changed)

    log_watcher.directoryChanged.connect(on_log_dir_changed(log_watcher, result_filename))
    log_watcher.fileChanged.connect(on_log_file_changed(ui, log_watcher))
    app.lastWindowClosed.connect(on_app_quit(ui, pool))

    ui.textbrowser_log.insertPlainText("Welcome to Title Machine!")
    ui.video_canvas.media.pause()
    window.show()

    app.exec_()
Beispiel #38
0
 def get_audio_sampling_rate(self, filename: str):
     infos = ffmpeg_parse_infos(filename)
     fps = infos.get('audio_fps', 44100)
     if fps == 'unknown':
         fps = 44100
     return fps