kkroening / ffmpeg-python

Python bindings for FFmpeg - with complex filtering support
Apache License 2.0
10.07k stars 892 forks source link

replicate filter_complex and split audio channel #630

Open cmosguy opened 2 years ago

cmosguy commented 2 years ago

I have an mp3 audio that I want to split and convert to FLAC. The mp3 has left/right stereo channel, and I am trying to understand how to execute the following code:

./bin/ffmpeg.exe -i \
./audio/stereo_in.mp3 \
-filter_complex "[0:a]channelsplit=channel_layout=stereo[left][right]" \
-map "[left]" ./audio/output_FL.flac \
 -map "[right]" ./audio/output_FR.flac

Can someone please show me how to write the python fluent API for this?

chapmanjacobd commented 2 years ago

I think you'll need to use ffmpeg.filter_multi_output

ffmpeg.filter_multi_output(stream_spec, filter_name, *args, **kwargs)

    Apply custom filter with one or more outputs.

    This is the same as filter except that the filter can produce more than one output.

    To reference an output stream, use either the .stream operator or bracket shorthand:

    Example
split = ffmpeg.input('in.mp4').filter_multi_output('split') 
split0 = split.stream(0) 
split1 = split[1] 

ffmpeg.concat(split0, split1).output('out.mp4').run() `
AdolfVonKleist commented 1 year ago

if i try this I currently get an error: @chapmanjacobd

...
Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, wav, from 's.wav':
  Duration: 00:00:09.40, bitrate: 256 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 8000 Hz, stereo, s16, 256 kb/s
Stream specifier '' in filtergraph description [0]split=2[s0][s1];[s0:a][s1:a]concat=n=2[s2] matches no streams.
Traceback (most recent call last):
...

code is identical though:

def split_streams(ifile):
    split = ffmpeg.input(ifile).filter_multi_output('split')
    split0 = split.stream(0)
    split1 = split[1]
    ffmpeg.concat(split0, split1).output('out.wav').run()
    return

is there some way to specify the stream to deal with this error?

AdolfVonKleist commented 1 year ago

I did get this to work, but I had to do something rather different. This splits the streams and supports applying filters separately to each.

class FfmpegRedactor():
    """Simple FFMPEG based redaction class.
    """
    def __init__(self, sample_rate='8k'):
        self.outfile = 'redacted.wav'
        self.sample_rate = sample_rate
        self.nchannels = 1

    def ffmpeg_redact_channel(self, stream, channel):
        """Redact specified sections of the input channel.
        """
        for redaction in self.redactions[channel]:
            aggro = redaction['start']
            if aggro < 0.3:
                aggro = 0.0
            else:
                aggro = aggro - 0.3
            rstr = "between(t,{0:0.2f},{1:0.2f})".format(aggro, redaction['end'])
            stream = stream.filter("volume", 0, enable=rstr)

        return stream

    def split_streams(self, ifile):
        """Retrieve the Right (FR) and Left (LR) streams.

        Retrieve the Right (FR) and Left (LR) streams.
        The input file must be stereo.
        """
        ch0 = ffmpeg.input(ifile)['a:0'].filter(
            'channelsplit',
            channel_layout='stereo',
            channels='FR'
        )

        ch1 = ffmpeg.input(ifile)['a:0'].filter(
            'channelsplit',
            channel_layout='stereo',
            channels='FL'
        )

        ch0 = self.ffmpeg_redact_channel(ch0, 1)

        ch1 = self.ffmpeg_redact_channel(ch1, 2)

        result = ffmpeg.filter((ch0, ch1), 'join', inputs=2, channel_layout='stereo')

        return result