Monday, February 07, 2022
Windows 10: Bulk convert mkv, mov, flv, avi, mp4 to mp4 and normalize audio, video dimensions and encoding using ffmpeg.exe
I'd like to share what I have worked on.
I have recently acquired a Synology DS220J and installed a Plex server on it. After being dismayed because I ran into a lot of "Server does not have the power to convert this video" messages, I figured out that this happens to videos that are:
- Not Video Encoded with H.264 (e.g: VP9, H.265, etc.)
- Resolution in excess of 1920 x 1080p
- Audio that is not mp3 or aac (e.g.: flac, ogg, etc.)
So I have written a windows batch script that will bulk-convert my video files (mkv, avi, webm, mp4, flv, etc.) so that the resultant videos will have the following characteristics:
- Video Encoded with H.264
- Resolutions in EXCESS of 1920 x 1080p scaled down to 1920 x 1080p. Any resolution less than or equal to 1920 x 1080p will be left as-is.
- Video output as *.mp4 regardless of input video type
- Audio converted to aac regardless of input audio type
- Audio gain normalized using aacgain.exe (alternately, we could force things to be mp3 and use mp3gain.exe)
You will need to create the following folder structure and add the following programs:
Folders
D:\Vids\bin
D:\Vids\bin\original
D:\Vids\bin\working
D:\Vids\bin\completed
Files
D:\Vids\bin\ffmpeg.exe
D:\Vids\bin\ffprobe.exe
D:\Vids\bin\aacgain.exe
D:\Vids\bin\normalize_video_for_plex.bat (copy the batch script below and name it thusly)
Place your video files in the D:\Vids\bin\original folder and run the following "normalize_video_for_plex.bat" DOS/Windows batch file. Copy/Paste the following DOS/Windows batch code into a text file named "normalize_video_for_plex.bat" and save it.
rem echo off
setlocal enableextensions enabledelayedexpansion
set A=0
set /A COUNTER=COUNTER+1
echo on
for %%I in (d:\Vids\bin\original\*.*) DO (
set ORIG_FILE=%%~nI%%~xI
set TEMP_INPUT_FILE=%%~xI
set TEMP_WORK=.bat
rem echo Creating working\.bat
echo echo off
echo cls
echo set ORIG_FILE="%%~nI%%~xI"
echo set TEMP_INPUT_FILE=TEMP_!COUNTER!%%~xI
echo set NEW_FILE="..\completed\%%~nI.mp4"
echo.
echo echo -------------------------------------------------------------------------
echo echo Normalizing: %%I
echo echo -------------------------------------------------------------------------
echo.
echo if not exist "%%I" ^(
echo echo No such file "%%I"
echo goto end
echo ^)
echo.
echo echo Making local copy as ..\working\%%TEMP_INPUT_FILE%%"
echo copy "%%I" "D:\Vids\bin\working\%%TEMP_INPUT_FILE%%"
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo for %%%%f in ^("%%TEMP_INPUT_FILE%%"^) do set OUTPUT_AUDIO_ONLY=%%%%~nf_audio_only.aac
echo for %%%%f in ^("%%TEMP_INPUT_FILE%%"^) do set OUTPUT_VIDEO_ONLY=%%%%~nf_video_only.mp4
echo for %%%%f in ^("%%TEMP_INPUT_FILE%%"^) do set OUTPUT_FILE=%%%%~nf_new.mp4
echo.
echo rem Set defaults, clean up folder before starting
echo set VIDEO_SCALE=default
echo set VIDEO_ENCODING=default
echo.
echo rem MAX_PIXEL_SIZE will equal 1920 x 1080 ^(2073600 pixels^)
echo set /A MAX_PIXEL_SIZE=1920*1080
echo.
echo if exist "%%OUTPUT_VIDEO_ONLY%%" del /F "%%OUTPUT_VIDEO_ONLY%%"
echo if exist "%%OUTPUT_AUDIO_ONLY%%" del /F "%%OUTPUT_AUDIO_ONLY%%"
echo.
echo echo Determining encoder type and video scale
echo FOR /F "tokens=* USEBACKQ" %%%%g IN ^(`D:\Vids\bin\ffprobe.exe -v error -select_streams v:0 -show_entries stream^^=width^^,height -of csv^^=s^^=x:p^^=0 "%%TEMP_INPUT_FILE%%"`^) do ^(SET "VIDEO_SCALE=%%%%g"^)
echo FOR /F "tokens=* USEBACKQ" %%%%g IN ^(`D:\Vids\bin\ffprobe.exe -v error -select_streams v:0 -show_entries stream^^=codec_name -of default^^=nokey^^=1:noprint_wrappers^^=1 "%%TEMP_INPUT_FILE%%"`^) do ^(SET "VIDEO_ENCODING=%%%%g"^)
echo.
echo rem Convert VIDEO_SCALE in the form of '1920x1080' to '1920*1080' ^(substitute * for x^)
echo rem so that it can be evaluated mathematically.
echo set PIXEL_EQN=%%VIDEO_SCALE:x=*%%
echo.
echo rem Evaluate the pixel size of the current video's single frame
echo set /A PIXEL_SIZE="%%PIXEL_EQN%%"
echo echo Found Video encoder '%%VIDEO_ENCODING%%' @ %%VIDEO_SCALE%% ^(%%PIXEL_SIZE%% pixels^)
echo.
echo rem If we determine that the current video's PIXEL_SIZE is greater than the MAX_PIXEL_SIZE
echo rem for 1920x1080, then set the NEED_SCALING flag to downconvert to 1920x1080
echo if %%PIXEL_SIZE%% GTR %%MAX_PIXEL_SIZE%% ^(set NEED_SCALING=1^) else ^(set NEED_SCALING=0^)
echo.
echo rem If the VIDEO_ENCODING is not equal to h264 ^(libx264^) then set NEED_ENCODING flag
echo if %%VIDEO_ENCODING%% NEQ h264 ^(set NEED_ENCODING=1^) else ^(set NEED_ENCODING=0^)
echo.
echo echo.
echo if %%NEED_SCALING%% EQU 1 ^(
echo if %%NEED_ENCODING%% EQU 1 ^(
echo echo Need to re-encode to h264 and rescale to 1920x1080
echo ^) ELSE ^(
echo echo Need to rescale to 1920x1080 only
echo ^)
echo ^) ELSE ^(
echo if %%NEED_ENCODING%% EQU 1 ^(
echo echo Need to re-encode to h264 only
echo ^) ELSE ^(
echo echo need no changes to video
echo ^)
echo ^)
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo echo Extracting video and audio streams to temp files
echo if %%NEED_SCALING%% EQU 1 ^(
echo if %%NEED_ENCODING%% EQU 1 ^(
echo echo Extracting audio stream to %%OUTPUT_AUDIO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -q:a 0 -map a -q:a 2 "%%OUTPUT_AUDIO_ONLY%%"
echo echo.
echo echo Extracting video stream to %%OUTPUT_VIDEO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -an -c:s copy -c:v libx264 -vf scale=1920:1080 -crf 20 "%%OUTPUT_VIDEO_ONLY%%"
echo ^) ELSE ^(
echo echo Extracting audio stream to %%OUTPUT_AUDIO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -q:a 0 -map a -q:a 2 "%%OUTPUT_AUDIO_ONLY%%"
echo echo.
echo echo Extracting video stream to %%OUTPUT_VIDEO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -an -c:s copy -vf scale=1920:1080 -crf 20 "%%OUTPUT_VIDEO_ONLY%%"
echo ^)
echo ^) ELSE ^(
echo if %%NEED_ENCODING%% EQU 1 ^(
echo echo Extracting audio stream to %%OUTPUT_AUDIO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -q:a 0 -map a -q:a 2 "%%OUTPUT_AUDIO_ONLY%%"
echo echo.
echo echo Extracting video stream to %%OUTPUT_VIDEO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -an -c:s copy -c:v libx264 "%%OUTPUT_VIDEO_ONLY%%"
echo ^) ELSE ^(
echo echo Extracting audio stream to %%OUTPUT_AUDIO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -q:a 0 -map a -q:a 2 "%%OUTPUT_AUDIO_ONLY%%"
echo echo.
echo echo Extracting video stream to %%OUTPUT_VIDEO_ONLY%%
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats -i "%%TEMP_INPUT_FILE%%" -an -c:s copy -vcodec copy -map 0:v "%%OUTPUT_VIDEO_ONLY%%"
echo ^)
echo ^)
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo echo Reassembling converted video and audio files
echo D:\Vids\bin\ffmpeg.exe -v 0 -stats %%OVERWRITE_FILE%% -i "%%OUTPUT_VIDEO_ONLY%%" -i "%%OUTPUT_AUDIO_ONLY%%" -codec copy "%%OUTPUT_FILE%%"
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo echo Normalizing audio track volume
echo D:\Vids\bin\aacgain.exe "%%OUTPUT_FILE%%"
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo echo Moving file to completed folder
echo move "%%OUTPUT_FILE%%" ..\completed\%%NEW_FILE%%
echo echo -------------------------------------------------------------------------
echo echo.
echo.
echo echo Cleaning up
echo if exist "%%OUTPUT_VIDEO_ONLY%%" del /F "%%OUTPUT_VIDEO_ONLY%%"
echo if exist "%%OUTPUT_AUDIO_ONLY%%" del /F "%%OUTPUT_AUDIO_ONLY%%"
echo if exist "%%TEMP_INPUT_FILE%%" del /F "%%TEMP_INPUT_FILE%%"
echo.
echo :end
echo echo -------------------------------------------------------------------------
echo echo.
echo echo Done
echo echo on
set /A COUNTER=COUNTER+1
) > "working\TEMP_!COUNTER!.bat"
echo off
This will create a series of temporary batch scripts in the D:\Vids\bin\working folder named TEMP_1.bat, TEMP_2.bat, etc. One for each of the videos in the original folder, and they will be the ones to actually perform the conversions.
To begin conversion, simply execute the TEMP_1.bat which will:
- make a local copy of the video in the working folder (named e.g.: TEMP_x.mp4)
- analyze the video to determine resolution and encoding and determine if any conversion is necessary
- extract the audio to TEMP_x_audio_only.aac
- extract the video to TEMP_x_video_only.mp4 with any re-encoding (if necessary) applied
- reconstruct the video from the above video and audio files
- apply volume gains using aacgain.exe
- write the completed file to D:\Vids\bin\completed
- clean up the D:\Vids\bin\working folder
I have been bulk-converting my videos for some time now, and this works like a champ and plays well with my Synology DS220J. I hope it helps you as well. If you need to make changes in max resolution or audio encoding or any Quality or Performance tweaks or even the install locations, go ahead! Most of the hard-work has been done for you.
Links for free software used:
- ffmpeg.exe and ffprobe.exe : https://ffmpeg.org/download.html
- aacgain.exe : http://aacgain.altosdesign.com/
- outube-dl.exe : https://youtube-dl.org/
~Lum
Labels: ffmpeg.exe ffmpeg aacgain mp4 convert downscale DS220J Plex