3 июня 2009 г. ffmpeg Flash flowplayer FLV Nginx PHP yamdi Сервисы FreeBSD

Видеосервис своими руками и организация псевдостримминга

videoservice

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

Для всего этого нам понадобиться:

  • Флеш-плеер для проигрывания видео-файлов - flowplayer
  • Программа для конвертирования видео-файлов - ffmpeg
  • Расширение PHP для получения превью-изображения - php-ffmpeg и библиотека для работы с графикой - GD
  • Эмуляция потокового видео (псевдостримминг) с помощью утилиты yamdi, модуля для nginx - http_flv_module и плагина для flowplayer - flowplayer.pseudostreamming

scheme-medium

Серверная ОСь по традиции - FreeBSD.

Установка необходимых нам утилит

ffmpeg

Для того чтобы конвертировать видео, нам необходимо установить утилиту ffmpeg. Есть еще аналогичная утилита - mencoder, тоже очень хорошая, но ffmpeg более популярен, поэтому именно его буду сейчас использовать. Эти утилиты с переменным успехом конкурируют между собой, поэтому также рекомендую посмотреть на mencoder в свободное время.

# cd /usr/ports/multimedia/ffmpeg
# make config

и выбираем LAME (lame MP3 codec)

# make install
Windows сборки ffmpeg: http://ffmpeg.arrozcru.org/builds/

yamdi

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

# cd /usr/ports/multimedia/yamdi
# make install

nginx

Теперь подошло время для nginx, который будет отдавать индексируемый (с помощью yamdi) видео-файл. На его месте может быть и lighttpd или еще какой-нибудь другой веб-сервер. Но я поддержу отечественного производителя! :)

# cd /usr/ports/www/nginx
# make config

и выбираем HTTP_FLV_MODULE, далее

# make install

Ну или просто при установке укажите опцию "--with-http_flv_module".

nginx-0.7.52. Теперь и под Windows: http://rmcreative.ru/blog/post/nginx-0.7.52-teper-i-pod-windows
Можно конечно использовать в качестве бекенда - PHP, например с помощью такого кода:
$start = (int) @$_GET['position'];
if ($start < 0) die("You fucking idiot");
// open file for reading
$fp = fopen($file, 'r+');
$fsize = filesize($file);
if ($start > 0)
{
    // seek to requested position
    fseek($fp, $start);
    // FLV header for the movie part. Magic. Just trust me.
    // Header code is completely taken from flv4php project
    $header = "FLV" . pack('C', 1 ) . pack('C', 5 ) . pack('N', 9 ) . pack('N', 9 );
    header("Content-Length: " . (strlen($header) + $fsize - $start));
    echo $header;
} else {
    header("Content-Length: " . $fsize);
}
set_time_limit(0);
while(!feof($fp)) {
    print(fread($fp, 1024));
    usleep(1000);// limit download speed
}
fclose($fp);

Но мне удобнее использовать для этого модуль nginx'а, да и быстрее будет.

php5-ffmpeg

Это расширение PHP нам понадобиться для того чтобы получать информацию о видео-файле, а также для получения видео-фрейма для превью. Также необходимо установить библиотеку GD и сам PHP в частности.

# cd /usr/ports/graphics/php5-ffmpeg
# make install
php5-ffmpeg для Windows: http://pyha.ru/forum/attachment/1256/1317.0 и http://pyha.ru/forum/attachment/1257/1317.0
Можно конечно получать информацию путем обращения к
# ffmpeg -i /path/filename.flv

и делать превью
# ffmpeg -i /path/filename.flv  -an -ss ВРЕМЯ_ПОЗИЦИИ_В_СЕКУНДАХ -r 1 -vframes 1 -s 320x240 -y -f mjpeg /path/filename.jpg

Но мне нравиться использовать расширение, это удобнее.

И перегрузите apache или nginx, смотря как вы используете PHP.

Конвертирование видео и получение изображения для превью

Для начала нам надо указать путь до исходного и результирующего файлов, прописать пути до превью-файлов, а также указать минимальную позицию фрейма для создания превью.

