Exemplo n.º 1
0
def test__merge_outputs():
    in_ = ffmpeg.input('in.mp4')
    out1 = in_.output('out1.mp4')
    out2 = in_.output('out2.mp4')
    assert ffmpeg.merge_outputs(out1, out2).get_args() == [
        '-i',
        'in.mp4',
        'out1.mp4',
        'out2.mp4',
    ]
    assert ffmpeg.get_args([out1,
                            out2]) == ['-i', 'in.mp4', 'out2.mp4', 'out1.mp4']
 def create_popen(self) -> Popen[bytes]:
     argument = [
         sys.executable,
         str(Path(__file__).resolve().parent / "windows.py"),
         str(self.time_to_force_termination),
         *ffmpeg.get_args(self.stream_spec),
     ]
     self.logger.debug(argument)
     # Reason: This method is instead of ffmpeg.run_async(). pylint: disable=consider-using-with
     return Popen(argument,
                  creationflags=CREATE_NEW_PROCESS_GROUP,
                  stdout=PIPE,
                  stderr=PIPE)
Exemplo n.º 3
0
def test_multi_passthrough():
    out1 = ffmpeg.input('in1.mp4').output('out1.mp4')
    out2 = ffmpeg.input('in2.mp4').output('out2.mp4')
    out = ffmpeg.merge_outputs(out1, out2)
    assert ffmpeg.get_args(out) == [
        '-i',
        'in1.mp4',
        '-i',
        'in2.mp4',
        'out1.mp4',
        '-map',
        '[1]',  # FIXME: this should not be here (see #23)
        'out2.mp4'
    ]
    assert ffmpeg.get_args([out1, out2]) == [
        '-i',
        'in2.mp4',
        '-i',
        'in1.mp4',
        'out2.mp4',
        '-map',
        '[1]',  # FIXME: this should not be here (see #23)
        'out1.mp4'
    ]
Exemplo n.º 4
0
def test_multi_passthrough():
    out1 = ffmpeg.input('in1.mp4').output('out1.mp4')
    out2 = ffmpeg.input('in2.mp4').output('out2.mp4')
    out = ffmpeg.merge_outputs(out1, out2)
    assert ffmpeg.get_args(out) == [
        '-i',
        'in1.mp4',
        '-i',
        'in2.mp4',
        'out1.mp4',
        '-map',
        '1',
        'out2.mp4',
    ]
    assert ffmpeg.get_args([out1, out2]) == [
        '-i',
        'in2.mp4',
        '-i',
        'in1.mp4',
        'out2.mp4',
        '-map',
        '1',
        'out1.mp4',
    ]
Exemplo n.º 5
0
def test_get_args_complex_filter():
    out = _get_complex_filter_example()
    args = ffmpeg.get_args(out)
    assert args == [
        '-i', TEST_INPUT_FILE,
        '-i', TEST_OVERLAY_FILE,
        '-filter_complex',
            '[0]trim=start_frame=10:end_frame=20,setpts=PTS-STARTPTS[v0];' \
            '[0]trim=start_frame=30:end_frame=40,setpts=PTS-STARTPTS[v1];' \
            '[v0][v1]concat=n=2[v2];' \
            '[1]hflip[v3];' \
            '[v2][v3]overlay=eof_action=repeat[v4];' \
            '[v4]drawbox=50:50:120:120:red:t=5[v5]',
        '-map', '[v5]', '/Users/karlk/src/ffmpeg_wrapper/ffmpeg/tests/sample_data/dummy2.mp4',
        '-y'
    ]
Exemplo n.º 6
0
def test_get_args_complex_filter():
    out = _get_complex_filter_example()
    args = ffmpeg.get_args(out)
    assert args == ['-i', TEST_INPUT_FILE1,
        '-i', TEST_OVERLAY_FILE,
        '-filter_complex',
            '[0]vflip[s0];' \
            '[s0]split=2[s1][s2];' \
            '[s1]trim=end_frame=20:start_frame=10[s3];' \
            '[s2]trim=end_frame=40:start_frame=30[s4];' \
            '[s3][s4]concat=n=2[s5];' \
            '[1]hflip[s6];' \
            '[s5][s6]overlay=eof_action=repeat[s7];' \
            '[s7]drawbox=50:50:120:120:red:t=5[s8]',
        '-map', '[s8]', TEST_OUTPUT_FILE1,
        '-y'
    ]
