You can batch encode all video files in a main folder and its subdirectories to H.265 with new names using FFmpeg combined with a simple shell script or PowerShell loop. This approach recursively finds every supported media file, applies the libx265 encoder, preserves audio streams, and outputs the result into a designated destination folder while renaming files based on your preferred naming convention.
Understanding the Directory Structure
Before writing any commands, it is crucial to define how your source and destination folders are organized. The goal is to process files from a root directory that contains multiple subfolders, each potentially holding various video formats like MP4, MKV, or AVI. You need a strategy that traverses this hierarchy without manually opening each folder.
FFmpeg itself does not have a built-in recursive file finder; it processes one input at a time. Therefore, you must use an external tool to iterate through the directory tree. On Windows, PowerShell is ideal for this task due to its robust string manipulation capabilities. On macOS and Linux, Bash with the
find command provides equivalent functionality. The key challenge is ensuring that the output path mirrors the input structure or places all encoded files into a single new location, depending on your storage preferences.
Setting Up the Encoding Parameters
To achieve high-quality compression with the H.265 codec, you must configure specific FFmpeg flags. The primary encoder is
libx265. However, simply calling this encoder is not enough for optimal results. You should set the
-crf (Constant Rate Factor) value to control quality versus file size. A CRF of 28 is generally considered visually lossless for most content, while lower values like 20-23 offer higher quality at the cost of larger files.
Additionally, you must handle audio streams correctly. If you do not specify audio handling, FFmpeg might drop them or re-encode them unnecessarily. Use
-c:a copy to stream-copy the audio, which saves significant processing time and avoids generation loss. For the video stream, use
-c:v libx265. It is also advisable to include
-pix_fmt yuv420p10le if you want 10-bit color depth, which improves gradient handling, though standard
yuv420p is sufficient for most playback devices.
For more details on comparing encoding efficiencies, you might find
FFmpeg vs LAME: Equivalent MP3 Encoding with Low CBR useful, as the principles of bitrate management apply similarly across video and audio codecs.
Implementing the Loop on Windows
PowerShell offers a straightforward way to iterate through directories. First, define your source root directory and your destination directory. Then, use the
Get-ChildItem cmdlet with the
-Recurse flag to locate all video files. You will need to filter these by extension, such as
*.mp4,
*.mkv, etc.
Inside the loop, construct the output filename. To rename the files, you can strip the original extension and append a suffix like
_encoded.mp4, or replace the entire name with a timestamp or sequential number. The command structure within the loop would look like this:
$sourcePath = "C:\Videos\Source"
$destPath = "C:\Videos\Encoded"
Get-ChildItem -Path $sourcePath -Include *.mp4,*.mkv,*.avi -Recurse | ForEach-Object {
$inputFile = $_.FullName
$outputFile = Join-Path $destPath ($_.BaseName + "_h265.mp4")
ffmpeg -i $inputFile -c:v libx265 -crf 28 -c:a copy -y $outputFile
}
This script processes each file sequentially. The
-y flag overwrites existing output files without prompting, which is essential for automation. Note that if your subfolders contain many files, this process may take considerable time depending on your CPU power.
Implementing the Loop on macOS and Linux
On Unix-like systems, the
find command is the standard tool for locating files. You can combine it with
exec or a
while read loop to pass filenames to FFmpeg. Because filenames can contain spaces, it is critical to handle them safely. Using
find ... -print0 and reading with
-d '' ensures that spaces and special characters do not break the command.
#!/bin/bash
SOURCE_DIR="./source_videos"
DEST_DIR="./encoded_videos"
mkdir -p "$DEST_DIR"
find "$SOURCE_DIR" -type f \( -name "*.mp4" -o -name "*.mkv" \) -print0 | while IFS= read -r -d '' file; do
filename=$(basename "$file")
basename_no_ext="${filename%.*}"
output_file="$DEST_DIR/${basename_no_ext}_h265.mp4"
ffmpeg -i "$file" -c:v libx265 -crf 28 -c:a copy -y "$output_file"
done
This bash script creates the destination directory if it doesn't exist, then iterates through all matching files. It strips the original extension and appends
_h265.mp4. This method is highly portable and works efficiently on headless servers or local machines.
Handling Edge Cases and Performance
When encoding multiple files, memory usage can spike if you attempt parallel processing. While FFmpeg supports multi-threading via
-threads 0 (which uses all available cores), running multiple instances simultaneously can overwhelm your system. It is often safer to run them sequentially unless you have ample RAM and a powerful multi-core processor.
If you encounter errors where certain files fail to decode, check the FFmpeg logs for unsupported codecs within those specific containers. Some older AVI files may contain codecs that
libx265 cannot easily work with if the container structure is non-standard. In such cases, you may need to transcode to an intermediate format first. For issues related to specific camera formats failing to play after conversion, refer to
DV Video from Sony Digital 8 Camera Fails to Play in AVI using FFmpeg with H.265, which discusses similar compatibility pitfalls.
Always verify the output files after encoding. Check that the duration matches the source and that audio sync remains intact. If you notice FPS drops or artifacts, adjust the
-crf value or try adding
-preset medium to balance speed and quality.
Frequently Asked Questions
Can I preserve the original folder structure in the output?
Yes, but it requires more complex scripting. Instead of placing all files in one destination folder, you must calculate the relative path from the source root to the current file and recreate that directory structure in the destination before saving the new file.
What is the best CRF value for H.265 encoding?
A CRF of 28 is widely recommended for a good balance between file size and visual quality. Lower values like 20 provide near-lossless quality but result in much larger files, while higher values like 32 reduce file size significantly but may introduce visible compression artifacts.
How do I skip files that are already encoded?
You can add a check in your script to see if the output file already exists before running FFmpeg. If the output file is present, the script can skip that iteration, saving time and computational resources.
Will copying the audio stream cause quality loss?
No, using
-c:a copy performs a stream copy, meaning the audio data is transferred exactly as it is in the source file without re-encoding. This is lossless and very fast.
Is H.265 compatible with all devices?
While H.265 is becoming standard, older devices like early smartphones or some smart TVs may not support it. Always test your encoded files on the target playback devices to ensure compatibility before archiving large batches.