Batch Transcode Videos with Find, Xargs, and FFmpeg

Transcoding large collections of video files can be automated efficiently in Unix-like operating systems using a combination of find, xargs, and ffmpeg. This guide provides a direct, practical method for locating video files across nested directories, passing them safely to xargs, and batch processing them with ffmpeg while handling spaces in filenames and controlling parallel execution.

The Complete Batch Transcoding Command

Run the following command in your terminal, replacing the directory path, file extension, and ffmpeg encoding parameters to fit your requirements:

find /path/to/videos -type f -name "*.mkv" -print0 | xargs -0 -n 1 -P 2 -I {} bash -c '
  input="$1"
  output="${input%.*}.mp4"
  ffmpeg -nostdin -y -i "$input" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k "$output"
' _ {}

Breakdown of the Command

1. Locating Files with find

  • /path/to/videos: The target directory where find will search recursively.
  • -type f: Restricts search results exclusively to files, ignoring directories.
  • -name "*.mkv": Filters for files ending with the specified extension. You can match multiple extensions using -name "*.mkv" -o -name "*.mov".
  • -print0: Separates results with a null character (\0) rather than a newline. This ensures file paths containing spaces, single quotes, or newlines are parsed without breaking downstream commands.

2. Managing the Pipeline with xargs

  • -0: Instructs xargs to expect null-terminated items, directly matching the -print0 flag from find.
  • -n 1: Passes one file at a time to the execution command.
  • -P 2: Controls concurrency by setting the maximum number of parallel processes. Adjust this number according to your CPU core count and GPU capabilities. A value of 1 processes videos sequentially.
  • -I {}: Defines {} as a placeholder for the file path passed to the shell.

3. Execution and Naming with bash and ffmpeg

  • bash -c '...' _ {}: Executes a subshell where $1 is assigned the current file path represented by {}.
  • output="${input%.*}.mp4": Uses POSIX shell parameter expansion to strip the original file extension and append the new desired extension (.mp4).
  • -nostdin: Prevents ffmpeg from consuming input from standard input, which would otherwise disrupt the xargs execution loop.
  • -y: Automatically overwrites existing output files with the same name. Remove this flag if you want ffmpeg to skip or prompt before overwriting.
  • -c:v libx264 -crf 23 -preset medium: Encodes the video stream to H.264 using standard constant rate factor and speed settings.
  • -c:a aac -b:a 128k: Encodes the audio stream to AAC at a 128 kbps bitrate.

Preserving or Deleting the Original Files

By default, the script creates a new transcoded file in the same directory alongside the original. If you want to automatically delete the source file after a successful conversion, append && rm "$input" directly after the ffmpeg command inside the subshell:

find /path/to/videos -type f -name "*.mkv" -print0 | xargs -0 -n 1 -P 2 -I {} bash -c '
  input="$1"
  output="${input%.*}.mp4"
  ffmpeg -nostdin -y -i "$input" -c:v libx264 -crf 23 -c:a aac "$output" && rm "$input"
' _ {}

Using && ensures that the original file is only removed if ffmpeg returns an exit status of 0, protecting your source media against removal in the event of an encoding failure.