Monday, February 07, 2022

 

Windows 10: Bulk Normalize Audio of mp4 Videos

 I've seen a Windows solution like what I've produced here, created by someone else many years ago - but I don't know what it was called, nor where to get it anymore. I also think it was only for AVI files. I've resorted to writing my own from scratch to handle MP4's. I'd like to share this with anyone and everyone who are interested as this site has helped me a lot to get me to this point.

I've written a DOS batch script in my Windows 10 that can take multiple *mp4 files (and yes - with spaces in the name) and normalize the volume using mp3gain. (I tried MP4GAIN, but the trial period ended in like 1 hour). This one will process much quicker than MP4GAIN, I found out. I do this because I can specifically control ffmpeg and mp3gain every step of the way and do things that mp4gain or ffmpeg-normalize might not be able to.

By all means, it's not fool-proof, nor is it optimized. Rather it can be a springboard for someone to tune/tweak for their own purposes. I extract the audio as mp3 VBR and let mp3gain do its magic on it and then put it back. I find I can process 5 concurrent normalizations, but it's subject to the available hard disk space if you want to try more at once.


Create the folders - D:\Vids\bin can be anywhere, but have the original, working and completed folders inside.

D:\Vids\bin

D:\Vids\bin\original

D:\Vids\bin\working

D:\Vids\bin\completed


Download the latest - they're free as of 9/1/2020 that I've checked. I'm using the following:

D:\Vids\bin\ffmpeg.exe  ( ffmpeg version git-2020-08-28-ccc7120 )

D:\Vids\bin\mp3gain.exe ( mp3gain version 1.4.6 )


Create a D:\Vids\bin\normalize.bat and put this in there:


echo off

setlocal enableextensions enabledelayedexpansion


for %%I in (original\*.mp4) DO (


  echo Creating working\%%~nI.bat

  echo echo off > "working\%%~nI.bat"

  echo echo Unpacking audio and video from %%I . >> "working\%%~nI.bat"

  echo echo ..\ffmpeg.exe -v 0 -i "..\%%I" -q:a 0 -map a ".\%%~nI_audio_only.mp3" -vcodec copy -map 0:v ".\%%~nI_video_only.mp4" >> "working\%%~nI.bat"

  echo ..\ffmpeg.exe -v 0 -i "..\%%I" -q:a 0 -map a ".\%%~nI_audio_only.mp3" -vcodec copy -map 0:v ".\%%~nI_video_only.mp4" >> "working\%%~nI.bat"

  echo echo . >> "working\%%~nI.bat"

  echo echo Normalizing extracted audio with Mp3Gain. >> "working\%%~nI.bat"

  echo echo ..\mp3gain.exe ".\%%~nI_audio_only.mp3" >> "working\%%~nI.bat"

  echo ..\mp3gain.exe ".\%%~nI_audio_only.mp3" >> "working\%%~nI.bat"

  echo echo Reconstructing normalized video from component parts. >> "working\%%~nI.bat"

  echo echo .\ffmpeg.exe -v 0 -i ".\%%~nI_video_only.mp4" -i ".\%%~nI_audio_only.mp3" -codec copy "..\completed\%%~nI%%~xI" >> "working\%%~nI.bat"

  echo ..\ffmpeg.exe -v 0 -i ".\%%~nI_video_only.mp4" -i ".\%%~nI_audio_only.mp3" -codec copy "..\completed\%%~nI%%~xI" >> "working\%%~nI.bat"

  echo echo Post-process clean up. >> "working\%%~nI.bat"

  echo del ".\%%~nI_audio_only.mp3" >> "working\%%~nI.bat"

  echo del ".\%%~nI_video_only.mp4" >> "working\%%~nI.bat"

  echo echo Done. >> "working\%%~nI.bat"

  echo echo on >> "working\%%~nI.bat"

  

)

pause

echo on

Place the original mp4's into your D:\Vids\bin\original folder and run normalize.bat. I do it from the cmd.exe DOS prompt, but you can double-click the batch file in Windows Explorer too. It will search the original folder for mp4's and create individual batch files in D:\Vids\bin\working. You can kick off one, two or four of these batch files in D:\Vids\bin\working at once.

Each original mp4 will be unpacked into a mp4 with no audio, and an mp3 VBR with no video.

The video and audio extractions are done at the same time, thinking this will likely produce two files that can be merged back together later without the video or audio being shorter than the other. The video and audio extracts go into D:\Vids\bin\working.

Then mp3gain.exe will be run against the extracted mp3 and then the audio and video streams will be merged again with ffmpeg and placed into the D:\Vids\bin\completed folder.

The temporary unpacked mp4 and mp3's will be deleted once the process completes.


=======

LIMITATIONS:

The filename of the mp4's cannot contain certain special characters (like ! or ♫) but characters such as spaces ( ) [ ] # ' @ - _ are OK. If you find that the generated script fails or exits much too quickly and doesn't process the video file - try renaming the original file.

I don't have a check for ffprobe to see if the embedded audio is already an mp3, and I found that not all aac audio can be normalized by aacgain.exe. Hence I force everything to come out as mp3 VBR at the highest quality. If the original audio of sufficient quality, I don't mind doing this.


REQUEST

If people can extend this script further to make it aware of the various audio types and do an audio copy if it is already mp3, it would be helpful. Or recognize that the AAC audio needs to be converted to mp3 or left alone as AAC.

Make it handle other video file types (Avi, mkv, wmv, etc.)

Check if a failure occurred after each step and exit the script, and allow for a resume feature so that re-extractions do not need to occur.

Add flags to enable compressing the video file using libx264 -crf 18 -preset veryslow to save some space - but this will make things run very slow.

Any other enhancements you can think of.

~Lum


 

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:

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:

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:

  1. make a local copy of the video in the working folder (named e.g.: TEMP_x.mp4)
  2. analyze the video to determine resolution and encoding and determine if any conversion is necessary
  3. extract the audio to TEMP_x_audio_only.aac
  4. extract the video to TEMP_x_video_only.mp4 with any re-encoding (if necessary) applied
  5. reconstruct the video from the above video and audio files
  6. apply volume gains using aacgain.exe
  7. write the completed file to D:\Vids\bin\completed
  8. 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:


~Lum

Labels:


This page is powered by Blogger. Isn't yours?