// Путь до залитого видео-файла, например временный файл полученный по $_FILES['tmp_file']
$inputPath = $_FILES['tmp_file'];

//Путь до результирующего файла, то есть отконвертированный и прошитый мета-данными видео-файл
$outputPath = "/path/video/filename.flv";

// Указываем минимальную позицию фрейма для захвата превью
$previewFrameMin = '3'; 

// Путь до превью-файла
$previewPath = "/path/preview/filename.jpg";

// Путь до мини-превью
$previewMiniPath = "/path/preview/filename_mini.jpg";

// Массив для сбора ошибок
$errorInfo = array();

Конвертируем исходный файл во временный, в формате FLV и разрешением 320x240, использую в качестве аудио-кодека mp3lame с частотой 44100, остальные настройки по умолчанию. Временный файл нужен для того, чтобы yamdi смог вшить мета-данные, так как он не может вшить в исходный файл.

/* 
 * Конвертируем FLV во временный файл ($outputPath . '_temp').
 */
passthru(
    '/usr/local/bin/ffmpeg -i ' . escapeshellarg($inputPath) .
        ' -f flv '.
        ' -s 320x240 '.
        ' -acodec libmp3lame '.
        ' -ar 44100 '.
        escapeshellarg($outputPath . '_temp'),
    $errorInfo['ffmpeg']
);

Прошиваем в видео-файл мета-данные для индексирования. Утилита yamdi написана на Си и работает очень быстро! А также удаляем временный файл.

/*
 * Прошиваем metadata (позиции ключевых фреймов) для перемотки
 */
passthru(
    '/usr/local/bin/yamdi -i ' . escapeshellarg($outputPath . '_temp') . 
        ' -o ' . escapeshellarg($outputPath),
    $errorInfo['yamdi']);

/*
 * Удаление временного файла, он нам уже не нужен...
 */
unlink($outputPath . '_temp');

Теперь получаем информацию о видео-файле и получаем объект фрейма для превью.

/*
 * С помощью расширения php5-ffmpeg получаем информацию о видео-файле
 */
$movie = new ffmpeg_movie($outputPath);

/*
 * Вычисляем время воспроизведения видеофайла
 */
$duration = $movie->getDuration();

/*
 * Вычисляем количество кадров
 */
$frameCount = $movie->getFrameCount();

/*
 * Вычисляем позицию фрейма для его захвата
 */
$framePosition = ($frameCount > $previewFrameMin) ? $frameCount / 2 : $previewFrameMin;

/*
 * Получаем объект фрейма для превью
 */
$preview = $movie->getFrame($framePosition);

А теперь получим и сохраним наши превьюшки.

/*
 * Записываем в файл - Превью (320x240)
 */
imagejpeg ($preview->toGDImage(), $previewPath);

/*
 * Записываем в файл - Мини-превью (160x120)
 */

$previewMini = imagecreatetruecolor(160, 120);

$r = imagecopyresized(
    $previewMini,
    $preview->toGDImage(),
    0, 0, 0, 0,
    160, 120,
    320, 240
);

imagejpeg($previewMini, $previewMiniPath);

Все, файл отконвертирован в FLV и прошит мета-данными, также созданы превью, теперь займемся выводом видео-файла - клиенту.

Плеер и показ видео

Почему flowplayer?

Сначала нам понадобится флеш-плеер, умеющий проигрывать видео в формате FLV и показывать картинки в формате JPEG (для превью).

Из всех аналогов (смотрите ниже, в "Полезные ссылки -> Видео-плееры") мне больше всего понравился flowplayer, он:

  • Кроссбраузерный и очень гибкий, и удобный в настройке
  • Бесплатный для некоммерческого использования, а коммерческая версия стоит его возможностей!
  • Имеет Flash и Javascript API, также поддерживает библиотеку JQuery (там доп. фишки какие-то)
  • Имеет кучу полезных плагинов
  • Умеет работать со стриммингом и псевдостриммингом
  • Легко поменять шаблоны оформления и языковые шаблоны для настройки локализованного интерфейса
  • Обширная документация, имеет достаточно большое сообщество в собственном форуме, а также оказывает бесплатную поддержку для коммерческой версии
  • Поддерживает плейлисты и имеет полноэкранный режим
  • Поддерживаемые форматы: FLV, SWF, MP3, MP4, H.264 video, JPG, PNG
  • Сырцы открыты!

