12.10.2017

ffmpeg с вебкамеры

Мне нужно было записать 5 минут видео с веб-камеры и уместить его в 5 мегабайт.

До сего момента с ffmpeg я не сталкивался, поэтому попытка записать видео с камеры для последующего его просмотра из веб-браузера делалась методом гугления проб и ошибок (мы вновь говорим про Ubuntu).

Если кто-то скажет, мол читай маны, там все понятно. Нет! Сначала нужно попробовать как оно работает в различных вариантах, а потом читать маны, вот тогда уже они понятны.



1. Проверка камеры

Для начала воткнем камеру в usb и проверим ее наличие командой:
ls /dev/video*
Если все хорошо, то в консоль будет выдано имя камеры типа:
/dev/video0

2. Простая запись

Самый простой пример записи с камеры будет вот таким:
ffmpeg -f v4l2 -i /dev/video0 -t 10 1.mp4

где:
-f v4l2 - API видео;
-i /dev/video0 - откуда берется видеопоток;
1.mp4 - куда кладется видеопоток;
-t 10 - ограничение 10 секунд (но прервать запись можно в любой момент, нажав q).

Результат можно посмотреть в браузере.
Однако, есть несколько но:
- разрешение будет не то, которые вы ожидали;
- размер файла будет большой;
- на слабом компьютере изображение будет дергать.
- в браузере андройда вы скорее всего не увидите результат.

Давайте резберем все по очереди.

Вот что нужно запомнить: перед -i ставятся все настройки для входящего потока, после -i, для записываемого.

3. Тюнингуем

3.1. Возможности камеры

Проверим возможности камеры, выполнив команду:
ffmpeg -list_formats all -i /dev/video0

У меня выдается:
[video4linux2,v4l2 @ 0x1a74790] Raw       :     yuyv422 :           YUYV 4:2:2 : 640x480 160x120 176x144 320x176 320x240 432x240 352x288 544x288 640x360 752x416 800x448 864x480 960x544 1024x576 800x600 1184x656 960x720 1280x720 1392x768 1504x832 1600x896 1280x960 1712x960 1792x1008 1920x1080 1600x1200 2048x1536 2592x1944
[video4linux2,v4l2 @ 0x1a74790] Compressed:       mjpeg :          Motion-JPEG : 640x480 160x120 176x144 320x176 320x240 432x240 352x288 544x288 640x360 752x416 800x448 864x480 960x544 1024x576 800x600 1184x656 960x720 1280x720 1392x768 1504x832 1600x896 1280x960 1712x960 1792x1008 1920x1080 1600x1200 2048x1536 2592x1944

Из выдачи мы видим:
- поток с камеры может быть не сжатый (Raw) и сжатый (Compressed);
- разрешение с камеры может быть от 160x120 до 2592x1944.

3.2. Выбираем разрешение и входной поток

Теперь зададим параметры камеры для входящего потока, которые нас интересуют, подставив их перед -i.
Меня интересует сжатый формат 640x460. Выполним пример:
ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -t 10 2.mp4

где:
-input_format mjpeg - поток сжатого видео;
-video_size 640x480 - разрешение;
- остальное описано выше.

3.3. Уменьшаем выходной поток

Записанный файл из сжатого видео гораздо меньше чем из не сжатого, но все равно огромен.
Выберем кодек, которым будем сжимать видео.
Нас интересует кодек, который позволит проиграть видео в браузере с использованием HTML5.

ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -f mp4 -t 10 2.mp4

где:
-f mp4 - контейнер выходного видео;
- остальное описано выше.

Вот теперь файл должен заметно уменьшится и должен проигрываться из браузера, с помощью кода:
<video controls width="640" height="480" autoplay>
  <source src="http://site.ru/2.mp4" type="video/mp4">
</video>

3.4. Еще уменьшаем выходной поток


Еще уменьшить выходной поток можно с помощью понижения fps.

fps у нас - это количество кадров в секунду, стандартно их 24-25, но если видео у нас в большинстве своем статичное, можно и до 5 понизить и до 1 даже.
Обратите внимание, если компьютер слабый, то при кодировке он не сможет вытянуть 24 кадра в секунду и будет пропускать кадры, так что изображение будет дергаться неравномерно. Поэтому для слабых компьютеров понижение fps - это даже необходимость.
При чем, например входной поток не всегда можно понизить сразу, устройство может не работать с таким fps, которым вы от него хотите.
Поэтому можно понизить и входной поток до допустимо возможного, чтобы не перегружать процессор и выходной, чтобы поджать видео.

ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -r 5 -i /dev/video0 -r 1 -f mp4 -t 10 2.mp4
где:
-r 5 - ограничение входного потока (до директивы -i) до 5 fps, дабы не грузить процессор;
-r 1 - ограничение входного потока (после директивы -i) до 1 fps, дабы поджать видео еще больше.
- остальное описано выше.

3.5. Еще уменьшаем выходной поток (альтернатива)

Альтернативно выходной поток можно понизить с помощью изменения битрейта (размера потока видео в секунду).
Забудем про пример выше (после такого уменьшения fps битрейт нам уже не поможет). Сделаем средний битрейт по размеру 100 килобит в секунду:
ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -b:v 100k -f mp4 -t 10 2.mp4

где:
-b:v 100k - средний битрейт 100 килобит в секунду;
- остальное описано выше.

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

Побирайте битрейт, читайте документацию про минимальный битрейт, максимальный битрейт, буфер и т.д. и т.п.

3.6. А как же браузер андройд?


На самом деле в процессе кодировки ffmpeg сам подсказывает что делать:
No pixel format specified, yuvj422p for H.264 encoding chosen.
Use -pix_fmt yuv420p for compatibility with outdated media players

Вот и добавим эту дерективу в выходной буфер:
 ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -vf format=yuv420p -f mp4 -t 10 2.mp4

где:
-vf format=yuv420p - формат, совместимый с медиаплеерами в сматрфонах;
- остальное описано выше.

P.S.
На сервере в .htaccess не лишне будет записать:
AddType video/mp4 .mp4 .m4v

P.S.S.
Тестируйте на динамичных изображениях. Статика будет маленькой сама по себе.

Ну вроде все для того чтобы посоревноваться в сжатии. У меня получается 5 мегабайт на 5 минут видео. А у вас?