Обладание большой видеотекой сегодня не редкость, и обычно в нее стараются собрать все в самом лучшем качестве. Однако другая сторона медали — несовместимость со старыми бытовыми проигрывателями, древними ноутбуками и прочими портативными гаджетами.
Я столкнулся с этим по банальной причине бытового комфорта. Имея неплохую медиастанцию под Windows, с подключенной 60" плазмой и шестиканальным звуком, настроенную с душой, с написанными скриптами для AutoHotkey и переписанным под собственный USB ИК приемник WinLIRC, я понял что не могу смотреть скачанные киношки каждый вечер, так как делать это хочется не вылезая из под одеяла в спальне. Где висит простенький 40" комбайн, купленный на распродаже, по случаю, в магазине одной из крупных торговых сетей. Возможностей бытового плеера, втроенного в LCD-телевизор, как оказалось, хватило на чтение флешек и SD карт любого размера, но вот в форматах он оказался ограничен AVI и кодеками MPEG-4-ASP и более слабыми.
Решение было простым — перегнать пару сотен несмотренных фильмов в AVI, а в дальнейшем автоматически создавать Lite-версию при окончании загрузки торрента rtorrent-ом на домашнем сервере. Таким образом решение должно было работать как под Windows так и под Linux, так как сервер работает под управлением Gentoo.
Однако технически осуществить все это с наскоку оказалось не так просто. Виндовые конвертеры с блекджеком и прочей мишурой отметались сразу, без попытки установки, а из остального остался только Avidemuх, имеющий как кросс-платформенную реализацию так и нормальный CLI интерфейс. Оставалось только автоматизировать процесс и дать технике заниматься тем, чем ей положено — работать.
Автоматизация создания скриптов Avidemux
На поверку CLI интерфейс Avidemux-а оказался довольно ущербным, но его спасает возможность записи конфигурации проекта в скрипт и последующее его выполнение опцией run:
avidemux2_cli --run script
Подглядеть формат скрипта довольно просто, для этого достаточно запустить GUI-версию Avidemux, сделать все необходимые настройки, а потом сохранить проект в файл. В этом файле хранятся все настройки кодеков и фильтров преобразования.
Собственно смену контейнера с помощью Avidemux можно делать без перекодирования видео, но в моем случае возникла необходимость изменять разрешение выходного файла и ограничивать его ширину 720 пикселями. При этом я не нашел иного способа установить выходное разрешение, кроме явного указания ширины и высоты в пикселях, поэтому идея использовать один скрипт для всех файлов отпала. Пришлось использовать MediaInfo, который также доступен для Linux, и получать из него размеры, а самое главное аспект, выводимого на экран изображения, и исходя из установленной ширины вычислять высоту. Дополнительно появилась идея слегка растягивать «широкоэкранные» фильмы по высоте, чтобы компенсировать визуальное сжатие висящего под небольшим углом телевизора.
Таким образом появился awk-скрипт (выбор awk обусловлен компактностью его реализации для windows, по сравнению, например с перлом). Скрипт ищет по маске *.mkv все файлы (строчку поиска легко изменить для любой платформы), затем для каждого найденного файла вызывает MediaInfo и создает скрипт для AviDemux, а также добавляет команду для вызова конвертера в пакетный файл. В дальнейшем этот скрипт легко переделывается для преобразования отдельного выбранного файла и подключению его менеджеру закачек.
Исходник скрипта
В приведенном исходнике удалены настройки выбранного видео-кодека, так как они занимают места больше чем весь скрипт и могут быть легко получены из проекта AviDemux. При этом следует экранировать все двойные кавычки символом обратной косой черты (в этом awk схож с си).
BEGIN {
# настройки
# ширина и высота дисплея, на котором будут проигрываться полученные файлы
WIDTH = 720
HEIGHT = 404
# следует ли образать края чтобы подогнать видео под размер экрана
CROP = 1
# макс. коэффициент растяжения для устранения/уменьшения ченых полос
STRETCH = 0.15
# имя командного файла, в который будут записаны команды для каждого видео
BATCH = "run.cmd"
# имена исполняемых файлов
AVIDEMUX = "P:\myProgs\AVIDemux.2.5.6\avidemux2_cli.exe"
MEDIAINFO = "P:\myProgs\MediaInfo_CLI_0.7.59\MediaInfo.exe"
# команда, формирующая список нужных файлов (dir, ls, find)
LIST = "dir /b *.mkv"
# конец настроек
ASPECT = WIDTH / HEIGHT
STRETCH += 1
while((LIST | getline fn) > 0) {
printf("FILE: %srn", fn);
InfoCommand = MEDIAINFO " --Output=Video;%Width%/%Height%/%AspectRatio%/%DisplayAspectRatio% ""fn"""
if((InfoCommand | getline tmp) > 0) {
if (match(tmp, /([0-9]+)/([0-9]+)/([0-9.]+)/([0-9.]+)/, m)) {
w = int(m[1])
h = int(m[2])
a = 0.0 + m[3]
d = 0.0 + m[4]
if (a != d) {
printf("tAspect/Display are not equal %d %drn", a, d);
}
# correct dimentions to square pixels
cw = w
ch = w / d
# stretch to fit display
if (cw / ch > ASPECT) {
sh = cw / d * STRETCH
printf("timage is wider than display, do vertical stretchrn")
if (sh > cw / ASPECT) {
sh = cw / ASPECT
}
sw = cw
# crop to fit display
if (CROP && (sw / sh > ASPECT)) {
printf("timage is still wider than display, do horisontal croprn")
sw = sh * ASPECT
}
fw = (sw < WIDTH) ? rint(sw, 8) : rint(WIDTH, 8)
fh = rint(sh / sw * fw, 4)
}
else if (cw / ch < ASPECT) {
sw = ch * d * STRETCH
printf("timage is higher than display, do horisontal stretchrn")
if (sw > ch * ASPECT) {
sw = ch * ASPECT
}
sh = ch
# crop
if (CROP && (sw / sh > ASPECT)) {
printf("timage is still higher than display, do horisontal croprn")
sh = sw / ASPECT
}
fh = (sh < HEIGHT) ? rint(sh, 4) : rint(HEIGHT, 4)
fw = rint(sw / sh * fh, 8)
}
rw = cw - sw
rh = ch - sh
printf("t%dx%d -stretch-crop-> %d(%+d)x%d(%+d) -display-fit-> %dx%drn", w, h, sw, -rw, sh, -rh, fw, fh);
# calc 4 crop values
if (rw < 0) rw = 0
if (rh < 0) rh = 0
rwl = int(rw / 2)
rwr = int(rw - rwl)
rht = int(rh / 2)
rhb = int(rh - rht)
# make new filenames
rsfn = fn
avifn = fn
sub(/.[A-Za-z0-9_]+$/, ".rs", rsfn)
if (rsfn == fn) rsfn = rsfn ".rs"
sub(/.[A-Za-z0-9_]+$/, ".avi", avifn)
if (avifn == fn) avifn = avifn ".avi"
# avidemux sript does not like "" in path
gsub(/\/, "/", fn)
gsub(/\/, "/", avifn)
# save run script
printf("//AD <- Needed to identify//rn")>rsfn
printf("var app = new Avidemux();rnapp.load("%s");rnapp.video.setPostProc(3,3,0);rnapp.video.addFilter("crop","left=%d","right=%d","top=%d","bottom=%d");rnapp.video.addFilter("resize","w=%d","h=%d","algo=0");rn", fn, rwl, rwr, rht, rhb, fw, fh)>rsfn
printf("app.video.codecPlugin("ЗДЕСЬ*ДОЛЖНЫ*БЫТЬ*НАСТРОЙКИ*КОДЕКА");rn")>rsfn
printf("app.audio.reset();rnapp.audio.codec("Lame",128,20,"80 00 00 00 00 00 00 00 02 00 00 00 05 00 00 00 00 00 00 00 ");rnapp.audio.normalizeMode=1;rnapp.audio.normalizeValue=0;rnapp.audio.delay=0;rnapp.audio.mixer="STEREO";rnapp.audio.drc=true;rnapp.setContainer("AVI");rn")>rsfn
printf("app.save("%s");rnapp.Exit();rn", avifn)>rsfn
# add to batch
printf("%s --nogui --run "%s"rn", AVIDEMUX, rsfn)>BATCH
}
}
close(InfoCommand)
}
close(LIST)
}
function rint(v, b) {
# floor value to specified base
return int(int(v + 0.5) / b) * b
}
Автор: ma5ter