Utilizo ffmpeg para codificar algunos videos que descargué de varios sitios de transmisión en hevc. En Windows utilizo un archivo por lotes para convertir estos archivos.
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"
Algunos de estos archivos tienen una tasa de bits muy baja y no quiero tocarlos. ¿Hay alguna forma en ffmpeg de omitir estos archivos? O cualquier comando que pueda incluir en el archivo por lotes, como usarlo ffprobe
para obtener la tasa de bits y omitirlo usando el comando.
Respuesta1
@echo off
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"
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
- Producción:
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.: 1Haydos espaciosentre^=^,^⟵⟶%%ien: delims^=^,^spacespace%%i
for %%# in (*.mp4)do for /f tokens^=2^,6^delims^=^,^spacespace%%i in (...
1.- Tu trabajo en casa: Reemplaza las variables a continuación de una manera compatible con tu escenario, también ve a tu carpeta bat:
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.Este lote utiliza múltiples for loop
, para que funcione deberá habilitarlo Deleyed Expansion
para que las variables reciban los valores actualizados/expandidos en tiempo de ejecución:
Setlocal EnableDelayedExpansion
3.Desafortunadamente tu actualfor /f ... dir .mp4 ...
no ayuda mucho, así que reemplácelo con un simple for
para obtener todos los.mp4
listado en un bucle:
for %%# in (*.mp4)do ....
4.Utilice un adicional para hacer uso de esta variable de bucle (en ) donde obtuvo la ruta/nombre completo ( ) del archivo mp4, y pase este bucle como entrada a la definición que ya está definida (explicada enfor /f
1st/for/var==%%#
%%~f#
ffprobe
artículo 5.), los tokens y delimitadores que se tomarán en ese comando.
for /f tokens^=2^,6^delims^=^,^ %%i in (ffmprobe ... %%~f# ...
5.Elffprobe
El comando utilizado en el bucle es:for /f
..\ffprobe.exe -show_entries stream=bit_rate "Google Chrome - Now Everywhere.mp4"
6.Comenzando con la redirección StdErr
a StdOut
la ffprobe
salida a filtrar findstr
usando el interruptor con el/End of a line
regex
números ([0-9]
) concatenado con la cadena .kb/s
y usando el escape adecuado enfor
bucle:
2^>^&1 "!_ffprobe!" -show_entries stream^=bit_rate "%%~f#" ^| "%__APPDIR__%findstr.exe" /e [0-9].kb/s
7.El comando expandido anterior y sin los escapes da como resultado:
2>&1 ..\ffprobe.exe -show_entries stream=bit_rate "Google Chrome - Now Everywhere.mkv" | "%__APPDIR__%findstr.exe" /e [0-9].kb/s
8.La salida del comando anterior tratada por el findstr
filtro da como resultado:
Duración: 00:01:00.08, inicio: -0.007000, tasa de bits: 350 kb/s
9.La salida del comando anterior tratada por el findstr
filtro da como resultado:
Duración: 00:01:00.08, inicio: -0.007000, tasa de bits: 350 kb/s
10.Al utilizar múltiples delimitadores, las cadenas en%%i
y%%j
la salida será00:01:00.08
y350
: es para la salida del último comando, será00:01:00.08
y350
:
... for /f tokens^=2^,6^delims^=^,^space %%i in (...
Duration: 00:01:00.08, start: -0.007000, bitrate: 350 kb/s
11.Suponiendo que su valor límite es350
(inclusivo) para Bite Rate, necesitarás usar alguna if
opción en la parte de trabajo:
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.El resultado deif
estrue
ofalse
, y realizará acciones según el caso, a efectos didácticos, consideraremos el expediente actual como untrue
caso:
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.:2Los caracteres:space^, están al final de la línea, justo en el salto de línea, donde durante la ejecución, el intérprete de comandos lo tratará como una sola línea, escapando del salto de línea aplicado.
13.Para aquellos archivos con tasas de mordida más bajas, es decir,false
casos en elif
comando, tiene las acciones para guardar los archivos que fueron excluidos delffmpeg
conversión, y aparecerá al final de la ejecución:
if %%~j GTR 349 (
case true
ffmpeg transcode file mp4
) else (
case false
skip this file .mp4
save the full path name
)
Obs.:3El
if
También funciona en diferentes diseños como: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.Usando los valores en el%%~f#
,%%~i
y%%~j
variables, donde respectivamente son la ruta y el nombre completo del archivo actual, su duración y su tasa de bits, podemos agregar fácilmente un contador ( set/a "_c+=1+0"
) y en el tiempo de ejecución incrementamos para crear/definir, uno por uno, la información de los archivos excluidos de la conversión:
)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.El comando set también se puede usar para listados de variables y valores, y usar unset user
, todas las variables con definiciónuser+(strings)
se enumerará así:
>set USER
USERDOMAIN=LAME_SLUG
USERDOMAIN_ROAMINGPROFILE=LAME_SLUG
USERNAME=ecker
USERPROFILE=C:\Users\ecker
dieciséis.En la última línea, donde tenemos unfor /f
bucle, se utilizará para hacer eco de cada variable definida con nombre_skip_*
, que se definió guardando los archivos que fueron ignorados durante la ejecución, y este bucle tomará todo lo que venga después del signo de=
(2do/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.La segunda y última parte de la última en el archivo bat, permitirá que el bucle ocurra (aislado) y solo después de enumerar todos los archivos que se omitieron, se pausará/tiempo de espera indefinido, esperando que se presione alguna tecla, cerrando así/ finalizando el setlocal y finalizando la ejecución:
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.Para evitar cualquier posible mensaje de error (Environment variable _skip_ not defined
), en los casos en los que ningun archivo fue ignorado por if
, simplemente agregue2^>nul
en'set _skip_*2^>nul'
, dentro del último buclefor
:
(for /f tokens^=2^delims^=^= %%i in ('set _skp_ 2^>nul')do echo\%%~i)do...
- Obs.4Aquí el
echo;
es solo para crear una línea separatista antes de enumerar los archivos omitidos.
- El mismo código en formato convencional/didáctico:
@echo off
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"
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
Algunas lecturas adicionales:
[√]si /?
[√]colocar /?
[√]CMD /?
[√]Buscartr
[√]En bucle
[√]Ejecución condicional || &&...
[√]¿Por qué el conjunto de llamadas funciona de manera diferente?
[√]Comprensión de inicio, 2>nul, cmd y otros símbolos en un archivo por lotes
[√]Utilice paréntesis/corchetes para agrupar expresiones en un archivo por lotes de Windows