Exemplo n.º 7
0
    def start(self):
        Logger.LOGGER.log(Logger.TYPE_INFO,
                          'Starting Server, output to: {}'.format(self.output))

        in1 = ffmpeg.input('pipe:')
        v1 = ffmpeg.drawtext(in1['v'], '%{localtime:%R}',
                             x=c.SERV_DRAWTEXT_X,
                             y=c.SERV_DRAWTEXT_Y,
                             escape_text=False,
                             shadowcolor=c.SERV_DRAWTEXT_SHADOW_COLOR,
                             shadowx=c.SERV_DRAWTEXT_SHADOW_X,
                             shadowy=c.SERV_DRAWTEXT_SHADOW_Y,
                             fontsize=c.SERV_DRAWTEXT_FONT_SIZE,
                             fontfile=c.SERV_DRAWTEXT_FONT_FILE,
                             fontcolor=c.SERV_DRAWTEXT_FONT_COLOR
                             )
        v1 = ffmpeg.overlay(v1, self.overlay_file, x=c.OVERLAY_X, y=c.OVERLAY_Y)
        v1 = ffmpeg.overlay(v1, self.overlay_file_outline, x=c.OVERLAY_X, y=c.OVERLAY_Y)

        a1 = in1['a']
        joined = ffmpeg.concat(v1, a1, v=1, a=1)

        self.ff = ffmpeg.output(joined, self.output, vcodec='h264',
                                aspect=c.SERV_OUTPUT_ASPECT,
                                acodec=c.SERV_OUTPUT_ACODEC,
                                crf=c.SERV_OUTPUT_CRF,
                                preset=c.SERV_OUTPUT_PRESET,
                                format='flv',
                                pix_fmt='yuv420p'
                                )

        self.cmd = ['ffmpeg', '-re']+ffmpeg.get_args(self.ff)
        self.process = subprocess.Popen(self.cmd, stdin=subprocess.PIPE, stdout=devnull, stderr=(
            None if SERVER_DEBUG else devnull))
        Logger.LOGGER.log(Logger.TYPE_INFO,
                    'Server Process Created')
        return self.process
Exemplo n.º 8
0
    def play(self):
        output_stream = None

        if self.media_type == "upnext":
            Logger.LOGGER.log(
                Logger.TYPE_INFO,
                'Playing upnext v:{} a:{} (Duration: {})'.format(
                    self.media_item.video_path, self.media_item.audio_path,
                    self.media_item.duration_readable))

            in1 = ffmpeg.input(self.media_item.video_path)
            in2 = ffmpeg.input(self.media_item.audio_path)
            v1 = ffmpeg.filter(in1['v'], 'scale', c.CLIENT_VIDEO_SCALE)
            v1 = ffmpeg.drawtext(v1,
                                 '{}'.format(self.media_item.overlay_text),
                                 x=c.CLIENT_DRAWTEXT_X,
                                 y=c.CLIENT_DRAWTEXT_Y,
                                 escape_text=False,
                                 shadowcolor=c.CLIENT_DRAWTEXT_SHADOW_COLOR,
                                 shadowx=c.CLIENT_DRAWTEXT_SHADOW_X,
                                 shadowy=c.CLIENT_DRAWTEXT_SHADOW_Y,
                                 fontsize=c.CLIENT_DRAWTEXT_FONT_SIZE,
                                 fontfile=c.CLIENT_DRAWTEXT_FONT_FILE,
                                 fontcolor=c.CLIENT_DRAWTEXT_FONT_COLOR)

            a1 = in1['a']
            a2 = in2['a']
            audio_join = ffmpeg.filter([a1, a2], 'amix', duration="first")

            output_stream = ffmpeg.concat(v1, audio_join, v=1, a=1)

        else:
            Logger.LOGGER.log(
                Logger.TYPE_INFO, 'Playing v:{} (Duration: {})'.format(
                    self.media_item, self.media_item.duration_readable))

            in1 = ffmpeg.input(self.media_item.video_path)
            v1 = ffmpeg.filter(in1['v'], 'scale', c.CLIENT_VIDEO_SCALE)
            a1 = in1['a']
            output_stream = ffmpeg.concat(v1, a1, v=1, a=1)

        self.ff = ffmpeg.output(output_stream,
                                'pipe:',
                                vcodec=c.CLIENT_VCODEC,
                                aspect=c.CLIENT_ASPECT,
                                flags=c.CLIENT_FLAGS,
                                g=c.CLIENT_G,
                                acodec=c.CLIENT_ACODEC,
                                strict=c.CLIENT_STRICT,
                                ab=c.CLIENT_AUDIO_BITRATE,
                                ar=c.CLIENT_AUDIO_RATE,
                                preset=c.CLIENT_PRESET,
                                hls_allow_cache=c.CLIENT_HLS_ALLOW_CACHE,
                                hls_list_size=c.CLIENT_HLS_LIST_SIZE,
                                hls_time=c.CLIENT_HLS_TIME,
                                format=c.CLIENT_FORMAT,
                                pix_fmt=c.CLIENT_PIX_FMT)

        self.cmd = ['ffmpeg'] + ffmpeg.get_args(self.ff)

        self.process = subprocess.Popen(
            self.cmd,
            stdout=self.server.stdin,
            stderr=(None if CLIENT_DEBUG else devnull))
        try:
            flex = c.CLIENT_FLEX  # Number of seconds of extra time before timeout
            timeout = (self.media_item.duration / 1000
                       )  # Content length in seconds
            self.process.wait(timeout=timeout + flex)
        except subprocess.TimeoutExpired:
            Logger.LOGGER.log(
                Logger.TYPE_ERROR,
                'Taking longer to play than expected, killing current item')
            kill(self.process.pid)
            self.process.returncode = 0

        return self.process.returncode  # returncode 0 if process exited without problems, 1 for general error
