четверг, 21 апреля 2011 г.

ffmpeg, самописный скрипт и "трубы".

Начну с того, что люди не использующие консоль и не пишущие хотя бы мелкие скрипты для своих нужд, могут дальше не читать и смело проходить мимо этой заметки, ибо ничего интересного для себя они тут не найдут. ;) Для остальных же преамбула...
Начать надо наверное с того, что в свое время я написал небольшой скриптик, который копировал  просмотренные мной ролики с ютюба мне на диск (к стати скрипт уже переписан, ибо старый вариант перестал работать с выходом нового флеша).

Все бы хорошо, но вот ролики в формате flv очень не любит мой домашний медиацентр в виде компа с установленным на нем XBMC. За то он с удовольствием проглатывает "матрешку", контейнер, который я тоже очень люблю за его удобство и универсальность (в отличии от того же убогого avi). ;) Ну что за проблема перепаковать из одного контейнера в другой старому прожженому любителю ком-строки?! Сказано - сделано! Пишем небольшой скрипт, который из текущей папки переделывает все flv в mkv, делов то! Благо есть чудо инструмент в виде ffmpeg.

find . -type f -name \*.flv -print | while read I

А дальше каждый файл с именем ${I} обрабатываем вышеуказанным ffmpeg. И все бы ничего, да вот только не работает цикл! :( Читает первую строку, обрабатывает и вываливается. Честно говоря решить эту головоломку я не смог. И поэтому обратился к "общественному сознанию" в виде гуглового buzz-а, где мы в несколько голов и решили проблему. :) Не буду пересказывать все исследования, скажу только результаты: оказалось, что при перекодировании ffmpeg (точнее его сборка под ubuntu) читает stdin! И соответственно "выгребает" все из трубы. Ну и конечно же цикл благословенно завершается. Вот такие вот мелкие, но поскудные грабли.

Ну а вот готовый рабочий вариант скрипта с небольшими "рюшечками" в виде zenity:

#!/bin/bash

QUANT=`find . -type f -name \*.flv -print | wc -l`
STEP=`echo 100/$QUANT | bc -l`
INIT=0

(
find . -type f -name \*.flv -print | while read I
do
        NAME=$I
        ffmpeg -i "${NAME}" -vcodec copy -acodec copy "${NAME}.mkv" >> /dev/null 2>&1 > /dev/null && rename "s/\.flv//" "${NAME}.mkv"
        INIT=`echo $INIT+$STEP | bc -l`
        echo $INIT
done
) | zenity --progress --text="Идет обработка видео"

Собственно конструкция "> /dev/null" и позволяет решить проблему с чтением stdin.
Так же была предложена еще одна конструкция:

(ffmpeg -i "${NAME}" -vcodec copy -acodec copy "${NAME}.mkv" >> log 2>&1) &
wait $! 

При таком подходе ffmpeg уходит в бакграунд и тоже не мешает нормальной работе pipe-а, а wait позволяет дождаться окончания выполнения перекодирования.

Надо сказать, что проблема проявляется на ffmpeg версии 0.6. Собраный из git свежий ffmpeg не проявлял излишней рьяности в плане чтения всего и вся из stdin. :)

В общем если кто-то наступит на грабли, в виде не работающего цикла с "пайпом" - имейте в виду: видимо какая-то сволочь читает стандартный ввод! ;)

P.S. За помощь в "разборе полетов" хочу сказать спасибо Юрке и Ромке, принявшим активное участие в нелегком деле поиска граблей. :)