ffmpeg fade and cross fade audio and video

NapoleonWils0n

Active Member

Reaction score: 50
Messages: 152

Using ffmpeg to fade and cross fade audio and video

ffmpeg can be used to fade and cross fade audio and video in a single clip,
or between 2 or more clips which is handy if you dont wont to open a video editor


Combine audio and video files into a new file

Sometimes you need to extract the audio from a video file for example to fix the audio with audacity,
and then recombine the cleaned audio file with the original video track

Extract the audio from a video file as a wav

Bash:
ffmpeg -i infile.mp4 -f wav outfile.wav
You can then open the wav file with audacity and clean up the audio with noise removal and adjust the audio levels etc
Audacity also has a macro function that lets you apply multiple audio filters and export the audio with a single click

After exporting the audio file as a wav we need to recombine the new audio with the original video track into a new video file

Bash:
#!/bin/sh

# combine video and audio clip

# script usage
script_usage=$(printf "%s\n%s\n" "$(basename "$0") -v video.(mp4|mov|mkv) -a audio.(wav|m4a)")

# video file destination
videofile="$HOME/Desktop/video-$(date +"%Y-%m-%d-%H-%M-%S").mp4"

# ffmpeg record function
record () {
    printf "%s\n" "+ Getting video duration" && \
    video_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$video" | cut -d\. -f1)
    vid_offset=$(printf "%s\n" "${video_dur}-1" | bc -l)
    printf "%s\n" "+ Combining video and audio" && \
    ffmpeg \
    -hide_banner \
    -stats -v panic \
    -i "$video" \
    -i "$audio" \
    -filter_complex \
    " [1:a] afade=t=in:st=0:d=1,afade=t=out:st='$vid_offset':d=1[fa]; \
      [0:v] fade=t=out:st='$vid_offset':d=1[fv]
    " \
    -c:a aac \
    -c:v libx264 -preset fast \
    -profile:v high \
    -crf 18 -coder 1 \
    -pix_fmt yuv420p \
    -map "[fv]" -map "[fa]" \
    -movflags +faststart \
    -f mp4 \
    "$videofile"
}

# check arguments
if [[ $# -eq 4 ]]; then
   # group commands
   {
   [ "$1" = '-v' ] && \
   [ -f "$2" ] && \
   printf "%s\n" "$2" | grep -Eo '.(mov|mp4|mkv)$' 1>/dev/null && \
   [ "$3" = '-a' ] && \
   [ -f "$4" ] && \
   printf "%s\n" "$4" | grep -Eo '.(wav|m4a)$' 1>/dev/null
   } || { printf "%s\n" "$script_usage" && exit; }
   # run record function to combine video and audio into a video file
   # set variables
   video="$2"
   audio="$4"
   record "$video" "$audio"
else
   { printf "%s\n" "$script_usage" && exit; }
fi
fade video out, and audio fade audio in and out

fade the audio of a clip in and out and fade the video out to black

note i dont fade the video in from black,
because then youtube will create black a thumbnail from the first video frame


Bash:
#!/bin/sh

# fade audio in and out, fade video out

# script usage
script_usage=$(printf "%s\n%s\n" "$(basename "$0") -i video.(mp4|mov|mkv)")

# video file destination
videofile="$HOME/Desktop/video-$(date +"%Y-%m-%d-%H-%M-%S").mp4"

# ffmpeg record function
record () {
    printf "%s\n" "+ Getting video duration" && \
    video_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$video" | cut -d\. -f1)
    vid_offset=$(printf "%s\n" "${video_dur}-1" | bc -l)
    printf "%s\n" "+ Converting video" && \
    ffmpeg \
    -hide_banner \
    -stats -v panic \
    -i "$video" \
    -filter_complex \
    " [0:a] afade=t=in:st=0:d=1,afade=t=out:st='$vid_offset':d=1[fa]; \
      [0:v] fade=t=out:st='$vid_offset':d=1[fv]
    " \
    -c:a aac \
    -c:v libx264 -preset fast \
    -profile:v high \
    -crf 18 -coder 1 \
    -pix_fmt yuv420p \
    -map "[fv]" -map "[fa]" \
    -movflags +faststart \
    -f mp4 \
    "$videofile"
}

# check arguments
if [[ $# -eq 2 ]]; then
   # group commands
   {
   [ "$1" = '-i' ] && \
   [ -f "$2" ] && \
   printf "%s\n" "$2" | grep -Eo '.(mov|mp4|mkv)$' 1>/dev/null && \
   } || { printf "%s\n" "$script_usage" && exit; }
   # run record function to combine video and audio into a video file
   # set variables
   video="$2"
   record "$video"
else
   { printf "%s\n" "$script_usage" && exit; }
fi
Audio cross fade between 2 clips

Bash:
#!/bin/sh

# ffmpeg fadein fadeout clips

# script usage
script_usage=$(printf "%s\n%s\n" "$(basename "$0") -i clip-1.mp4 -i clip-2.mp4")

# video file destination
videofile="$HOME/Desktop/fade-a-$(date +"%Y-%m-%d-%H-%M-%S").mp4"

# check arguments passed to script
if [ $# -eq 4 ]; then
    {
    [ "$1" = '-i' ] && \
    [ -f "$2" ] && \
    [ "$3" = '-i' ] && \
    [ -f "$4" ]
    } || { printf "%s\n" "$script_usage" && exit; }
else
   { printf "%s\n" "$script_usage" && exit; }
fi

# variable names for files passed to script
clip1="$2"
clip2="$4"

# clip durations for fades
clip1_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$clip1" | cut -d\. -f1)
clip2_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$clip2" | cut -d\. -f1)

# clip1 use the bc command to remove 1 second from length of clip for cross fade
clip1_offset=$(printf "%s\n" "${clip1_dur}-1" | bc -l)
clip2_offset=$(printf "%s\n" "${clip2_dur}-1" | bc -l)

# ffmpeg command
ffmpeg \
-i "$clip1" -i "$clip2" \
-filter_complex \
"   [0:v][1:v]concat=n=2[output];
    [0:a] afade=t=in:st=0:d=1 [fadein]; \
    [1:a] afade=t=out:st='$clip2_offset':d=1 [fadeout]; \
    [fadein][fadeout] acrossfade=d=1:o=0 [audio]
" \
-map "[output]" -map "[audio]" "$videofile"
Audio Video cross fade between 2 clips

Bash:
#!/bin/sh

# ffmpeg cross fade clips

# script usage
script_usage=$(printf "%s\n%s\n" "$(basename "$0") -i clip-1.mp4 -i clip-2.mp4")

# video file destination
videofile="$HOME/Desktop/fade-va-$(date +"%Y-%m-%d-%H-%M-%S").mp4"

# check arguments passed to script
if [ $# -eq 4 ]; then
    {
    [ "$1" = '-i' ] && \
    [ -f "$2" ] && \
    [ "$3" = '-i' ] && \
    [ -f "$4" ]
    } || { printf "%s\n" "$script_usage" && exit; }
else
   { printf "%s\n" "$script_usage" && exit; }
fi

# variable names for files passed to script
clip1="$2"
clip2="$4"

# clip durations for fades
clip1_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$clip1" | cut -d\. -f1)
clip2_dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$clip2" | cut -d\. -f1)

# clip1 use the bc command to remove 1 second from length of clip for cross fade
clip1_offset=$(printf "%s\n" "${clip1_dur}-1" | bc -l)
clip2_offset=$(printf "%s\n" "${clip2_dur}-1" | bc -l)

# ffmpeg command
ffmpeg \
-i "$clip1" -i "$clip2" \
-an -filter_complex \
"   [0:v]trim=start=0:end='$clip1_offset',setpts=PTS-STARTPTS[firstclip];
    [1:v]trim=start=1,setpts=PTS-STARTPTS[secondclip];
    [0:v]trim=start='$clip1_offset':end='$clip1_dur',setpts=PTS-STARTPTS[fadeoutsrc];
    [1:v]trim=start=0:end=1,setpts=PTS-STARTPTS[fadeinsrc];
    [fadeinsrc]format=pix_fmts=yuva420p,     
                fade=t=in:st=0:d=1:alpha=1[fadein];
    [fadeoutsrc]format=pix_fmts=yuva420p,
                fade=t=out:st=0:d=1:alpha=1[fadeout];
    [fadein]fifo[fadeinfifo];
    [fadeout]fifo[fadeoutfifo];
    [fadeoutfifo][fadeinfifo]overlay[crossfade];
    [firstclip][crossfade][secondclip]concat=n=3[output];
    [0:a] afade=t=in:st=0:d=1 [audiofadein]; \
    [1:a] afade=t=out:st='$clip2_offset':d=1 [audiofadeout]; \
    [audiofadein][audiofadeout] acrossfade=d=1 [audio]
" \
-map "[output]" -map "[audio]" "$videofile"
 
Top