Feed output of one filter to the input of one filter multiple times with ffmpeg

Viewed 639

I have the following ffmpeg commands to create a podcast episode:

# remove all silence at start and end of the audio files
ffmpeg -i call.mp3 -af silenceremove=1:0:-50dB call1.mp3
ffmpeg -i response.mp3 -af silenceremove=1:0:-50dB response1.mp3

# remove silence longer than 1 second anywhere within the audio files
ffmpeg -i call1.mp3 -af silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB call2.mp3
ffmpeg -i response1.mp3 -af silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB response2.mp3

# normalize audio files
ffmpeg -i call2.mp3 -af loudnorm=I=-16:LRA=11:TP=0.0 call3.mp3
ffmpeg -i response2.mp3 -af loudnorm=I=-16:LRA=11:TP=0.0 response3.mp3

# cross fade audio files with intro/interstitial/outro
ffmpeg -i intro.mp3 -i call3.mp3 -i interstitial.mp3 -i response3.mp3 -i outro.mp3
  -filter_complex "[0][1]acrossfade=d=1:c2=nofade[a01];
                   [a01][2]acrossfade=d=1:c1=nofade[a02];
                   [a02][3]acrossfade=d=1:c2=nofade[a03];
                   [a03][4]acrossfade=d=1:c1=nofade"
  output.mp3

While this "works" fine, I can't help but feel like it would be more efficient to do this all in one ffmpeg command. Based on what I found online this should be possible, but I don't understand the syntax well enough to know how to make it work. Here's what I tried:

ffmpeg -i intro.mp3 -i call.mp3 -i interstitial.mp3 -i response.mp3 -i outro.mp3
       -af [1]silenceremove=1:0:-50dB[trimmedCall]
       -af [3]silenceremove=1:0:-50dB[trimmedResponse]
       -af [trimmedCall]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceCall]
       -af [trimmedResponse]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceResponse]
       -af [noSilenceCall]loudnorm=I=-16:LRA=11:TP=0.0[call]
       -af [noSilenceResponse]loudnorm=I=-16:LRA=11:TP=0.0[response]
      -filter_complex "[0][call]acrossfade=d=1:c2=nofade[a01];
                       [a01][2]acrossfade=d=1:c1=nofade[a02];
                       [a02][response]acrossfade=d=1:c2=nofade[a03];
                       [a03][4]acrossfade=d=1:c1=nofade"
  output.mp3

But I have a feeling I have a fundamental misunderstanding of this because I got this error which I don't understand:

Stream specifier 'call' in filtergraph description 
[0][call]acrossfade=d=1:c2=nofade[a01];
[a01][2]acrossfade=d=1:c1=nofade[a02];
[a02][response]acrossfade=d=1:c2=nofade[a03];
[a03][4]acrossfade=d=1:c1=nofade
       matches no streams.

For added context, I'm running all these commands through @ffmpeg/ffmpeg so that last command actually looks like this (in JavaScript):

await ffmpeg.run(
  '-i', 'intro.mp3',
  '-i', 'call.mp3',
  '-i', 'interstitial.mp3',
  '-i', 'response.mp3',
  '-i', 'outro.mp3',
  '-af', '[1]silenceremove=1:0:-50dB[trimmedCall]',
  '-af', '[3]silenceremove=1:0:-50dB[trimmedResponse]',
  '-af', '[trimmedCall]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceCall]',
  '-af', '[trimmedResponse]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceResponse]',
  '-af', '[noSilenceCall]loudnorm=I=-16:LRA=11:TP=0.0[call]',
  '-af', '[noSilenceResponse]loudnorm=I=-16:LRA=11:TP=0.0[response]',
  '-filter_complex', `
[0][call]acrossfade=d=1:c2=nofade[a01];
[a01][2]acrossfade=d=1:c1=nofade[a02];
[a02][response]acrossfade=d=1:c2=nofade[a03];
[a03][4]acrossfade=d=1:c1=nofade
  `,
  'output.mp3',
)
1 Answers

Got an answer from @k1cc0 on twitter. Based on their suggestion, I updated my code to this and it worked like a charm:

await ffmpeg.run(
  '-i', 'intro.mp3',
  '-i', 'call.mp3',
  '-i', 'interstitial.mp3',
  '-i', 'response.mp3',
  '-i', 'outro.mp3',
  '-filter_complex', `
    [1]silenceremove=1:0:-50dB[trimmedCall];
    [3]silenceremove=1:0:-50dB[trimmedResponse];

    [trimmedCall]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceCall];
    [trimmedResponse]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceResponse];

    [noSilenceCall]loudnorm=I=-16:LRA=11:TP=0.0[call];
    [noSilenceResponse]loudnorm=I=-16:LRA=11:TP=0.0[response];

    [0][call]acrossfade=d=1:c2=nofade[a01];
    [a01][2]acrossfade=d=1:c1=nofade[a02];
    [a02][response]acrossfade=d=1:c2=nofade[a03];
    [a03][4]acrossfade=d=1:c1=nofade
  `,
  'output.mp3',
)

Effectively, here's the ffmpeg command:

ffmpeg  -i intro.mp3 -i call.mp3 -i interstitial.mp3 -i response.mp3 -i outro.mp3
  -filter_complex "[1]silenceremove=1:0:-50dB[trimmedCall];
                   [3]silenceremove=1:0:-50dB[trimmedResponse];

                   [trimmedCall]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceCall];
                   [trimmedResponse]silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB[noSilenceResponse];

                   [noSilenceCall]loudnorm=I=-16:LRA=11:TP=0.0[call];
                   [noSilenceResponse]loudnorm=I=-16:LRA=11:TP=0.0[response];

                   [0][call]acrossfade=d=1:c2=nofade[a01];
                   [a01][2]acrossfade=d=1:c1=nofade[a02];
                   [a02][response]acrossfade=d=1:c2=nofade[a03];
                   [a03][4]acrossfade=d=1:c1=nofade"
  output.mp3

The key here is that -filter_complex allows you to do exactly what I was trying to do, so if you need to pass multiple filters through in sequence that's what you need and then you specify the filter.

Related