Super User Asked by Metallizer on November 3, 2021
I use ffmpeg to encode some videos I downloaded from various streaming sites to hevc. In windows I use a batch file to convert these files.
FOR /F "tokens=*" %%G IN ('dir /b *.mp4') DO ffmpeg -n -i "%%G" -c:v libx265 -crf 22 -c:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio "%%~nG.mkv"
Some of these files have very low bitrate and I don’t want to touch those. Is there any way in ffmpeg to skip these files? Or any command I can include in the batch file, like using ffprobe
to get the bitrate and skip it using command.
@echo off
cd /d "%~dp0" && setlocal enabledelayedexpansion
set "_ffmpeg=F:2020-SUQ1569837ffmpegbinffmpeg.exe"
set "_ffprobe=F:2020-SUQ1569837ffmpegbinffprobe.exe"
for %%# in (*.mp4)do 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 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"
)else set/a "_c+=1+0" && <con: call set "_skp_!_c!=Skipped File: %%~nx# Duration: %%~i Bit Rate: %%~j"
echo;& (for /f tokens^=2^delims^=^= %%i in ('set _skp_ 2^>nul')do echo%%~i) & %__APPDIR__%timeout.exe -1 & endlocal
x265 [info]: HEVC encoder version 3.4+2-73ca1d7be377
x265 [info]: build info [Windows][GCC 9.3.1][64 bit] 8bit+10bit
x265 [info]: using cpu capabilities: MMX2 SSE2Fast LZCNT SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
x265 [info]: Main profile, Level-3.1 (Main tier)
x265 [info]: Thread pool created using 4 threads
x265 [info]: Slices : 1
x265 [info]: frame threads / pool features : 2 / wpp(12 rows)
x265 [info]: Coding QT: max CU size, min CU size : 64 / 8
x265 [info]: Residual QT: max TU size, max depth : 32 / 1 inter / 1 intra
x265 [info]: ME / range / subpel / merge : hex / 57 / 2 / 3
x265 [info]: Keyframe min / max / scenecut / bias : 23 / 250 / 40 / 5.00
x265 [info]: Lookahead / bframes / badapt : 20 / 4 / 2
x265 [info]: b-pyramid / weightp / weightb : 1 / 1 / 0
x265 [info]: References / ref-limit cu / depth : 3 / off / on
x265 [info]: AQ: mode / str / qg-size / cu-tree : 2 / 1.0 / 32 / 1
x265 [info]: Rate Control / qCompress : CRF-22.0 / 0.60
x265 [info]: tools: rd=3 psy-rd=2.00 early-skip rskip mode=1 signhide tmvp
x265 [info]: tools: b-intra strong-intra-smoothing lslices=4 deblock sao
frame= 1440 fps= 55 q=29.8 Lsize= 2570kB time=00:01:00.11 bitrate= 350.3kbits/s speed=2.32x
x265 [info]: frame I: 6, Avg QP:22.93 kb/s: 1138.86
x265 [info]: frame P: 705, Avg QP:25.55 kb/s: 498.87
x265 [info]: frame B: 729, Avg QP:28.95 kb/s: 98.52
x265 [info]: Weighted P-Frames: Y:0.9% UV:0.6%
x265 [info]: consecutive B-frames: 58.8% 11.0% 11.4% 6.6% 12.2%
encoded 1440 frames in 25.96s (55.47 fps), 298.86 kb/s, Avg QP:27.26
Skipped File: Live_TV_-_Bloomberg.mp4 Duration: 00:00:36.42 Bit Rate: 315
Skipped File: HVDC Light - ABB 3D.mp4 Duration: 00:03:32.16 Bit Rate: 336
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-SUQ1569837ffmpegbinffmpeg.exe"
set "_ffprobe=F:2020-SUQ1569837ffmpegbinffprobe.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 MediaMP4Convert
cd /d "D:MidiaMP4Convet"
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. Unfortunately your current for /f ... dir .mp4 ...
doesn't help much, so replace it with a simple for
to get all the .mp4
listed in a loop:
for %%# in (*.mp4)do ....
4. 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# ...
5. The ffprobe
command used in for /f
loop is:
..ffprobe.exe -show_entries stream=bit_rate "Google Chrome - Now Everywhere.mp4"
6. 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
7. 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
8. 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
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. 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
11. 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)]
12. 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.
13. For those files with lower bite rates, that is, false
cases in the if
command, you have the actions to save the files that were excluded from the ffmpeg
conversion, and will be listed at the end of the run:
if %%~j GTR 349 (
case true
ffmpeg transcode file mp4
) else (
case false
skip this file .mp4
save the full path name
)
Obs.:3 The if
also work in differents layouts like:
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 (case true && ffmpeg transcode file mp4 ) else (
case false && skip this file .mp4 && save the full path name )
if %%~j GTR 349 (case true && ffmpeg transcode file mp4
)else case false && skip this file .mp4 && save the full path name
14. Using the values in the %%~f#
, %%~i
and %%~j
variables,
where respectively are the path and full name of the current file, its duration and its bit rate, we can easily add a counter (set/a "_c+=1+0"
) and in execution time we increment to create/define, one by one the information of files excluded from the conversion:
)else set/a "_c+=1+0" && <con: call set "_skp_!_c!=Skipped File: %%~nx# Duration: %%~i Bit Rate: %%~j"
%%~f# == Live_TV_-_Bloomberg.mp4
%%~i == 00:00:36.42
%%~j == 315
set "_c+=1+0" && call set "_skp_1=Skipped File: Live_TV_-_Bloomberg.mp4 Duration: 00:00:36.42 Bit Rate: 315"
15. The set command can also be used for variable and value listings, and using a set user
, all variables with defined user+(strings)
will be listed like this:
>set USER
USERDOMAIN=LAME_SLUG
USERDOMAIN_ROAMINGPROFILE=LAME_SLUG
USERNAME=ecker
USERPROFILE=C:Usersecker
16. In the last line, where we have a for /f
loop, it will be used to echo each variable defined with name _skip_*
, which was defined by saving the files that were ignored during execution, and this looping will take everything that comes after the sign of =
(2nd/tokens^=2
):
for /f tokens^=2^delims^=^= ... set _skp_1 .... echo%%~i
_skp_1=Skipped File: Live_TV_-_Bloomberg.mp4 Duration: 00:00:36.42 Bit Rate: 315
↓
tokens^=2 ⇄ Skipped File: Live_TV_-_Bloomberg.mp4 Duration: 00:00:36.42 Bit Rate: 315
echo;& (for /f tokens^=2^delims^=^= %%i in ('set _skp_')do echo%%~i)...
17. The second and last part of the last in the bat file, will let the loop occur (isolated) and only after listing all the files that were skipped, it will pause / indefinite timeout, waiting for some key to be pressed, thus closing / finalizing the setlocal, and ending the execution:
1st part: (for /f .....)
2nd part: %__APPDIR__%timeout.exe -1 & endlocal
echo; & (for /f tokens^=2^delims^=^= %%i in ('set _skp_')do echo%%~i) & %__APPDIR__%timeout.exe -1 & endlocal
18. To avoid any possible error message (Environment variable _skip_ not defined
), in cases where no file was ignored by if
, just add 2^>nul
in 'set _skip_*2^>nul'
, inside the last loop for
:
(for /f tokens^=2^delims^=^= %%i in ('set _skp_ 2^>nul')do echo%%~i)do...
echo;
is only for create one separatist line before to list the skipped files..@echo off
cd /d "%~dp0"
setlocal enabledelayedexpansion
set "_ffmpeg=F:2020-SUQ1569837ffmpegbinffmpeg.exe"
set "_ffprobe=F:2020-SUQ1569837ffmpegbinffprobe.exe"
for %%# in (*.mp4) do (
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 (
if %%~j gtr 3200 (
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"
) else (
set /a "_c+=1+0"
set "_skp_!_c!=Skipped File: %%~nx# Duration: %%~i Bit Rate: %%~j"
)
)
)
echo.
for /f "tokens=2 delims==" %%i in ('2^>nul set _skp_')do echo%%~i
%__APPDIR__%timeout.exe -1
endlocal
Some further reading:
[√] if /?
[√] set /?
[√] CMD /?
[√] Findstr
[√] For Loop
[√] For /F Loop
[√] Conditional Execution || && ...
[√] Why does call set work differently
[√] Understanding start, 2>nul, cmd, and other symbols in a batch file
[√] Use parentheses/brackets to group expressions in a Windows batch file
Answered by Io-ol on November 3, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP