FFmpeg Batch Transcode Audio
Recently I have been dealing with transcoding media files for my streaming site travisflix and performing ad-hoc media conversions is no longer realistic. I strongly recommend checking out my FFmpeg Examples to give you a head start if you’re not familiar with deep diving in FFmpeg details.
Before you choose to batch transcode the audio for your entire library, the following will stdout anything not using the AAC codec:
find . -mount -depth -maxdepth 2 -regextype posix-extended -regex '.*mkv$|.*mp4$' -type f -exec bash -c 'echo "{}" $(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "{}")' 2>/dev/null \; | grep -v 'aac$' # Same command but multiple lines find . -mount -depth -maxdepth 2 -regextype posix-extended -regex '.*mkv$|.*mp4$' -type f -exec bash -c \ 'echo "{}" $(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "{}")' 2>/dev/null \; | grep -v 'aac$'
The following script will look in $TEMPDIR
for mkv/mp4 files not using the AAC audio codec, and if found, transcodes them into $WORKDIR
then moves the final output file back to the original source, overwriting the original file. Test functionality first before running on a large batch.
#!/usr/bin/env bash # ## DESCRIPTION: Transcode with FFmpeg all (mkv, mp4) media files in specified directory, ## overwriting the original source files. ## ## AUTHOR: Travis Runyard ## Revised: 04/18/2020 ## URL: https://sysinfo.io/ffmpeg-batch-transcode-audio/ # Exit on first non-zero exit code set -e # Set shell options shopt -s globstar shopt -u nullglob # Declare variables (optionally) are indexed arrays # Only associative arrays require declaration, but standardizing is a good thing declare -a FILENAME declare -a LOG # Modify TEMPDIR and WORKDIR to suit your needs TEMPDIR="/mnt/pool0/p0ds0smb/temp/ffmpeg" WORKDIR="$TEMPDIR/.working" SCRIPT_NAME=$(basename "$0") usage() { cat 1>&2 <<EOF Usage: "$SCRIPT_NAME" [options] -h| --help Help shows this message. -d| --directory Specify the directory to process. -w| --workdir Writable working directory for FFmpeg. If you don't specify this option, the subdirectory named "working" will be used within the --directory path or \$TEMPDIR. EOF } while [ "$1" != "" ]; do case $1 in -d | --directory ) shift TEMPDIR="${1:-$TEMPDIR}" WORKDIR="$WORKDIR" ;; -w | --workdir ) shift WORKDIR="${1:-$WORKDIR}" ;; -h | --help ) usage exit 0 ;; * ) usage exit 1 esac shift done cd $TEMPDIR echo -e "*** DEBUG ***\n\$TEMPDIR = $TEMPDIR\n\$WORKDIR = $WORKDIR" [ -d "$WORKDIR" ] || mkdir "$WORKDIR" # Set field separator to newline OIFS="$IFS" IFS=$'\n' FILENAME=($(find . ! -path "*$(basename "$WORKDIR")/*" -regextype posix-extended -regex '.*\.(mkv|mp4)$' -type f -print)) if [ -n "$FILENAME" ]; then for f in "${FILENAME[@]}"; do LOG+=("$f") AUDIOFORMAT=($(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name,channels -of default=nw=1:nk=1 "$f")) if ! [ "${AUDIOFORMAT[0]}" = "aac" ]; then # Build ffmpeg array within the loop to populate variables args=( -nostdin -y -i "$f" -map 0:v:0 -map 0:a -map 0:s? -ac "${AUDIOFORMAT[1]}" -ar 48000 -metadata:s:a:0 language=eng -c:v copy -c:a aac -c:s copy "$WORKDIR/$(basename "$f")" ) echo -e "*** DEBUG: ffprobe detected '${AUDIOFORMAT[0]}' in the default audio stream with '${AUDIOFORMAT[1]}' channels\nPreparing to convert audio codec to AAC..." ; sleep 1 echo -e "*** DEBUG: ffmpeg ${args[@]}" ffmpeg "${args[@]}" || break echo -e "*** DEBUG: Moving '$WORKDIR/$(basename "$f")' back to source directory name '$(dirname "$f")'"; sleep 1 mv -ufv "$WORKDIR/$(basename "$f")" "$(dirname "$f")" || break fi done printf '*** DEBUG ***\nPROCESSED:%s\n' for i in "${LOG[@]}"; do echo "$i"; done else echo -e "*** DEBUG: No files to process" fi