Динамические водяные знаки на видео
Интересный момент произошел в развитии направления обработки и преобразования видео. Возникла задача по наложению не статической картинки водяных знаков, а полноценного видеоролика (например, титров или рекламы или динамического лого).
На входе у нас есть:
- начальный видеоролик
- последовательность кадров титров для наложения в виде серии PNG-кадров (можно использовать и видео, но возникают вопросы с альфа-каналом)
На выходе надо получить видеоролик с наложенной последовательностью кадров (для простоты в дальнейшем я буду называть эту последовательность маской, хотя это и не совсем терминологически правильно).
В общем и целом задача решается двумя очевидными методами:
1. плохим – разобрать исходное видео на кучу кадров, последовательно скрестить их с маской
2. хорошим – расковырять vhooks и сделать по подобию watermark соответствующий плагин, который смог добавлять маску в момент перекодирования.
Почему плох первый метод? Ответ на это вполне очевиден, стоит лишь провести серию экспериментов по решению данным путем. Представьте себе, что у вас видео 640 на 480 длится в течение часа (для простоты условимся на 24 кадрах в секунду, т.е. 86 400 кадров). Ffmpeg умеет раскладывать видео в последовательность JPG и PNG файлов. При отображении в PNG потребуется, при «весе» средней картинки в районе 0.5 МБайт, что-то 43.2 Гбайта места на жестком диске (не говоря о том факте, что файловой системе изрядно поплохеет ворочать объем в десятки тысяч файлов, но это мелочи). Т.е. расточительно. Если же проецировать в JPG, то дискового пространства потребуется меньше (от количества файлов мы не уходим, но все полегче), но при этом качество картинки ухудшится из-за сжатия с потерями. Это неприятно. Кроме этого, надо учитывать, что в обоих процессах, как разборки, так и сборки, операции ввода-вывода съедят сколь угодно мощную машину.
Несмотря на все неприятности этого метода, можно попробовать реализовать приведенную схему, дабы воочию убедится в порочности подхода.
Сначала сделаем серию кадров из видео простой командой:
1 | ffmpeg -i terminator_vp6.flv -f image2 src/i%09d.jpg |
Далее, на полученную последовательность наложим маску. Приведенный ниже пример на PHP использует ImageMagic для совмещения кадров:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #!/usr/bin/php5 <? $s = array(); $m = array(); $d = dir('src'); while (false !== ($e = $d->read())) { if($e != '.' && $e != '..') array_push($s, $e); } $d->close(); $d = dir('mask'); while (false !== ($e = $d->read())) { if($e != '.' && $e != '..') array_push($m, $e); } $d->close(); sort($s); sort($m); $sz = getimagesize('src/'.$s[0]); if($sz[0] > 0 && $sz[1] > 0) // маску необходимо привести к размеру ролика foreach($m as $mi) { system("convert mask/$mi -resize $sz[0]x$sz[1] cmask/$mi"); }; $i = 0; while($i < count($s) && count($s) > 0 && count($m) > 0) { for($j = 0; $j < count($m); $j++) { // собственно наложение system("composite -compose over cmask/".$m[$j]." src/".$s[$i]." result/".$s[$i]); $i++; if($i >= count($s)) break; } } ?> |
И, наконец, соберем полученные кадры в единый ролик. Надо отметить, что мои попытки собрать видео из PNG потерпели неудачу, что, скорее всего, моя вина, глубоко этот вопрос не изучал.
1 | ffmpeg -f image2 -i result/i%09d.jpg -f flv -b 128K -vcodec libx264 result.flv |
Разумеется, тут и без кунг-фу с двупроходным сжатием понятно, что данный способ решения задачи не самый оптимальный.
На основании вышеизложенных «страшностей» можно обратиться ко второму методу, а именно – к созданию плагина для ffmpeg, позволяющего вставлять маску на лету. Но тут оказалась другая неприятность – ffmpeg как постоянно развивающийся и совершенствующийся проект (для которого нормой использовать не готовы бинарные сборки, а собирать экспортированные из системы контроля версий исходники) потерял vhooks как класс вследствие того, что разработчики сочли это неудачным решением (а так оно и было). Беда в том, что после удаления vhooks в коде проекта не осталось ни одной альтернативы или схожего механизма, а наследник avfilters – находится достаточно в сыром состоянии и еще не включен в основное дерево. Получается, что единственно возможный метод решения на данный момент – написание полноценного приложения, что достаточно трудоемко, учитывая размер кода ffmpeg. К счастью, в рассылке пользователей обнаружилось письмо, приводящее решение вопросов с водяными знаками (которые попали под раздачу вместе с vhooks), на основании которого можно предположить, что имеющаяся функциональность avfilters низведет нашу задачу до статуса тривиальной и решаемой штатными средствами. Осталось немного подождать, пока avfilters не включат в основную ветку.


