20 January 2012

Zendframework + ffmpeg + gearman + amazon = Видео encoder сервис

Self Promo
Хочу поделиться с хабрасообществом опытом создания веб сервиса для конвертирования видео и сохранения в облачном хранилище. Сразу оговорюсь, сервис написан для внутреннего пользования одной европейской компании, и уже работает более 6 месяцев. Одним из направлений компании является продукт WebTv. Было очень проблематично на каждом новом сайте разворачивать структуру для конвертирования видео, и зачастую эти процессы очень тормозят работу сервера.
Было решено создать сервис, который бы удовлетворял следующим требованиям:
  • Легкая интеграция с любым сайтом.
  • Реализовать процесс конвертации более одного видео файла в одно и то же время.
  • Реализовать процесс сбора видео с ftp, IMAP и просто напрямую получать видео файл через HTTP POST.
  • Опционально при помощи дополнительных параметров, уметь вращать видео, вещать водные знаки и т.д.
  • Реализовать безопасную систему авторизации во фронте, где юзеры могут видеть, какие данные уходят на видео сервис помимо видео файла.
  • Отправлять готовые результаты на Amazon S3.
  • Хорошенько обрабатывать ошибки, и оповещать об этом клиентский сервис.


Самая простая схема интеграции с сервисом.


Самая используемая схема — загрузка видео POST'ом. На клиентском сайте в форму добавления нового видео встраивается flash загрузчик, который отправляет файл сразу на видео сервис вместе с данными авторизации. На диаграмме ниже подробно изображены все этапы:



Проверка данных и видео на стороне сервиса.



Аутентификация
Т.к. логин и пароль для авторизации доступны в html коде, и это не есть хорошо, надо было что — то придумать. Решением стало использование одноразовых паролей со ограниченным сроком жизни и двух ступенчатая система аутентификации. Сервис клиента генерирует пароль и сохраняет его к себе в базу (а так же время генерации и маркер использован / не использован). После получения запроса видео сервис стучиться на клиентский сервер, по заранее определенному урлу, и получает подтверждение о актуальности пароля.

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

Gearman


Собственно стояла задача после сохранения запроса в базу начать конвертировать видео. Естественно старый добрый cron здесь подходит слабо, запуск раз в минуту, последовательное выполнение. До этого я уже пользовался Gearman и мой выбор пал на этот инструмент. На самом деле пришлось с ним повозиться, почему — то поначалу в упор не хотел работать как надо:
function converter()
{
//Здесь код конвертера
}
$worker = new GearmanWorker();
$worker->addFunction("convert", "converter");
//и т.д.


Воркеры постоянно возвращали -1 и отмирали. Я вспомнил, что в предыдущих проектах делал воркеры немного по другому. А унаследовался от GearmanWorker, cделал базовую прослойку для всех типов воркера:

   class App_Model_Worker_Abstract extends GearmanWorker 
   {
	public function __construct($gearman) 
	{
		parent::__construct();
		if (!$this->addServer($gearman['host'], $gearman['port'])) {
			throw new Exception("Can't connect to Gearman");
		}		
	}

	public function work() 
	{
		while (parent::work());
		return  true;
	}	
   }


а далее уже собрал нужные воркеры:

   // Конвертор
        class App_Model_Worker_Converter extends App_Model_Worker_Abstract 
        {
        
	public function __construct($gearman) 
	{
		parent::__construct($gearman);
                //Тут еще немного кода	
	}	
	
	public function action($job)
	{
		$fileId = $job->workload();
		$file = $this->_modelFileResponse->getPrepareToConvertFile($fileId);	
                //Собственно здесь конвертируем.
	}
    }
   //Воркер, который отправляет готовые результаты в хранилище 
	class App_Model_Worker_SendResponse extends App_Model_Worker_Abstract 
        {
		public function __construct($gearman) 
		{
			parent::__construct($gearman);	
		}	
		
		public function action($job)
		{
			$responseId = $job->workload();
                        //Тут собственно код загрузчика.
		}
	}


Далее, вылезла еще одна не приятная вещь, соединение с ДБ закрывалось через какое — то промежуток времени, поэтому я решил, что буду закрывать соединение перед началом процесса конвертации:

   $model->getAdapter()->closeConnection();


