Похоже, что пакетный файл / гибридный сценарий JScript , написанный rojo , не предназначен для рекурсивного выполнения для всех файлов * .mkv и * .mp4 в дереве каталогов.По этой причине я полностью переписал командный файл и пропустил части скрипта JScript.
Похоже, что информация о высоте вывода видео на ffprobe
, поскольку опция "stream=codec_name,height"
здесь на самом деле не нужна, потому что каждое видео должнообрабатываться независимо от его высоты.По этой причине "stream=codec_name"
в строке определения ProbeOptions
должно быть достаточно для этой задачи, чтобы уменьшить вывод ffprobe
на одну строку.
Вывод JSON ffprobe
также может быть обработан в этом случае использованиянепосредственно с помощью FOR , используя в качестве разделителей запятую ,
, двоеточие :
, левую квадратную скобку [
, горизонтальную табуляцию TAB , правую квадратную скобку ]
, левый {
и правая скобка }
и нормальное пространство ПРОБЕЛ .Строки, начинающиеся с {
, могут полностью игнорироваться при обработке вывода в формате JSON.Чувствительное к регистру сравнение строк используется для определения, содержит ли строка значение codec_name
с интерпретацией первого значения кодера / декодера как видеокодека, а второго как аудиокодека.
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ProgramFolder=C:\ffmpeg-4.0.2-win64-static\bin"
set "ProbeOptions=-v quiet -show_entries "stream^^=codec_name" -of json"
set "MpegOptions=-hide_banner -fflags +genpts+discardcorrupt+fastseek -analyzeduration 100M -probesize 50M -hwaccel dxva2 -y -threads 3 -v error -stats"
set "FilesFound=0"
set "FilesEncoded=0"
for /F "delims=" %%I in ('dir *.mkv *.mp4 /A-D-H /B /S 2^>nul') do (
set "FullFileName=%%I"
set "TempFileName=%%~dpnI_new%%~xI"
set "AudioCodec="
set "AudioOption=ac3"
set "VideoCodec="
set "VideoOption=h264_nvenc"
set /A FilesFound+=1
for /F "eol={ tokens=1,2 delims=,:[ ]{} " %%B in ('""%ProgramFolder%\ffprobe.exe" %ProbeOptions% "%%I""') do (
if "%%~B" == "codec_name" (
if not defined VideoCodec (
set "VideoCodec=%%~C"
if "%%~C" == "h264" set "VideoOption=copy"
) else (
set "AudioCodec=%%~C"
if "%%~C" == "ac3" set "AudioOption=copy"
)
)
)
setlocal EnableDelayedExpansion
echo(
echo File: !FullFileName!
echo Video codec: !VideoCodec!
echo Audio codec: !AudioCodec!
if not "!VideoOption!" == "!AudioOption!" (
"%ProgramFolder%\ffmpeg.exe" %MpegOptions% -i "!FullFileName!" -c:v !VideoOption! -c:a !AudioOption! "!TempFileName!"
if not errorlevel 1 (
move /Y "!TempFileName!" "!FullFileName!"
if not errorlevel 1 set /A FilesEncoded+=1
)
if exist "!TempFileName!" del "!TempFileName!"
)
endlocal
)
if %FilesFound% == 1 (set "PluralS=") else set "PluralS=s"
echo(
echo Re-encoded %FilesEncoded% of %FilesFound% video file%PluralS%.
endlocal
pause
Внимание: Пробел между [
и ]
должен быть в пакетном файле символом табуляции!
Пакетный файл сначала устанавливает локальную среду с включенными расширениями команд, необходимыми для этого пакетного файла, и отключается с задержкой.Расширение переменных среды для возможности корректной обработки также файлов с одним или несколькими восклицательными знаками в имени файла или пути к файлу.
Далее некоторые переменные среды определены для использования позже в сценарии.Что-то особенное - это определение переменной ProbeOptions
из-за строки аргумента "stream=codec_name"
, которая позже должна быть передана в отдельный командный процесс, запускаемый FOR , требующий двойного экранирования знака равенства с двумя ^
, чтобы в итоге получить=
передано ffprobe.exe
.
Внешний FOR выполняется один раз в отдельном командном процессе, запущенном с cmd.exe /C
в фоновом режиме командной строки:
dir *.mkv *.mp4 /A-D-H /B /S 2>nul
DIR выводит для обработки STDOUT этого командного процесса
- только имена файлов из-за
/B
- не скрытыхфайлы из-за
/A-D-H
(атрибут не каталог и не скрытый) - , соответствующий либо шаблону подстановки
*.mkv
, либо *.mp4
- в текущем каталоге и всех его подкаталогах из-за
/S
- с полным путем также из-за
/S
.
Возможно, не найдено подходящего имени файла, что приводит к выводу сообщения об ошибке DIR для обработки STDERR .Это сообщение об ошибке подавляется путем перенаправления его на устройство NUL .
. Прочтите статью Microsoft о Использование операторов перенаправления команд для объяснения 2>nul
.Оператор перенаправления >
должен быть экранирован с помощью символа вставки ^
в командной строке FOR , чтобы интерпретироваться как литеральный символ, когда интерпретатор команд Windows обрабатывает эту командную строку перед выполнением команды FOR , котораявыполняет встроенную командную строку dir
в отдельном командном процессе, запущенном в фоновом режиме.
FOR захватывает все строки, выводимые в STDOUT фонового командного процесса, и обрабатывает их посленачало cmd.exe
прекращено.Таким образом, FOR обрабатывает список полных имен файлов, не изменяющихся при выполнении цикла.
На дисках с NTFS также было бы безопасно использовать:
for /R %%I in (*.mkv *.mp4) do (
Это также приводит к обработке всех не скрытых файлов * .mkv и * .mp4 в текущем каталоге и всех подкаталогах.NTFS возвращает список файлов, отсортированных по алфавиту.Но такой подход проблематичен на дисках FAT32 и ExFAT, поскольку код, выполняемый на каждой итерации цикла, может привести к обновлению таблицы размещения файлов.FAT32 и ExFAT возвращают имена файлов, соответствующие определенным критериям, просто так, как они хранятся в настоящее время в таблице размещения файлов, в которой последний измененный файл в каталоге всегда находится внизу таблицы каталога.Это означает, что список имен файлов может измениться, пока цикл выполняется в первом, втором, третьем, ... имени файла, возвращаемом файловыми системами FAT32 и ExFAT.Это может привести к обработке видеофайла более одного раза и пропуску других.Поэтому лучше обработать список имен файлов, который полностью загружается в память перед началом итерации цикла.
FOR с параметром /F
по умолчанию пропускает пустые строки, не выводимые DIR в этом случае и строки, начинающиеся с точки с запятой, что также здесь невозможно, поскольку каждая строка начинается с буквы диска C
.Но FOR будет разбивать каждую захваченную строку на подстроки (токены) с использованием обычного пробела и горизонтальной табуляции в качестве разделителей строк и назначать только первую строку, разделенную пробелом / табуляцией, указанной переменной цикла I
.Такое поведение здесь не требуется, так как всегда необходимо полное имя файла, даже если он содержит один или несколько пробелов.По этой причине delims=
используется для определения пустого списка разделителей строк, что приводит к полному отключению режима разделения строк и присваивается переменной цикла I
всегда имя файла найденного файла * .mkv или * .mp4 спуть, имя и расширение.
На каждой итерации цикла происходит следующее:
- Полное квалифицированное имя текущего * .mkv или * .mp4 файла присваивается переменной среды
FullFileName
. - Полное имя текущего файла * .mkv или * .mp4 с добавлением
_new
слева от расширения файла присваивается переменной среды TempFileName
. - Переменная среды
AudioCodec
удаляется, если существует из предыдущей итерации цикла. - Переменная среды
AudioOption
определяется со строковым значением ac3
, являющимся требуемым аудиокодеком. - Переменная среды
VideoCodec
удаляется, если существует из предыдущей итерации цикла. - Переменная окружения
VideoOption
определяется со строковым значением h264_nvenc
требуемый видеокодек. - Переменная среды
FilesFound
увеличивается на единицу с помощью простого арифметического выражения, оцениваемого командой SET .
Тогдаеще один FOR используется для запуска командной строки ffprobe
с cmd.exe /C
в фоновом режиме.В этом особом случае необходимо заключить всю командную строку в двойные кавычки из-за строки аргумента "stream=codec_name"
для правильной передачи всей командной строки дополнительному командному процессу, запущенному FOR .
Внутренний FOR захватывает вывод, записанный ffprobe
в формате JSON для обработки STDOUT запущенного командного процесса, и обрабатывает этот вывод построчно.Интерес представляют только строки, содержащие "codec_name"
.Поэтому опция eol={
используется для полного игнорирования всех строк, начиная с {
.Опция tokens=1,2
приводит к получению первой подстроки, назначенной указанной переменной цикла B
, и второй подстроки следующей переменной цикла C
в соответствии с ASCII-таблицей .Список разделителей, заданный параметром delims=
, приводит к получению более или менее простого имени свойства, заключенного в двойные кавычки, например "codec_name"
, и его значения также заключенного в двойные кавычки, такие как "h264"
, назначенные переменным цикла B
и * 1186.*.
Если строка, назначенная переменной цикла B
без двойных кавычек, явно заключенных в двойные кавычки, чувствительна к регистру и равна строке "codec_name"
, то эта строка представляет реальный интерес. Значение кодека, присвоенное переменной цикла C
, присваивается без двойных кавычек любой переменной окружения VideoCodec
или AudioCodec
в зависимости от того, какой видеокодек уже найден в выводе JSON в одной из обработанных строк ранее. Кроме того, опция видео или аудио, используемая, возможно, позже, устанавливается на copy
, если видео или аудио кодек уже является требуемым кодеком h264
соответственно ac3
.
Необходимо включить отложенное расширение переменных среды после обработки вывода ffprobe
, чтобы иметь возможность обрабатывать значения переменных среды, определенные ранее в одном и том же командном блоке. Прочитайте этот ответ для получения подробной информации о командах SETLOCAL и ENDLOCAL .
Сначала выводится пустая строка с echo(
и следующим полным именем файла текущего видеофайла и его текущего видео- и аудиокодека.
Условие IF сравнивает чувствительные к регистру параметры видео и аудио. Две строки параметров идентичны, только если текущий видеофайл уже закодирован в формате h264 / ac3, и в этом случае обе переменные среды имеют значение copy
. Таким образом, если две сравниваемые строки не идентичны, видеофайлы необходимо перекодировать с помощью ffmpeg
, чтобы изменить видеокодек или аудиокодек или оба кодека.
Перекодирование видеофайла прошло успешно при ffmpeg
выходе с кодом выхода не больше или равно 1
, т. Е. Со значением 0
. В этом случае временный видеофайл, созданный ffmpeg
, перемещается поверх текущего видеофайла с перезаписью существующего видеофайла, если текущий видеофайл не защищен от записи атрибутом только для чтения или разрешениями NTFS.
Эти действия приводят к обновлению таблицы размещения файлов на дисках FAT32 и ExFAT, что является причиной того, что внешний FOR выполняет DIR для получения списка имен видеофайлов в память перед итерациями цикла .
Переменная среды FilesEncoded
увеличивается на один из исходных видеофайлов, может быть действительно успешно заменена перекодированной версией.
Временный видеофайл, созданный ffmpeg
на существующем вообще после выполнения ffmpeg.exe
, окончательно удаляется в случае любой ошибки, приводящей к тому, что этот файл все еще существует после других командных строк.
Наконец, после обработки всех не скрытых файлов * .mkv и * .mp4 выводится сводная информация с использованием двух переменных среды счетчика, и исходная среда восстанавливается перед остановкой выполнения пакетного файла, чтобы можно было увидеть все выходные данные. запустив пакетный файл с двойным щелчком по нему.
Чтобы понять используемые команды и то, как они работают, откройте окно командной строки, выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.
del /?
dir /?
echo /?
endlocal /?
for /?
if /?
move /?
pause /?
set /?
setlocal /?