1

I use ffmpeg to encode some videos I downloaded from various streaming sites to HEVC. In windows I use a batch file to convert all these files in a directory.

ffmpeg -n -i input.mp4 -c:v libx265 -crf 22 -c:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio output.mkv

Overall Bitrate of these videos varies between 300 kb/s to 1500 kb/s. In my testing using same CRF for all these video produced either bigger output file in the case of high bitrate videos or low quality video in the case of low bitrate. So I manually change the CRF for a bunch of similar bitrate videos. How can I achieve this using a batch file. Like, if overall Bit Rate above 950 = crf 26 if Overall Bit Rate between 750 to 949 = crf 24 if Overall Bit Rate between 500 to 749 = crf 22 if overall Bit Rate below 499 = crf 18

1 Answer 1

2
@echo off & color 0A & cls 

cd /d "%~dp0" && setlocal enabledelayedexpansion
set "_ffmpeg=F:\2020-SU\Q1569837\ffmpeg\bin\ffmpeg.exe"
set "_ffprobe=F:\2020-SU\Q1569837\ffmpeg\bin\ffprobe.exe"
set "_range_crf=99999-950-26,949-750-24,749-500-22,499-0-18"

for %%# in (*.mp4)do echo; && set /a "_c+=1+0" && for /f tokens^=2^,6^delims^=^,^  %%i in ('
2^>^&1 "!_ffprobe!" -show_entries stream^=bit_rate "%%~f#"^|%__APPDIR__%findstr.exe /e [0-9].kb/s
')do echo\Check File: "%%~nx#" ^| Bite Rate: %%~j kb/s && call :^) "!_range_crf!" "%%~j" "%%~n#" "%%~f#" 

echo\ & <con: rem./ && echo\Total Files: 000!_c! && %__APPDIR__%timeout.exe /t -1 & endlocal && goto=:EOF

:^) 
echo\ && for %%i in (%~1)do for /f "tokens=1-3delims=-" %%a in ('echo\%%~i
')do if %~2 leq %%~a if %~2 geq %%~b title<nul & title .:^| File Name:"..\%~nx4" ^| Bite Rate: %~2 kb/s ^|:. && (
       "!_ffmpeg!" -y -i "%~4" -hide_banner -v error -stats -c:v libx265 -crf %%~c -c:a libopus -b:a 48k -vbr on ^
          -compression_level 10 -frame_duration 60 -application audio "%~3.mkv" && exit /b 0 )
  • The mechanic behind for and ifs ...
       %2       %%a         %2      %%b              %%c         
:: if 951 leq 99999 and if 951 GEQ  950 = True  crf = 26 then Exit /loop :label
:: if 951 leq   949 and if 951 GEQ  750         crf = 24 then      /loop :label 
:: if 951 leq   749 and if 951 GEQ  500         crf = 22 then      /loop :label 
:: if 951 leq   499 and if 951 GEQ    0         crf = 18 then      /loop :label 
::
:: if 800 leq   999 and if 800 GEQ  950 = False crf = 26 then Keep /loop :label
:: if 800 leq   949 and if 800 GEQ  750 = True  crf = 24 then Exit /loop :label
:: if 800 leq   749 and if 800 GEQ  500         crf = 22 then      /loop :label 
:: if 800 leq   499 and if 500 GEQ    0         crf = 18 then      /loop :label 
::
:: if 600 leq   999 and if 600 GEQ  950 = False crf = 26 then Keep /loop :label
:: if 600 leq   949 and if 600 GEQ  750 = False crf = 24 then Keep /loop :label
:: if 600 leq   749 and if 600 GEQ  500 = True  crf = 22 then Exit /loop :label 
:: if 600 leq   499 and if 600 GEQ    0         crf = 18 then      /loop :label 
::
:: if 501 leq   999 and if 501 GEQ  950 = False crf = 26 then Keep /loop :label
:: if 501 leq   949 and if 501 GEQ  750 = False crf = 24 then Keep /loop :label
:: if 501 leq   749 and if 501 GEQ  500 = True  crf = 22 then Exit /loop :label
:: if 501 leq   499 and if 501 GEQ    0         crf = 18 then      /loop :label 
:: 
:: if 498 leq   999 and if 498 GEQ  950 = False crf = 26 then Keep /loop :label
:: if 498 leq   949 and if 498 GEQ  750 = False crf = 24 then Keep /loop :label
:: if 498 leq   749 and if 498 GEQ  500 = False crf = 22 then Keep /loop :label
:: if 498 leq   499 and if 498 GEQ    0 = True  crf = 18 then Exit /loop :label

Obs.: 1 There are two spaces between ^=^,^⟵⟶%%i in: delims^=^,^spacespace%%i

for %%# in (*.mp4)do for /f tokens^=2^,6^delims^=^,^spacespace%%i in (...


