ffmpeg fade and cross fade audio and video

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"
 
Back
Top