Exemplo n.º 9
0
def ffmpeg_condense_audio(audiofile,
                          sub_times,
                          quality: Union[int, None],
                          to_mono: bool,
                          outfile=None):
    if outfile is None:
        outfile = "condensed.flac"
    # logging.info(f"saving condensed audio to {outfile}")

    # get samples in audio file
    audio_info = ffmpeg.probe(audiofile, cmd='ffprobe')
    sps = int(audio_info['streams'][0]['time_base'].split('/')
              [1])  # audio samples per second, inverse of sampling frequency
    # samples = audio_info['streams'][0]['duration_ts']  # total samples in audio track

    stream = ffmpeg.input(audiofile)

    clips = list()
    for time in sub_times:  # times are in milliseconds
        start = int(time[0] * sps / 1000)  # convert to sample index
        end = int(time[1] * sps / 1000)
        # use start_pts for sample/millisecond level precision
        clips.append(
            stream.audio.filter('atrim', start_pts=start,
                                end_pts=end).filter('asetpts', 'PTS-STARTPTS'))
    combined = ffmpeg.concat(*clips, a=1, v=0)

    kwargs = {}
    if Path(outfile).suffix.lower() == ".mp3":
        if quality is None:
            kwargs['audio_bitrate'] = "320k"
        else:
            kwargs['audio_bitrate'] = f"{quality}k"
    if to_mono:
        kwargs['ac'] = 1

    combined = ffmpeg.output(combined, outfile, **kwargs)

    combined = ffmpeg.overwrite_output(combined)
    logging.debug(
        f"ffmpeg_condense_audio: ffmpeg arguments: {' '.join(ffmpeg.get_args(combined))}"
    )
    args = ffmpeg.get_args(combined)
    if len("ffmpeg " + " ".join(args)) > 32766 and os.name == 'nt':
        logging.info(
            "Arguments passed to ffmpeg exceeds 32767 characters while running on a Windows system. "
            "Will try using a temporary file to pass filter_complex arguments to ffmpeg."
        )
        idx = args.index("-filter_complex") + 1
        complex_filter = str(args[idx])
        # write complex_filter to a temporary file
        fp = tempfile.NamedTemporaryFile(
            delete=False
        )  # don't delete b/c can't open file again when it's already open in windows
        fp.write(complex_filter.encode(encoding="utf-8"))
        fp.close()
        args[idx] = fp.name
        args[idx - 1] = "-filter_complex_script"
    args = ["ffmpeg"] + args

    # ffmpeg.run(combined, quiet=logging.getLogger().getEffectiveLevel() >= logging.WARNING)

    pipe_stdin = False
    pipe_stdout = False
    pipe_stderr = False
    quiet = logging.getLogger().getEffectiveLevel() >= logging.WARNING

    stdin_stream = subprocess.PIPE if pipe_stdin else None
    stdout_stream = subprocess.PIPE if pipe_stdout or quiet else None
    stderr_stream = subprocess.PIPE if pipe_stderr or quiet else None
    process = subprocess.Popen(args,
                               stdin=stdin_stream,
                               stdout=stdout_stream,
                               stderr=stderr_stream)
    out, err = process.communicate(input)
    retcode = process.poll()
    if retcode:
        raise Error('ffmpeg', out, err)
