Search code examples
ffmpeg

Ffmpeg: Concat multiple videos and an audio file. Videos may or may not have audio and I cant know it before runnign the command


I have a list of mp4 and an aac audio file. I have a concat.txt file with all the mp4s that need to be concatenated. I need to concat all the mp4s and merge the aac on top.

As said in the title, the mp4s individually may or may not have an audio track, and I can not check the mp4s with no audio, add a dummy sound track as it will be an automated process with thousands of commands executed daily.

The sound of the mp4, if present, must be preserved and merged with the aac

Here is a working command for the case where at least the first of my mp4s has an audiotrack:

ffmpeg -f concat -safe 0 -i concat.txt -i audio.aac  -c:v copy -filter_complex "[0:a][1:a] amix=inputs=2:duration=shortest [audio_out]" -map 0:v -map "[audio_out]" -y result.mp4

However that breaks if my mp4s dont have audio

Stream specifier ':a' in filtergraph description [0:a][1:a] amix=inputs=2:duration=shortest [audio_out] matches no streams.

I've been reading about anullsrc, and would like to take [0:a] if it exists, or a generated empty audio channel, and merge it with my aac, but I'm not getting there.

Any help would be appreciated

Thanks


Solution

  • bash script

    #!/bin/bash
    #LST=($(ls -1tr *.mp4))
    LST=("input 1.mp4" "only_video.mp4" "1280x720x30_2.mp4")
    #LST=("1280x720x30_1.mp4" "only_video.mp4" "1280x720x30_2.mp4")
    f="${LST[0]}"
    
    IFS=, read VID WID HEI SAR FMT FPS TBN <<< $(ffprobe -v 0 -select_streams v:0 -show_entries stream=codec_name,width,height,sample_aspect_ratio,r_frame_rate,time_base,pix_fmt -of csv=p=0 "$f")
    echo $VID $WID $HEI $SAR $FMT $FPS $TBN
    if [ "$SAR" = "N/A" ]; then SAR=1; fi
    #FPS=30
    TBN=${TBN#*/}
    echo $VID $WID $HEI $SAR $FMT $FPS $TBN
    
    IFS=, read AUD SRA CHL <<< $(ffprobe -v 0 -select_streams a:0 -show_entries stream=codec_name,channel_layout,sample_rate -of csv=p=0 "$f")
    echo $AUD $SRA $CHL
    
    TXT=list.txt
    mkdir out
    o="out/${f%.*}.mkv"
    ffmpeg -i "$f" -c copy "$o" -y -hide_banner
    echo "file '$o'" > $TXT
    
    for (( i=1; i<${#LST[@]}; i++ )); do
      f="${LST[$i]}"
      o="out/${f%.*}.mkv"
      AU2=$(ffprobe -v 0 -select_streams a:0 -show_entries stream=codec_name -of default=nw=1:nk=1 "$f")
      if [ "$AU2" = "" ]; then
        echo --- create empty audio
        ffmpeg -i "$f" -f lavfi -i anullsrc -c:v copy -c:a $AUD -q:a 5 -shortest "$o" -y -hide_banner
      else
        if [ "$AU2" = "$AUD" ]; then
          echo --- copy audio
          ffmpeg -i "$f" -c copy "$o" -y -hide_banner
        else
          echo --- encode audio
          ffmpeg -i "$f" -c:v copy -c:a $AUD -q:a 5 -shortest "$o" -y -hide_banner
        fi
      fi
      echo "file '$o'" >> $TXT
    done
    
    cat "$TXT"
    ffmpeg -f concat -safe 0 -i "$TXT" -i "input 3.mp3" -filter_complex "
    [0:a][1:a] amix=inputs=2:duration=shortest
    " -c:v copy -c:a aac out/output.mp4 -y -hide_banner
    
    mpv out/output.mp4
    

    it was simplified and edited part of script