flowplayer

Остальное описание возможностей на официальном сайте: http://flowplayer.org/

Настройка flowplayer

С плеером мы определились, теперь надо его настроить для показа видео. В дистрибутив плеера входит:

  • flowplayer-3.1.1.swf - сам плеер
  • flowplayer.controls-3.1.1.swf - flash-controls
  • flowplayer-3.1.1.min.js - это так называемый js-api

Для реализации псевдо-стримминга нам не хватает flowplayer.pseudostreaming-3.1.2.swf, который можно скачать и ознакомиться с использованием тут: http://flowplayer.org/plugins/streaming/pseudostreaming.html.

Для работы со всеми видами стриминга есть титуриал: http://flowplayer.org/documentation/configuration/streaming.html

Итак, приступим:

<!-- Подключаем js-api -->
<script type="text/javascript" src="/swf/flowplayer-3.1.1.min.js"></script>

<!-- Настраиваем плеер -->
<script type="text/javascript">
    flowplayer(
        "player",
        "/swf/flowplayer-3.1.1.swf",
        { 
            playlist: [
                {
                    url: 'http://example.com/preview/filename.jpg' 
                }, 
                { 
                    url: 'http://example.com/video/filename.flv',  
                    autoPlay: false,
                    autoBuffering: false,
                    provider: 'pseudo'
                }
            ],
            plugins: {
                pseudo: { 
                    url: '/swf/flowplayer.pseudostreaming-3.1.2.swf' 
                }
            }
        }
    );
</script>

<!-- DIV в который мы поместим плеер --> 
<div style="width:416px;height:312px;display:block;padding:10px;" id="player"></div> 

Все опции указанные в настройках можно посмотреть тут: http://flowplayer.org/documentation/configuration/clips.html

Настройка http_flv_module

Теперь настроим nginx на отдачу файла со смещением, для эмуляции стримминга.

location ~ \.flv$ {
                flv;
                root /path/video;
        }

То есть, говорим nginx'у чтобы он для расширения "flv" использовал модуль http_flv_module и указываем в root путь до видео-файлов. Документация по http_flv_module: Директивы модуля ngx_http_flv_module

Ну вот и все, теперь у вас полноценный "видеосервис"!

Полезные ссылки

ffmpeg
Работа с видео на php с помощью php ffmpeg
Продвинутый метод воспроизведения flash-видео на веб-страницах (или эмулируем стрим-видео)
Наносим водяной знак на видео с помощью ffmpeg
Как же работают видеохостинги? Часть первая. Сборка.
Как же работают видеохостинги? Часть вторая. Ffmpeg.

Видео-плееры
Подборка видео-проигрывателей на флэше
Видео и аудио плееры для сайтов
Идеальный видео-плеер
Идеальный видеоплеер. Не утопия

Комментарии

неплохой обзорчег. а как же мультидоменность, прогрессбар при загрузке и прочие вкусности?)))))))))))))

Мультидоменность обеспечивает коммерческая версия flowplayer :)

отлично, молодец!

пхп бекэнд это нечто))) работать будет только столько потоков вещания одновременно сколько воркеров в нгинкс-конфиге. про скорость я вообще молчу) так что не вариант)

Спасибо, хороший обзор!

сколько воркеров в нгинкс-конфиге
или в апаче :)

Это кто-то реализовал уже??

Дима, что именно?

Неплохо.а эти бибиотеки позволяют работатьь с mp3 файлами ?
выдирать кусочки, менять битрейт и т.д.?

Сергей, ffmpeg умеет работать с mp3 и flowplayer тоже.