Статистика показывает, что достаточно гонять 3-4 воркера, чтобы нормально обслуживать до 10 запросов в час. Подробнее прочитать про Gearman можно здесь Gearman.org

FFmpeg


С этим «фруктом» у меня связано много бессонных ночей. По началу все было тривиально, у меня уже были отлажены стандартные запросы, типо сделать ресайз, битрейты и все такое — здесь все тривиально. Самое интересное началось когда нужно было за один проход и ресайзить и поворачивать, и маску вещать и паддинги добавлять, вообще все это вместо ни в какую не хотело работать. На помощь пришли vfilters, которые были добавлены сравнительно не давно. Вся прелесть в том, что можно последовательно применять фильтры, т.е. к примеру сначала повернуть, отресайзить, добавить паддингов, повесить водные знаки. Вот как выглядит типичная команда для ffmpeg в моем сервисе:

/usr/local/bin/ffmpeg -i "/home/encoderws/public_html/data/files/requests/example-test-5c2a1c8a2e9e540690ba05670a526872_d917a8705cade8efdd87008df20ef19c" -acodec libfaac -vcodec mpeg4 -b 400k -ar 44100 -f mp4 -ab 96k -ac 2 -vf "[in] rotate=90 [rt0], [rt0] scale=203:360 [sc0], [sc0] pad=640:360:219 [pd0], [pd0] scale=640:360 [sc1], movie=0:png:/home/encoderws/public_html/data/files/watermarks/watermark.png [wm], [sc1] [wm] overlay=0:0:1 [out]" -y '/home/encoderws/public_html/data/files/responses/example-test-5c2a1c8a2e9e540690ba05670a526872_d917a8705cade8efdd87008df20ef19c.mp4


Т.к. каждый запрос требует создания двух видео flv + mp4, то по окончанию работы, данные синхронизируются и воркер, чья работа закончилась позже, ставит в очередь задачу загрузки готовых данных на amazon. Если возникли ошибки при конвертации, оповещаем об этом сервер.

Amazon


Другой тип Gearman воркера, занимается загрузкой готовых данных на Amazon S3 используя стандартную Zend библиотеку для работы с Amazon api.
Вообще хочу сказать, что за последний год Amazon несколько раз расстраивал, например менеджер из Европы пишет мне что мол, у него видео не проигрывается во фронте, а я ему пишу, мол как так, я вот сижу прям это видео смотрю, которое у тебя не работает. Потом дошло, мы обращаемся к разным датацентрам Amazon. А еще бывают проблемы при загрузке больших видео, примерно раз в месяц, какое — нибудь видео да не загрузиться, а сервер Amazon вернет какой то невнятный код ошибки.

Про Amazon S3 можно почитать подробнее здесь Amazon Simple Storage.

Другие вкусности


Сервис по запросу умеет вешать водяные знаки на видео, умеет вращать видео, корректно изменять размер по заданным параметрам, заполняя пустое пространство черным. Для этого используются vfilters, поставляемые с ffmpeg в последних версиях.

Умеет собирать видео с ftp, imap, amazon s3 storage и так же конвертировать его, далее оповещая сервер (Но эти методы используются крайне редко).

Собственно любопытная штука про IMAP, речь идет про сбор видео отправленных через MMS. Я в детали реализации не вдавался, мое дело было просто залезть на почту по IMAP протоколу найти новые сообщения, собрать из них все видео файлы и обработать.

И вот сейчас компания думает об еще одной вкусности. Прикрутить Asteriks, и к примеру клиент делает видео звонок со своего телефона, который поддерживает 3G оставляет видео сообщение, через Asteriks эта штука попадает в специальную папочку. Каждые 5 минут Видео сервис по фтп заходит на нее, если нашло что — то новое, обрабатывает, и возвращает на сервер клиента, готовое к употреблению видео.

Последние 3 месяца сервер работает стабильно, и не разу не упал. За пол года обработано более 4000 запросов.

Интересно услышать мнения, замечания, конструктивную критику.
Tags:zendсервисыamazon s3amazon rtmp streamerffmpegyoutubegearman
Hubs: Self Promo
+25
1.9k 116
Comments 27