Exemplo n.º 10
0
def ffmpeg_condense_video(audiofile: str, videofile: str, subfile: str,
                          sub_times, outfile):
    logging.info(f"saving condensed video to {outfile}")

    # get samples in audio file
    audio_info = ffmpeg.probe(audiofile, cmd='ffprobe')
    sps = int(audio_info['streams'][0]['time_base'].split('/')
              [1])  # audio samples per second, inverse of sampling frequency
    # samples = audio_info['streams'][0]['duration_ts']  # total samples in audio track

    audiostream = ffmpeg.input(audiofile)
    videostream = ffmpeg.input(videofile)
    substream = ffmpeg.input(subfile)
    vid = videostream.video.filter_multi_output('split')
    # sub = videostream['s'].filter_multi_output('split')
    aud = audiostream.audio.filter_multi_output('asplit')

    clips = []
    for idx, time in enumerate(sub_times):  # times are in milliseconds
        # start = int(time[0] * sps / 1000)  # convert to sample index
        # end = int(time[1] * sps / 1000)
        start = time[0] / 1000
        end = time[1] / 1000
        # use start_pts for sample/millisecond level precision

        a = aud[idx].filter('atrim', start=start,
                            end=end).filter('asetpts', expr='PTS-STARTPTS')
        v = vid[idx].trim(start=start, end=end).setpts('PTS-STARTPTS')
        # s = sub[idx].trim(start=start, end=end).setpts('PTS-STARTPTS')
        clips.extend((v, a))

    out = ffmpeg.concat(*clips, v=1, a=1).output(substream, outfile)

    # output = ffmpeg.output(joined[0], joined[1], outfile)
    out = ffmpeg.overwrite_output(out)
    logging.debug(
        f"ffmpeg_condense_video: ffmpeg arguments: {' '.join(ffmpeg.get_args(out))}"
    )

    args = ffmpeg.get_args(out)
    if len("ffmpeg " + " ".join(args)) > 32766 and os.name == 'nt':
        logging.info(
            "Arguments passed to ffmpeg exceeds 32767 characters while running on a Windows system. "
            "Will try using a temporary file to pass filter_complex arguments to ffmpeg."
        )
        idx = args.index("-filter_complex") + 1
        complex_filter = str(args[idx])
        # write complex_filter to a temporary file
        fp = tempfile.NamedTemporaryFile(
            delete=False
        )  # don't delete b/c can't open file again when it's already open in windows
        fp.write(complex_filter.encode(encoding="utf-8"))
        fp.close()
        args[idx] = fp.name
        args[idx - 1] = "-filter_complex_script"
    args = ["ffmpeg"] + args

    # ffmpeg.run(out, quiet=logging.getLogger().getEffectiveLevel() >= logging.WARNING)

    pipe_stdin = False
    pipe_stdout = False
    pipe_stderr = False
    quiet = logging.getLogger().getEffectiveLevel() >= logging.WARNING

    stdin_stream = subprocess.PIPE if pipe_stdin else None
    stdout_stream = subprocess.PIPE if pipe_stdout or quiet else None
    stderr_stream = subprocess.PIPE if pipe_stderr or quiet else None
    process = subprocess.Popen(args,
                               stdin=stdin_stream,
                               stdout=stdout_stream,
                               stderr=stderr_stream)
    out, err = process.communicate(input)
    retcode = process.poll()
    if retcode:
        raise Error('ffmpeg', out, err)