работа с видео php
Потоковая загрузка и обработка видео посредством node.js + php и ffmpeg — часть первая
Работаю в аутсорсинговой компании и как-то встала задача загрузки видео с возможностью последующей обработки для внутренних нужд приложения: ресайз до нужного размера, конвертирование к нужному формату, вытягивание аудио-дорожек (если таковые присутствуют), раскадровка видео. В конце результаты нужно сохранить в облачном хранилище для последующего использования в онлайн-редакторе. Требования: масштабируемость, неограниченый размер видео, скорость, кроссбраузерность, наглядность.
Поскольку тема очень обширная, разделю ее на разделы:
Часть первая
1. Кроссбраузерность
Для того, чтобы обеспечить это требование нельзя было использовать HTML5 загрузку файлов (не во всех браузерах она реализована). Поэтому мы пошли путем создания обычной HTML4 формы, которая сабмитилась на сервер с node.js. Для того, чтобы получить текущий прогресс загрузки/обработки видео создавались AJAX-запросы. В форму включался случайно сгенерированная строка-идентификатор (например, 32 символа). Node.js привязывал к ней информацию по текущему файлу и при каждом запросе состояния передавал вот такой JSON:
На основании этого пользователь видит состояние загрузки.
2. Авторизация пользователя
Поскольку фронтенд на PHP (там же происходит авторизация пользователя) нужно было как-то передать данные сессии на сервер node.js. Для этих целей использовался memcached. О подмене сессий в PHP можно прочесть тут. Суть проста: сессии сохраняются в memcached, при загрузке через форму передается session_id, который считывается node.js. Далее node.js обращается к memcached в поисках соответствущей сессии, берет user_id и т.д. Здесь есть один важный момент: session_id в самой форме нужно ставить вначало, потому что POST-запрос передает параметры в порядке их следования в HTML. То есть, если мы сделаем так:
В таком случае к node.js сначала прийдет сам файл, а потом session_id, что не есть хорошо. Ведь нам нужно сначала знать, от кого приходит файл и отменить в случае, если пользователь не авторизирован. Если в примере выше поменять местами file и session_id, то сначала мы получаем сессию, притормаживаем загрузку файла, и ждем пока сервер проверит, все ли в порядке с пользователем и только тогда продолжаем загрузку. В node.js можно ставить request на паузу.
3. Потоковая обработка видео
Проблема в том, что не все видео может быть обработано сразу. Например, по спецификации mp4 формата метаданные идут в конце файла. А нам ведь нужно сразу знать какие размеры у видео, какие дорожки есть в файле, длительность и т.д. Плюс к этому некоторые форматы видео при перекодировке нуждаются, чтобы была возможность обращаться к разным частям исходного файла. Исходя из этого есть два случая с двумя вариантами вариантами каждый:
1. Вначале загрузки, когда мы вытягиваем командой ffmpeg информацию о файле, используя node.js:
В переменной ffmpeg_stdout мы получим все дорожки, которые содержатся в файле или ошибку. Если есть ошибка, значит ffmpeg не может обработать файл потоком. В таком случае предупреждаем пользователя об этом и загружаем весь файл сразу, и только после этого производим нужные операции.
2. В данном случае скорость важна, поэтому мы пытаемся запустить раскадровку, как только видео только началось загружаться. Но если формат не поддерживает потоковую обработку, то приходится ждать момента полной загрузки.
Получается такой алгоритм: возможно извлечь метаданные, тогда пытаемся налету обработать видео, если и это получается, то радуемся, во всех остальных случаях ждем полной загрузки. На практике лишь небольшое количество видео требуется загружать полностью, чтобы начать обработку.
На сегодня пожалуй все, завтра утром на работу.
Хотел бы завершить на мажорной ноте: возможности безграничны, можно распределить нагрузку на несколько серверов, на ходу показывать результаты, например вытягивать последний готовый фрейм из видео. Работает это все на Amazon EC2. По скорости могу сказать, что скорость загрузки не было возможности проверить (канал слабый), но не меньше 10мбит, думаю точно. А по нагрузке на сервер: Middle server в одиночку одно видео тянет где-то 2Мбит видео, то есть загрузка ушла далеко вперед, а обработка не поспевает. Наибольшее время занимает ресайз. Раскадровка и вытягивание аудио-дорожки относительно быстро происходит.
Если пост понравится, опишу реализацию и механизм работы более подробно в следующие разы.
Скриншоты того, что получилось. Сразу оговорюсь, это еще не рабочий вариант, поэтому строго не судите:
flash-видео на веб-страницах и PHP
Эта статья — перепечатка статьи от 16 ноября 2007 г. с моего блога, ссылку на который можно найти в моем же профайле — но, поскольку так уж вышло, что тема эта, кажется, до сих пор многим интересна, к тому же не так давно я читал на эту тему доклад на конференции PHPConf… в общем — судите сами.
Вступление
В последнее время все большее распространение получает технология трансляции видео через веб – без необходимости скачивать медиа-контент на машину пользователя, с просмотром прямо из браузера. Таким образом можно просматривать довольно большое количество различных форматов видео, однако, это требует наличия определенных плагинов на стороне пользователя. Очевидно, самым распространенным подобным плагином является flash-player (ну хорошо, хорошо: если быть точным, то он, конечно же, не может сам по себе проигрывать видео – но позволяет легко создать приложение, способное воспроизводить видео-контент). Яркий пример такого подхода – youtube.com. Попытаюсь рассказать о том, как самостоятельно организовать трансляции флэш-видео на своей веб-странице и какие существуют подходы к этой проблеме.
Следует отметить, что статья не претендует на статус хоть сколько-нибудь инновационной и является чистой воды компиляцией из источников, доступных в открытом виде. Я надеюсь, тем не менее, что эта компиляция даст многим тот необходимый минимум знаний о предмете доклада, который заложит основу для собственных разработок и исследований.
1. О формате FLV
Итак, flash-player воспроизводит видео в формате FLV, других форматов не понимает.
Подробнее о самом формате можно почитать, например, здесь: en.wikipedia.org/wiki/FLV, а также, само собой, на сайте компании Adobe – производителя flash-player. В рамках данного доклада нам не понадобятся детальные знания о формате, а те, что понадобятся, приведены ниже.
FLV-файл состоит из заголовка и собственно самого фильма.
Заголовок содержит определенную мета-информацию о фильме: продолжительность, размер, и т. д. и т. п. На самом деле, нас с вами в этой мета-информации заинтересует только одно обстоятельство – а именно то, что там содержатся данные о ключевых фреймах фильма (их позиции по временной и байтовой шкале). Сама мета-информация представляет собой произвольной вложенности ассоциативный массив, сериализованный в формат AMF, который является одним из принятых стандартов при разработке приложений на flash.
Примечание: для PHP существует несколько библиотек, позволяющих выполнять (де)сериализацию в/из AMF.
2. Как организовать трансляцию FLV-файлов на веб-страницах
Чтобы считать трансляцию успешной, нужно соблюсти два условия: сервер успешно показал контент, а клиент его не менее успешно посмотрел. Соответственно, необходимы две части приложения – сервер и клиент. В качестве клиента выступает flash-приложение, способное воспроизвести видео-поток с указанного URL. Подобных программ существует множество, и мы здесь не будем подробно на них останавливаться. Поговорим о серверной части. Итак, какие же есть возможные пути реализации?
2.1. Download — скачивание
Это самый простой путь. Он требует простейшего клиента, который просто запрашивает видео-поток по заданному URL и воспроизводит его.Серверу только и надо, что обработать HTTP-запрос и выдать соответствующий контент. Не нужно никакого специфического софта.
«Тогда к чему какие-то еще другие пути?» — такой вопрос сложно не задать. Однако, не все так гладко с этим способом… Дело в том, что если он отлично подходит для показа небольших видео-фрагментов, длиной до 2-3 минут, то фильмы подлиннее так показывать не годится: для того чтобы пользователь мог посмотреть какой-либо кусок фильма, необходимо, чтобы этот кусок уже закачался на его компьютер. Другими словами, находясь в начале фильма, мы не можем передвинуть «ползунок» таймера в конец и посмотреть финальные титры. Такой способ не дает также никаких возможностей для защиты видео-контента от скачивания.
2.2. Streaming – потоковое видео
Этот способ – пожалуй, наиболее продвинутый. Здесь есть возможность перемотки к произвольному месту в потоке, определенные механизмы защиты контента (если говорить откровенно, то эти механизмы всего лишь затрудняют получение контента, являясь, по сути, защитой только от неумелого взломщика). Также полезная возможность – организация «живых» видео-трансляций. Если вам необходимы живые трансляции – нужно стримить, другого выхода у вас нет.
Что ж, возникает тот же вопрос, что и с предыдущим способом: «если стриминг так хорошо, то к чему какие-то еще другие пути?». Стриминг хорош, но не каждому по карману. За медиа-сервер (например, «Flash Media Server») придется выложить кругленькую сумму. Впрочем, имеются и опенсорс решения, например, ffserver (который, однако, не вполне подпадает под тему доклада, поскольку не умеет стримить файлы), а также Red5, который написан на Java и поэтому также подойдет не для всех.
2.3. HTTP-streaming (эмуляция потокового видео через HTTP)
Из названия можно сделать догадку о том, что третий способ является комбинацией первых двух. В каком-то приближении можно считать, что это так и есть. Как мы уже отмечали, большим недостатком 1-го способа (download) является невозможность перемотки в произвольное место фильма. В HTTP-streaming эта проблема решается так: при перемотке в то место, которое еще не успело скачаться на машину пользователя, текущее скачаивание прекращается и на сервер отправляется новый запрос, содержащий указатель на то, с какого места в фильме нужно начать скачивание. Здесь есть определенные тонкости, которые мы подробнее обсудим в следующей главе. Что же касается преимуществ и недостатков такого подхода… На мой взгляд, для трансляции файлов – это лучший вариант. Он достаточно прост для того, чтобы реализовать его на любом языке (скриптовом, как PHP или Ruby – или же компилируемом, как C), так что вы можете решать, что использовать, исходя из требований к скорости разработки, скорости работы результирующего приложения, имеющегося ПО и прочая, прочая. Также для таких распространенных быстрых веб-серверов как Nginx и Lighttpd имеются готовые модули для трансляции FLV-файлов, написанные на C и работающие весьма шустро.
На этом способе я и остановлюсь немного подробнее…
3.Http-streaming
3.1. Готовые решения: lighttpd, nginx+http_flv_module, flv4php.
Поддержка Http-streaming осуществлена в популярных веб-серверах lighttpd и nginx. В случае использования этих решений, вам всего лишь нужно положить FLV-файлы в место, доступное веб-серверу, все остальное – дело сервера и клиента, никакого программного кода для серверной части писать не придется. Клиент же должен будет запрашивать FLV-файлы, добавляя к URL’у GET-параметр «start», например, local-nginx/sample.flv?start=12345.
Существует также бесплатное решение (возможно, их и больше) на основе PHP — flv4php. Его большой плюс – в том, что в этом проекте имеется готовый плеер – клиент для HTTP-streaming. Минус – в том, что это решение довольно тяжеловесное, и, по данным моих тестов на своей рабочей машине, сильно нагружает процессор (странное явление, адекватного объяснения которому мы не нашли, однако, от греха подальше, поспешили отказаться от использования серверной части flv4php и ограничились тем, что позаимствовали у них плеер, который после модификации под наши нужды стал служить нам верой и правдой). Еще один недостаток – в качестве «тумбы» (картинка, которую пользователь видит при открытии страницы на экране плеера) используется первый фрейм фильма, и это поведение не кастомизируется. Допускаю мысль о том, что flv4php можно настроить и лучшим образом, и заставить его таки делать то, что нужно, и без диких нагрузок на процессор. И тем не менее, небольшой фрагмент PHP-кода, приведенный ниже, делает практически то же самое, что flv4php – и гарантированно с хорошей производительностью 🙂
3.2. Сделай сам или все только кажется сложным.
Как видите, все предельно просто, думаю, пояснения излишни.
4.Преобразование в FLV из других форматов. Основы ffmpeg & mencoder. Мета-информация и как ее впихнуть в FLV-файл.
Литература и ссылки:
Этот раздел не отличается полнотой, на самом деле, конечно же, пришлось несколько больше излазать:)
Работа с видео на php с помощью php ffmpeg
Работа с видео на php с помощью php ffmpeg
Установка php ffmpeg под Windows.
скорей всего у нас получилось так что ознакомимся с функциями =)
Перевод официальной документации (API Docs)
$movie->getDuration()
Вернёт продолжительность аудио или видео файла в секундах.
$movie->getFrameCount()
Вернёт количество кадров (фреймов) аудио или видео файла.
$movie->getFilename()
Вернёт путь к видео или аудио файлу
$movie->getComment()
Вернёт поле комментария из аудио или видео файла
$movie->getTitle()
Вернёт поле заголовка из аудио или видео файла
$movie->getCopyright()
Вернёт поле «авторское право» из аудио или видео файла
$movie->getArtist()
Вернёт ID3 поле «Артист» из mp3 файла
$movie->getGenre()
Вернёт ID3 поле «Жанр» из mp3 файла
$movie->getTrackNumber()
Вернёт ID3 поле «номер дорожки» из mp3 файла
$movie->getYear()
Вернёт ID3 поле «Год» из mp3 файла
$movie->getFrameHeight()
Вернёт высоту видео в пикселях
$movie->getFrameWidth()
Вернёт ширину видео в пикселях
$movie->getPixelFormat()
Вернёт формат пикселов видео
$movie->getBitRate()
Вернёт скорость потока(битрейт) видео или аудио файла в битах в
секунду
$movie->getVideoBitRate()
Вернёт скорость потока(битрейт) видео в битах в секунду.
Замечание : Работает только с файлами у которых постоянный битрейт
$movie->getAudioBitRate()
Вернёт скорость потока(битрейт) аудио у видео или аудио файла в
битах в секунду.
$movie->getAudioSampleRate()
Вернёт скорость дискретизации аудио у видео или аудио файла в битах
в секунду.
$movie->getFrameNumber()
Вернёт текущий номер кадра.
$movie->getVideoCodec()
Вернёт название видео кодека который использован для видео файла
как строку
$movie->getAudioCodec()
Вернёт название аудио кодека который использован видео файл как
строку
$movie->getAudioChannels()
Вернёт количество аудио каналов (1 моно 2 стерео и т д) как целое
число
$movie->hasAudio()
Вернёт логическое значение соответствующие наличию или отсутствию
аудио
$movie->getNextKeyFrame()
Вернёт следующий ключевой кадр как ffmpeg_frame объект. Возратит
логическую ложь (false) если кадра нет
ffmpeg_frame object methods
$frame = new ffmpeg_frame(Resource gd_image)
Создаст обьект ffmpeg_frame из ресурса GD картинки.
Замечание : Функция недоступна если нет библитеки GD.
$frame->getWidth()
Вернёт ширину кадра.
$frame->getHeight()
Вернёт высоту кадра.
Замечание: Обрезание всегда происходит до измения размера кадра.
Значения параметров обрезания должны быть натуральными числами.
Замечание : Параметры должны быть натуральными числами
$frame->toGDImage()
Возвращает truecolor GD картинку (ресурс) кадра
Замечание : Функция недоступна если нет библитеки GD.
ffmpeg_animated_gif
$gif = new ffmpeg_animated_gif(String output_file_path, Integer width,
Integer height, Integer frame_rate, [Integer loop_count])
Создает новый ffmpeg_animated_gif обьект
Создаем сервер для потокового видео: глава из книги по PHP от нашего разработчика
У нас в Skyeng работают весьма талантливые люди. Вот, например, бэкенд-разработчик Words Сергей Жук написал книгу про событийно-ориентированный PHP на ReactPHP, основанную на публикациях его блога. Книга англоязычная, мы решили перевести одну самодостаточную главу в надежде, что кому-то она сможет пригодиться. Ну и дать скидочную ссылку на всю работу.
В этой главе мы рассмотрим создание элементарного асинхронного сервера для видео-стриминга на ReactPHP Http Component. Это компонент высокого уровня, предоставляющий простой асинхронный интерфейс для обработки входящих соединений и HTTP-запросов.
Для поднятия сервера нам потребуются две вещи:
— инстанс сервера (React\Http\Server) для обработки входящих запросов;
— сокет (React\Socket\Server) для обнаружения входящих соединений.
Для начала давайте сделаем очень простой Hello world сервер, чтобы понять, как все это работает.
Если мы запустим этот скрипт, он будет выполняться бесконечно. Работающий сервер отслеживает входящие запросы. Если мы откроем адрес 127.0.0.1:8000 в нашем браузере, мы увидим строку Hello world. Отлично!
Простой стриминг видео
Давайте теперь попробуем сделать что-нибудь поинтереснее. Конструктор React\Http\Response может принять читаемый поток (инстанс класса ReadableStreamInterface ) в качестве тела ответа, что позволяет нам передавать поток данных непосредственно в тело. Например, мы можем открыть файл bunny.mp4 (его можно скачать с Github) в режиме чтения, создать с ним поток ReadableResourseStream и предоставить этот поток в качестве тела ответа:
Давайте теперь обновим окно браузера и посмотрим потоковое видео:
Супер! Мы сделали стриминговый видео-сервер с помощью нескольких строк кода!
Важно создать инстанс ReadableResourseStream непосредственно в функции обратного вызова сервера. Помните об асинхронности нашего приложения. Если мы создадим поток вне обратного вызова и просто передадим его, никакого стриминга не случится. Почему? Потому что процесс чтения видеофайла и обработка входящих запросов сервера работают асинхронно. Это значит, что пока сервер ждет новые соединения мы также начинаем читать видеофайл.
Поэтому есть вероятность, что к тому моменту, когда на сервер придет первый запрос, мы уже достигнем конца видеофайла, и у нас не будет данных для стриминга. Когда обработчик запроса получит уже закрытый ответный поток, от просто отправит пустое тело ответа, что приведет к пустой странице браузера.
Улучшения
Но здесь есть проблемы. Видите их?
Проверка наличия файла
Прежде чем открыть файл и создать поток, мы должны проверить, существует ли вообще этот файл на сервере. Если нет – возвращаем 404:
Теперь наш сервер не будет падать, если пользователь запросил неверный файл. Мы выдаем правильный ответ:
Определение MIME-типа файла
Ограничение на запрос файлов
Осталась проблема с запросом файлов. Пользователь может задать любой файл на сервере в строке запроса. Например, если код нашего сервера находится в server.php и мы укажем такой запрос в адресной строке браузера: 127.0.0.1:8000/?video=../server.php, то в результате получим следующее:
Теперь тот же запрос выдаст страницу 404. Исправлено!
Рефакторинг
Получается, мы можем здесь выделить два метода:
Метод makeResponseFromFile() также очень прост. Если по указанному пути нет файла, мы сразу же возвращаем ошибку 404. В противном случае мы открываем запрошенный файл, создаем читаемый поток и возвращаем его в теле ответа:
Вот код класса VideoStreaming целиком:
Разумеется, вместо простого обратного вызова обработчика запроса у нас теперь в три раза больше кода, но если этот код будет меняться в будущем, нам будет значительно проще проводить эти изменения и поддерживать наше приложение.
Примеры из этой главы можно найти на GitHub.
У Сергея также есть полезный регулярно обновляемый англоязычный блог.
Наконец, напоминаем, что мы всегда находимся в поиске талантливых разработчиков! Приходите, у нас весело.
Работа с видео в php
Нужно организовать на сайте загрузку видео, для последующего просмотра, с выбором качества 360p 720p 1080p. Возможно ли это сделать на php, не прибегая к изменениям настройки сервера? И как определять 360р или 720p и т.д.
Сейчас я делаю так, загружаю видео, конвертирую в mp4, сохраняю, вывожу на сайт в html5 player.
Но нет выбора качества. А еще есть такой не большой вопрос, как у vk.com происходит обработка видео после, что после ее окончания сервис оповещает пользователя, что видео добавлено?
1 ответ 1
Никогда не работал с видео-библиотками в PHP, но могу сказать, что вам точно нужно сделать:
Для начала вам необходимо создать PHP-файл, не имеющий (или имеющий очень большое) ограничение на работу по времени, а так же, который не прекращает свою работу, если пользователь оборвал соединение. Это можно реализовать рядом функций ini_set()t. В этот файл подключить библиотеки работы с видео-файлами. Скрипт должен будет сначала проверить, не побит ли файл. Далее конвертировать полученное видео в mp4, а после сделать несколько версий с разным качеством (140p,240p,360p,480p,720p,1080p. ) Если видеофайл большой (несколько гигабайт) это может занять вплоть до нескольких часов.
Не знаю, зачем вам понадобилось создать такой сервис, ведь уже есть такие сервисы как YouTube, RuTube, VK, которые имеют все эти функции, зачем изобретать велосипед, который и так уже много раз изобретен.