1. - You home work: Replace the variables below in a way compatible with your scenario, also go to your bat folder:

set "_ffmpeg=F:\2020-SU\Q1569837\ffmpeg\bin\ffmpeg.exe"
set "_ffprobe=F:\2020-SU\Q1569837\ffmpeg\bin\ffprobe.exe"

cd /d "%~dp0"

rem :: if your *.pm4 files are not in the same directory 
rem :: as your bat file, use the full path to drive/folder 
rem :: Example for drive D: folder/subfolder \Media\MP4\Convert 

cd /d "D:\Midia\MP4\Convet"

2. This batch is using multiple for loop, for it to work you will need to enable Deleyed Expansion so that the variables receive the updated/expanded values ​​at run time:

Setlocal EnableDelayedExpansion

3. Define a variable delimited by a comma, and containing between the delimiter, the values ​​to use them in the if within the for loop, so that you can work with maximum value, minimum value and compatible crf:

set "_range_crf= max-min-crf,max-min-24,max-500-22,499-0-18"
set "_range_crf=99999-950-26,949-750-24,749-500-22,499-0-18"

4. Use a simple for loop and one to list your .mp4 files and in addition, use a counter (set /a "_c+=1+0") to get a total at the end of the run:

for %%# in (*.mp4)do echo; && set /a "_c+=1+0"
  • Obs: 2 The echo; is used only to break the line and try to make the visualization easier in execution, creating a separator line in the loop.

5. Use an additional for /f to make use of this loop variable (in the 1st/for/var==%%#) where you got the full path/name (%%~f#) of the mp4 file, and pass this loop as input to ffprobe already defining (explained on item 5.), the tokens and delimiters to be taken in that command.

for /f tokens^=2^,6^delims^=^,^  %%i in (ffmprobe ...  %%~f# ... 

6. The ffprobe command used in for /f loop is:

..\ffprobe.exe -show_entries stream=bit_rate "Google Chrome - Now Everywhere.mp4"

7. Starting with redirecting StdErr to StdOut of the ffprobe output to be filtered by findstr by using the switch /End of a line with the regex numbers ([0-9]) concatenated with the string .kb/s and using proper scaping in for loop:

2^>^&1 "!_ffprobe!" -show_entries stream^=bit_rate "%%~f#" ^| "%__APPDIR__%findstr.exe" /e [0-9].kb/s

8. The above expanded command and without the escapes, results in:

2>&1 ..\ffprobe.exe -show_entries stream=bit_rate "Google Chrome - Now Everywhere.mkv" | "%__APPDIR__%findstr.exe" /e [0-9].kb/s

9. The output of the above command treated by the findstr filter results in:

Duration: 00:01:00.08, start: -0.007000, bitrate: 350 kb/s

10. The output of the above command treated by the findstr filter results in:

Duration: 00:01:00.08, start: -0.007000, bitrate: 350 kb/s

11. By using multiple delimiters, the strings in %%i and %%j output will be 00:01:00.08 and 350: is for last command output, will be 00:01:00.08 and 350:

... for /f tokens^=2^,6^delims^=^,^space %%i in (...
Duration: 00:01:00.08, start: -0.007000, bitrate: 350 kb/s

enter image description here

12.

Assuming your limit value is 350 (inclusive) for Bite Rate, you will need to use some if option in the work part:

 if %%~j > Bit_Rate      ∕∕ the same:  if %%~j > 349  (349 exclusive) 
 if %%~j  Bit_Rate      ∕∕ the same:  if %%~j  350  (350 inclusive) 

 set "_bit_rate=349"
 if %%~j > %_bit_rate%   ∕∕ the same:  if %%~j > 349  (349 exclusive) 

 set "_bit_rate=350"
 if %%~j  %_bit_rate%   ∕∕ the same:  if %%~j  350  (350 inclusive) 


 if LSS - Less Than                if [integer or int(var)]  < [integer or int(var)]
 if GTR - Greater Than             if [integer or int(var)]  > [integer or int(var)]
 if LEQ - Less Than or Equals      if [integer or int(var)]   [integer or int(var)]
 if GEQ - Greater Than or Equals   if [integer or int(var)]   [integer or int(var)]

13. The result of if is true or false, and will perform actions depending on the case, for didactic purposes, we will consider the current file as a true case:

 if %%~j GTR 349 (
      case true
      ffmpeg transcode file mp4
    ) else (
      case false
      skip this file .mp4
      save the full path name 
    )
  •  if %%~j gtr 349 2>&1 ("!_ffmpeg!" -y -i "%%~f#" -hide_banner -v error -stats -c:v libx265 -crf 22 ^
       -c:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio "%%~n#.mkv"
  • Obs.:2 The characters: space^, are at the end of the line, right at the line break, where in execution, the command interpreter will treat it as a single line, escaping the applied line break.

// Sorry, the explaining goes to continue tomorrow...

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .