Эффективное сжатие видеозаписей лекций

Oct 14, 2008 12:40

У наших студентов начался курс логики. Читает Карпович Валентин Никонович. Поскольку двое немного нездоровы, а один из студентов на начало курса вообще отсутствовал (видимо, его больше не будет) - решено было начать записывать лекции на видео. Благо, всё необходимое для этого (цифровая видеокамера и штатив) имеется.

Первую половину первой лекции я снимал практически с рук: постоянно "наезжал" и "отъезжал", брал разные планы и т.д. Затем нашёл довольно удачную точку съёмки в аудитории - откуда видно всю доску целиком и преподавателя (если, конечно, он двигается по аудитории в разумных пределах), и всю дальнейшую съёмку вёл с этой точки. Позднее вся запись лекции свелась к тому, что перед началом занятия я приходил и ставил камеру, а по окончании лекции забирал её.

Вопрос возник в том, что делать с записями. Камера со встроенным жёстким диском, пишет в MPEG-2 и имеет три настройки качества с битрейтом 3, 6 и 9 мбит/с. С минимальным качеством одна полуторачасовая лекция занимает около двух гигабайт, со средним - примерно 1 DVD-болванку. В курсе около 30 занятий. Т.е. при среднем качестве записи понадобится около 30 дисков, при минимальном - около 15. Вроде как не проблема в наше время, однако захотелось бОльшего.

Заниматься практически ручным перекодированием в "стандартный" divx/xvid + mp3 не хотелось - просто потому что большинство подобных перекодировщиков перед запуском, собственно, кодирования, требуют некоторого количества настроек. Даже если настройки сохранены в профиль - его всё равно нужно выбрать. А я не хотел делать с этими лекциями вручную ничего. Ну, разве что - вырезать пятиминутный перерыв в середине - да и то, если будет желание.

А обработать, хотя бы немного, стоило бы. Во-первых - занятие проходит вечером, в 17:30. Сейчас в это время на улице уже достаточно темно, поэтому проходит оно при свете. Микрофон на камере ненаправленный, и поэтому ловит практически все звуки. Особенно - гудение ламп дневного света. Во-вторых, ввиду ненаправленности микрофона общая громкость полезной речи сравнительно небольшая. А вот когда кто-то во время перерыва подходит к камере и начинает разговаривать прямо перед ней - всё значительно громче. В общем, хотелось бы избавиться от гула лам, а также сделать динамическую компрессию - типа как АРУ на старых магнитофонах.

Жизнь понемногу учит пользоваться потоковыми редакторами. sed-ом, например отлично делаются глобальные замены в большом тексте (фактически вся типографика). А для аудио я начал экспериментировать с sox. Почему? Потому что редактор командной строки очень удобен в плане того, что очень легко сохранить его в скрипт с нужными параметрами.

Как я уже говорил, камера пишет в формате MPEG-2. Звук при этом стерео, в формате AC3 с частотой 48000гц. Итак, сперва я пересэмплировал звук в привычные 44100гц, а также свёл его в моно (потому что микрофон на камере - всё равно моно). Делается это нехитрой командой:

mplayer film.MPG -af resample=44100:0:0 -ao pcm:fast:file=tmp.wav -vo null

Получаем нехилый wav под 500 мб. А дальше начинаются эксперименты с sox. Попытка "в лоб" вырезать 50Гц не увенчалась успехом. То есть частота-то наверное, вырезалась, но вот гул ламп это ничуть не уменьшило. Но мы же упорные :). Файл был открыт в audacity, там я выделил кусок, где не было ничего, кроме шума, и посмотрел его частотную "расчёску". Забавно, что 50Гц там вообще не оказалось. Зато был довольно чёткий спектр гармоник - 100, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900 и 1000Гц. Как оказалось, это было именно то, что нужно. Я добавил к каждой гармонике погрешность в 2 герца и записал вырезание этой "расчёски" в параметрах sox. Вуаля! Гул ламп пропал полностью! Причём в целом тембр звука совершенно не изменился. А дальше осталось сжать диапазон. Для этого в sox тоже есть определённая команда. В общем, все "хотелки" по поводу звука также были решены единственной командой:

sox tmp.wav -c 1 $tmp/tmp.wav bandreject 50 2 bandreject 100 2 bandreject 200 2 bandreject 250 2 bandreject 300 2 bandreject 350 2 bandreject 400 2 bandreject 500 2 bandreject 600 2 bandreject 700 2 bandreject 800 2 bandreject 900 2 bandreject 1000 2 compand 0.3,0.8 6:-70,-60,-20 -3 -90 0.2

Затем было решено избавиться от промежуточного wav-а - благо, в линухе для этого есть "системные" средства - очереди fifo. Для этого всего лишь нужно создать fifo вместо временного файла - ну и добавить & в конец вызова mplayer, чтобы он ушёл в фоновый режим и на переднем плане запустился sox.

Получился wav с нужными качествами. Что дальше? Снова ужать его в AC3 или mp3? Да нет уж. Я решил пойти по пути прогресса! mp3 - бойан! mp4 - рулез! Я решил пожать аудио и видео в mp4 и упаковать в контейнер mp4. Благо - все утилиты для работы с mp4 есть для работы с командной строки, а стал быть не требуют ручного дергания мышью и могут быть запущены в скрипте. Для кодирования аудио в mp4 (AAC) я использовал бесплатный кодировщик от Nero. Он есть для windows и для linux, причём совершенно одинаков для обеих платформ. Опять же - у контейнера mp4 не вызывает проблем переменный битрейт аудио (в отличие от mp3 в avi). А поскольку сжать звук хотелось как можно сильнее, я выбрал битрейт... 32кбита. Это может показаться слишком мало, однако контрольное кодирование и прослушивание показали вполне достойный результат. Не забываем, что кодируем мы обычную речь, а не студийную звукозапись :). К тому же цель кодирования - сохранить разборчивость этой самой речи. А на тембр, по сути. наплевать.

Можно было бы сжимать аудио тоже через fifo. В этом случае получилась бы красивая комбинация трёх команд, которая без всяких временных файлов сразу выдаёт нужный результат. Однако я решил при таком низком битрейте воспользоваться двухпроходным кодированием (в nero такой есть). А для этого нужен готовый wav-файл на диске. Поэтому один временный файл всё же остаётся.

Получилась законченная комбинация команд, которая была оформлена как функция в скрипте интерптератора bash. Она извлекает аудиодорожку из сырого видеофайла, перекодирует её в моно 44100, удаляет гул ламп и применяет динамическую компрессию, а затем сжимает в mp4 (AAC). Вот она (тут же привожу и начало файла скрипта):

#!/bin/bash

tmp=.

EncodeAudio()
{
# Сперва извлекаем аудио, меняем его частоту на 44100 и сохраняем в wav для дальнейшей обработки...
echo "Output audio to temporary wav..."
mkfifo fifo.wav
mplayer $1 -af resample=44100:0:0 -ao pcm:fast:file=fifo.wav -vo null &

# Удаляем полностью частоту 50Гц и гармоники 100, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900 и 1000Гц
# (это практически полностью убивает гул ламп дневного света, не влияя на тебмр остального звука в целом)
# Тут же применяем динамическую компрессию (для усиления слабых звуков без порчи громких)
echo "Processing audio - reducing 50Hz noise harmonics and dynamic compression"
sox fifo.wav -c 1 $tmp/tmp.wav bandreject 50 2 bandreject 100 2 bandreject 200 2 bandreject 250 2 bandreject 300 2 bandreject 350 2 bandreject 400 2 bandreject 500 2 bandreject 600 2 bandreject 700 2 bandreject 800 2 bandreject 900 2 bandreject 1000 2 compand 0.3,0.8 6:-70,-60,-20 -3 -90 0.2
rm fifo.wav

# Кодируем аудио. Битрейт 32кбс, моно, кодек AAC.
echo "Encoding audio to mono 32kbps AAC"
neroAacEnc -2pass -br 32000 -if $tmp/tmp.wav -of $tmp/tmp.m4a
rm $tmp/tmp.wav
echo "Audio processing is finished"
}

Осталось видео. Но тут вообще всё просто. Никаких очередей fifo, никаких обработок. Просто берём и кодируем. С каким битрейтом? Понятно, что с переменным. А с каким именно? И тут на сцену выходит параметр кодировщика "постоянное качество". В общем случае он даёт результат непредсказуемых размеров и потому как-то избегается. Однако характер лекции как таковой, по идее, должен обеспечивать достаточно маленький размер кадра. Статичная картинка с камеры на штативе, в которой сидит преподаватель - в общем-то, тоже практически неподвижно. Иногда он встаёт и чертит что-то на доске. Но в целом всё очень малоподвижно. Да, забыл сказать: всё это относится к бесплатному кодеру x264.

Я пользовался не отдельным кодером, а тем, что встроен в mencoder. Команда упаковки видео (опять же, одна-единственная) получилась вот такая:

mencoder video.MPG -nosound -vf softskip,scale=-2:384,crop=672:384:6:0,dsize=1.78 -ovc x264 -x264encopts crf=28:bframes=16:b-pyramid:direct=auto:filter=-2,-1:subme=1:analyse=none:me=dia:merange=12:threads=auto:no-psnr -of rawvideo -o $tmp/tmp.264

Параметр качества здесь - crf. Я экспериментировал с разными значениями от 27 до 30 и остановился на 28.

Ну и, собственно, заключительный этап - сведение аудио и видео вместе. Аудио мы закодировали раньше. Видео - только что. Команду сведения я решил оформить в одной функции с командой кодирования видео. Получилось вот, что:

EncodeMuxVideo()
{
logic=$1
crf=$2
echo $logic $crlf
if [ -z $crf ]; then
echo "using default crf=28"
crf=28
fi

# Кодируем видео в H.264 (кодируем с постоянным качеством, а не с битрейтом. Качество (crf) =28).
echo "Processing 1-pass video"
mencoder $1 -nosound -vf softskip,scale=-2:384,crop=672:384:6:0,dsize=1.78 -ovc x264 -x264encopts crf=$2:bframes=16:b-pyramid:direct=auto:filter=-2,-1:subme=1:analyse=none:me=dia:merange=12:threads=auto:no-psnr -of rawvideo -o $tmp/tmp.264
echo "Audio and video processed. Muxing..."
MP4Box -add "$tmp/tmp.264" -add "$tmp/tmp.m4a":lang=Russian -fps 25.0 -tmp "$tmp" -new $logic$crf.mp4
rm $tmp/tmp.264
}

Что осталось? Осталось последовательно вызвать сперва кодирование аудио, затем кодирование видео и сведение файла, и наконец, удалить временный файл с сырым видео. Последняя функция скрипта выглядит очень просто:

MkFile()
{
EncodeAudio $1
EncodeMuxVideo $1 $2
rm $tmp/tmp.m4a
}

и, наконец, последняя строчка скрипта. Где, собственно, всё и происходит:

MkFile logic_13_10.MPG 28

(logic_13_10.MPG это имя исходного файла).

Итак, как же сейчас происходит кодирование? По окончании записи лекции я тут же "сливаю" файлы в компьютер. На камере жёсткий диск конструктивно отформатирован в fat32, и поэтому видеозаписи автоматически делятся на файлы максимум по 2Гб. Я открываю первый файл в AviDemux и отвечаю утвердительно на его вопросы о том, нужно ли проиндексировать файл, и нужно ли "склеить" в один клип все обнаруженные куски. Затем в этой программе я удаляю пятиминутный перерыв в середине лекции и сохраняю всю лекцию целиком в один файл в формате MPEG-PS. Он получается размером 3,5 - 4 Гб. Наконец, я вписываю имя этого файла в конец скрипта (как параметр вызова MkFile) и запускаю скрипт. Обычно это происходит вечером, и я ухожу домой. А наутро получаю готовую сжатую лекцию.

Стоит ли овчинка выделки? Думаю, очень даже. Сжатие с постоянным качеством дало действительно непредсказуемый результат. Я видел, конечно, 2-часовые фильмы, сжатые на 700-мегабайтную болванку, но там уже, как правило, заметны артефакты сжатия. Но сжать полтора часа видео в... 80-100 мегабайт?.. Да-да, именно столько! При этом визуально качество картинки практически не отличимо от оригинала. Весь функционал записи сохраняется (а требуется, чтобы можно было разглядеть надписи на доске). Звук качественно лучше, чем в оригинале (за счёт того, что он громче, и нет постороннего гула). И что получаем? Весь курс лекций (30 штук) "с запасом" влезает на одну(!) однослойную DVD-болванку. Ну чем не архивный формат?

И - уверен - недалеко время, когда такие файлы (mp4) будут читаться "железными" плейерами. в конце концов это стандарт...
Previous post Next post
Up