выдирать кусочки, менять битрейт и т.д.?
да

как можно с вами связатьс лично?

Сергей, пройти в раздел сайта "Контакты", а по какому вопросу? Почему не в блог комментировать?

могу и в блог.
мне нужно разработать небольшой скриптик, желательно в связке с флеш, чтобы можно было вырезать кусок mp3 файла. уже неделю не могу найти кто бы мне помог...

Сергей, я во флеше не спец, а на серверной стороне надо просто передавать позиции и всё. Вся задача упирается во флеш.

ну а если без флеш?
какие варианты?, цены?, сроки? и самое главное - кто может сделать?

Сергей, что значит без флеш?

ну без флеш, значит без прослушивания и забиваем ручками в минутах и секундах откуда и до куда вырезаем...

цены?, сроки? и самое главное — кто может сделать?
если я, то возьмусь за 2000-3000 руб (судя по заданию), в течении 2х дней смогу сделать

при этом либо должен быть сервер с ssh доступом и freebsd в качестве платформы, либо толковый админ, который все сам настроит что ему скажу

что могу сделать:
* вырезаю аудио по переданным параметрам секунд
* проигрываю обрезанное аудио
* смена битрейта

аську надобы для оперативности ;)
моя тридцать семь десять пять семь.

Сергей, у меня только жаббер и скайп

отписал вам на почту.

Я вот что-то понять не могу... вот этот кусок

/*
 * Удаление временного файла, он нам уже не нужен...
 */
unlink($outputPath . '_temp');
/*
 * С помощью расширения php5-ffmpeg получаем информацию о видео-файле
 */
$movie = new ffmpeg_movie($outputPath);

До этого мы НИГДЕ не копировали файлик в $outputPath,т.е. получается, что мы грохаем темповый файл и пытаемся создать "из ниоткуда" $outputPath-файл... или я что-то не понимаю?

Все - нашел... мои кривые глаза - ямди прошивает escapeshellarg($outputPath)...

Алексей, угу )

Не знаю как и быть. У меня есть свой плеер, написаный на acstinscript 2. Думаю как бы мне туда запихнуть этот псевдостример... Ведь .as библиотека псевдострименга под 3-ий идет, а я в нем ни бум-бум((( Декомпильнул этот flowplayer - нифига там не понял. У гого есть какие мысли что мне делать7

Либо использовать flowplayer, либо учить AS3

Обнадежил(((... Т.е. других вариантов нет7 Неужели нет какой-нить недокументированного метода или еще какого-нить способа грузить видео с нужной метки как seek(5), например7 Просто чисто для этой задачки пока AS3 учить нет времени, т.к. он сильно отличается от AS2 как я посмотрел, а плеер мне нужен именно мой... там своя специфика и другие плеера не подойдут ну никак... Может хоть какая зацепка есть7 а...7

Я не специалист во Flash/Flex и не знаю язык AS

на http://uppod.ru/talk_177 в первом абзаце понял, что нужно чтобы на сервере был lighttpd. Что это такое7 А флеш-проигрыватель даже не придется трогать. Как я понял просто запрос будет не к file.flv, а file.flv?start=123, например и сервак пришлет видео с нужного места. Если я правильно понял - все не так уж и плохо и возможно даже этот flowplayer ковырять не придется. Тока что это за lighttpd, как его сделать, что прописать в var so = new SWFObject("flash/my_swf.swf", "movie", "100%", "100%", "9", "#ffffff");... html страницы? Вроде что-то проясняется, но непонятно что конкретно.

лайти это веб-сервер, альтернативный nginx
если у вас будет работать с лайти без проблем, то и с nginx тоже будет

[code]

stenogramma

var so = new SWFObject("flash/psevdo_1.swf", "movie", "100%", "100%", "9", "#ffffff");//Мой плеер)))
so.addParam("allowFullScreen", "true");
so.addVariable("start_seek", "1");//Стартовый параметр
so.addVariable('autostart','true');//Тоже откуда-то возможно нужно
so.addVariable("file_name", "http://flowplayer.org/video/Extremists.flv");//Беру с http://flowplayer.org
so.addVariable("streamscript", "lighttpd");//Это я вычитал с форумов всяких
so.write("flashcontent1");

[/code]

Так вот когда я отсылаю с параметром start_seek=0 - все окейно работает. В моем плеере переменная start_seek прибавляется к переменной file_name и получается запрос типа "http://flowplayer.org/video/Extremists.flv?start=0" и видео грузится по этому пути. Как я понял - главное что запрос идет на сервак lighttpd, а http://flowplayer.org должно быть именно то что нужно. и как я понял нужно передавать в GET-е переменную start. Но работает тока с start=0. Не знаю - вроде капаю в нужном направлении, но файл отказывается грузиться если start не равно 0. Мысли есть7

Я не работал с лайти, по идее все должно быть нормально

В строке so.addVariable("streamscript", "lighttpd"); я передаю переменную — как ее должен обработать плеер? Может в этом дело, что я никак эту переменную не обрабатываю во флеше?

попробую поискать на форумах... блин, инфы мало((( В основном рассматривают уже готовые плееры, а что в нем должно быть, чтобы работало нигде нет. Должно быть все просто .....myFlv.flv?start=10, а файл лежать на lighttpd, например. Но не пашет. Чего-то я не учитываю((

А какой для видео нужен хостинг? Т.е. должны ли быть какие-то особые характеристики - выделенный сервер и т.п. Есть два "обычных" хостинга - буфферизация происходит медленно, хотя ролики не очень большие, и смотрю их я один.

Max Folder, нужен установленный ffmpeg, yamdi, nginx (+mod_flv), php-ffmpeg. Но, можно обойтись только ffmpeg, а через скрипт, который я приводил в статье можно эмулировать mod_flv

adw0rd, это я понял. А технические характеристики сервера, толщина канала? Нужно ли что-то специфическое (например, аренда сервера за 10000 рублей в месяц), или я просто беру хостинг, условно говоря, за 200 рублей в месяц и радуюсь жизни?

Условно говоря - да, можете взять хостинг и радоваться жизни :) А там уже узнаете - подходит он вам или нет. У меня прекрасно работает на FirstFDS за 600 руб.

Извиняюсь, за очередной глупый вопрос. FirstFDS (хотя оно где-то упоминается) я не нашел, нашел firstvds.ru - это оно? Виртуальный выделенный сервер.

Max Folder, да, именно FirstVDS, извините, с просони не то написал :/

Друзья, подскажите - какие ключи у хука watermark??? что-то не могу в инете найти нормальное описание. И работает ли корректно watermark.so с водяными знаками в формате png?

Алексей, вам сюда

Я конвертирую видео используя Fedora+mencoder+On2VP6. Качество просто ураган. Нарезку делаю напрямую с DVD iso файла. Качество практически без потерь при битрейте 700кБит.
Тестировал производительность на реальных серверах.
Один WindowsXP другой Fedora конфигурации идентичные. Конвертера настроены идентично.
В Windows XP в 5 потоков конвертации 1 фильм снимает часов так за 14.
В Fedora в 10 потоков конвертации 1 фильм снимает практически в риалтайме - 1.5-2 часа.
Вывод: под Linux-ом фильм конвертируется в 14 раз быстрее.
Здесь полностью подробно описано как настроить качественный HDTV-видеохостинг
http://webmax.do.am/publ/administration/nastroyka_servera_hdtv_videohostinga/1-1-0-1
Настраивал полностью по этой статье.

подскажите кто тестировал данный скрипт скриминга на php, он работает вообще?

и каким плеером передавать GET['position']; ??
я так понимаю тут есть какие-то своя реализация...?

===
$start = (int) @$_GET['position'];
if ($start < 0) die("You fucking idiot");
// open file for reading

....

мне вот захотелось переписать его на perl (или на Си) :)

зашел сюда http://fanno.dk/index.php?option=com_content&task=blogcategory&id=15&Itemid=53, там у