- PVSM.RU - https://www.pvsm.ru -

Потоковое видео в Android

В этой заметке я хочу рассказать о некоторых подводных камнях, с которыми можно столкнуться при работе с потоковым видео в Android приложениях. Конкретно, речь пойдёт о конвертации видео и протоколах доставки/воспроизведения видео.
Сразу оговорюсь, что экспертом я в данной области не являюсь, а лишь хочу поделится недавно полученным опытом.


Представим, что перед вами стоит задача реализовать Android приложение, способное проигрывать множество файлов, заливаемых пользователями на ваш сервер. Написать свой youtube, с блекджеком и кодеками. Для этого вам придётся решить как минимум две задачи: конвертации видео к поддерживаемому на Android формате, воспроизведение видео с удалённого источника. Рассмотрим обе эти задачи более подробней.

Конвертация видео

И так, прежде чем воспроизвести какое-то видео нашем Android устройстве, надо это видео перекодировать в поддерживаемый формат. В документации к Android чётко обозначен список [1] этих самых форматов.

Для того, что бы перекодировать файлы, заливаемые пользователями на ваш сервис, или же записать поток с TV-тюнера, вам потребуется помощь специальной утилиты ffmpeg [2], являющейся де-факто стандартом в отрасли. Подробную инструкцию по её установке можно найти на сайте одноимённого проекта [3].

Наиболее распространённым сейчас (на мой взгляд) способом хранения видео является контейнер MP4 с запокавкой кодеком H.264 AVC. Их мы, собственно, и рассмотрим.

Первым делом обратите внимание, что Android поддерживает не все возможности кодека H.264, а только определённый набор — профиль, именуемый Baseline Profile [4](BP). Так, например, в BP не входят такие полезные фичи H.264 как CABAC [5] или B-Frames [6].

Для нас это значит, что если мы будем использовать эти фичи при кодировании видео, то Android проигрывать это видео будет не обязан. Хотя и может, если ваш телефон достаточно мощный и вендор позаботился об установке и поддержке дополнительных кодеков. Так, например, видео в Main Profile без проблем проигрывается на Samsung Galaxy SII. На телефонах же обычного класса (например, Samsung Galaxy Ace) мы получим сообщение о невозможности воспроизведения видео и ошибку с кодом неверного кодека в logcat'е.

Но перейдём от теории к практике. Для того, что бы пережать видео, необходимо выполнить следующую команду:

ffmpeg -i in.3gp -f mp4
-vcodec libx264 -vprofile baseline -b:v 1500K
-acodec libfaac -b:a 128k -ar 44100 -ac 2
-y out.mp4

Рассмотрим подробнее каждый из параметров:

  • -i src входной (перекодируемый) файл;
  • -f mp4 используемый видеоконтейнер;
  • -vcodec libx264 используемый видеокодек;
  • -vprofile baseline используемый профиль;
  • -b:v 1500K bitrate;
  • -acodec libfaac используемый аудиокодек;
  • -b:a 128k аудио bitrate;
  • -ar 44100 частота звука;
  • -ac 2 количество аудиопотоков;
  • -y флаг перезаписи выходного файла;

Так же стоит отметить, что можно обойтись и без указания профиля, а явно включить/отключить нужные опции кодека H.264 через параметр -x264opts, так что бы они удовлетворяли условиям BP. Но это же занятие для любителей.

Раздача видео

Самый простой способ воспроизвести видео с удалённого сервера — это скачать его во временное хранилище и воспроизвести локально. Однако, думаю всем понятно, что в виду размеров современных видеозаписей — это не вариант.

Как же быть? Платформа Android предлагает нам нативную поддержку следующих технологий/протоколов:

  • HTTP/HTTPS progressive streaming;
  • HTTP/HTTPS live streaming;
  • RTSP (RTP, SDP);

Рассмотрим их по порядку.

Progressive streaming

Наиболее простой способ раздачи видео с помощью обычного web-сервера, сводящийся по сути к скачиванию заранее подготовленного файла по HTTP(S) протоколу. Вся соль в данном случае заключается в том, что воспроизведение файла начинается не по окончанию загрузки, а как только будет скачано достаточно данных (наполнен некоторый буфер).

Тут стоит уточнить, что при использовании контейнера MP4, необходимо сформировать файл так, что бы метаданные о видео потоке (moov atoms) располагались в начале файла (после атома ftyp), перед видеоданными (mdat atoms). Сделать это можно с помощью обработки файла утилитой qt-faststart:

qt-faststart output.mp4 result.mp4

Основной проблемой progressive streaming'а является невозможность перемотки видео к нескачанному моменту, наличие достаточного количества свободного места на устройстве и необходимость поддержки большого числа «толстых» клиентов, скачивающих видео, на web-сервере.

Воспроизведение с помощью данной технологии поддерживается платформой Android нативно. Вы без проблем (если не считать канал связи, мощность девайса и наличие свободного места) сможете проиграть удалённый файл с помощью стандартного класса MediaPlayer.

Pseudo streaming

Данная технология является логическим расширением progressive streaming'a и позволяет решить одну из его главных проблем — перемотки к ещё не скачанному фрагменту. Применима для контейнеров MP4/FLV с кодеком H.264/AAC.

Единственным отличием от progressive streaming'a в данным случае является, тот факт, что вам потребуется специальный web-сервер, который с учётом временной метки в GET-запросе будет отдавать нужный вам фрагмент видео файла. Примером такого web-сервера естественно может служить православный NGINX с его ngx_http_mp4_module [7].

Мне не удалось найти какой-либо официальной информации относительно поддержки данного стандарта в Android. Однако, эмперическим путём было установлено, что она присутствует как минимум на устройствах HTC Desire и Samsung Galaxy SII. Однако, хочу обратить внимание, что да же в случае отсутствия нативной поддержки на вашем устройстве всегда можно воспользоваться сторонними плеерами типа MX Player, которые самостоятельно реализуют логику скачки и воспроизведения фрагментов видео с нужной временной меткой, что позволяет организовать перемотку.

Live streaming

Довольно нестандартный протокол [8] передачи данных от компании Apple. Суть его сводится к тому, что раздаваемый файл «пилится» на множество небольших частей, объединяемых спецтальным файлом-playlist'ом формата M3U8. Передача данных происходит по протоколу HTTP(S).

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

Однако, появляются и проблемы. Для «распила» файла и создания playlist'а потребуется ресурсы процессора, время и место на сервере. Для вещания файла в сеть, как и в предыдущих примерах, потребуется HTTP сервер (без каких-либо дополнительных модулей).

«Распилить» видео файл можно использовать VLC [9]:

vlc -I dummy /path/to/pornofilm.mpg vlc://quit --sout '#transcode{width=320,height=240,fps=25,vcodec=h264,vb=256,venc=x264{aud,profile=baseline,level=30,keyint=30,ref=1},acodec=mp3,ab=96}:std{access=livehttp{seglen=10,delsegs=false,numsegs=0,index=/path/to/web/server/root/pornofilm.m3u8,index-url=http://localhost/pornofilm/stream-########.ts},mux=ts{use-key-frames},dst=/path/to/web/server/root/pornofilm/stream-########.ts}'

Воспроизвести такой файл можно по URL localhost/pornofilm.m3u8 [10].

Поддержка HTTP Live Streaming на нативном уровне в Android присутствует начиная с версии 3.0. С помощью сторонних плееров (DicePlayer, MX Player), судя по wiki, можно добиться поддержки с версии 2.2.

Real Time Streaming Protocol (RTSP)

Протокол [11] прикладного уровня с поддержкой состояния, разработанный специально для передачи видео. Формат команд очень напоминает HTTP. Сами же команды напоминают кнопки на обычном кассетном магнитофоне: PLAY, PAUSE, RECORD и т.д.

В отличие от HTTP Live Streaming RTSP не требует разбиения фалов на мелкие части и составления playlist'ов. Нужные части файла будут генерироваться и отдаваться клиенту налету. В качестве RTSP сервера можно использовать VLC [12].

Стоит заметить, что сам протокол RTSP не определяет способ передачи данных, а делегирует это другим протоколам. Например, RTP [13]. Для вещания файла по протоколу RTP нужно будет запустить VLC со следующими параметрами:

vlc -vvv /path/to/pornofilm.mp4 --sout '#rtp{dst=localhost,port=1234,sdp=rtsp://localhost:8080/pornofilm.sdp}'

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

Поэтому вернёмся к протоколу RTSP и воспроизведению видео по требованию (Vidoe On Demand). Для того, что бы использовать VLC в качестве RTSP сервера для проигрывания VOD необходимо прежде всего запустить VLC, указав атрибуты RTSP сервера и Telnet интерфейса:

vlc -vvv -I telnet --telnet-password 123 --rtsp-host 127.0.0.1 --rtsp-port 5554

После этого как сервер запущен, необходимо произвести его настройку. Делать это удобнее всего с помощью telnet'a, так как такой подход даёт возможность настройки налету:

new porno vod enabled

setup porno input /path/to/pornofilm.mpg

Для воспроизведения видео (в том числе и на платформе Android) необходимо запросить его по URL rtsp://localhost:5554/pornofilm.

Из недостатков можно отметить тот факт, что HTTP открыт зачастую на всех firewall'ах и проксях… с RTSP в случае политики Deny,Allow всё иначе.

Кроме того, при использовании RTSP-сервера для добавления/удаления файлов на сервере придётся обновлять его конфигурацию (список vod'ов). Да, для этого есть telnet, но это всё равно сложнее, чем просто заливать или удалять файлы из каталогов web-сервера.

Воспроизведение с помощью данной технологии поддерживается платформой Android нативно. Например, с помощью всё того же стандартного класса MediaPlayer.

Multicast

Многие считают, что multicast не работает в Android. Это не совсем так.

Во первых, в большинстве случаев он просто отключен по умлочанию, что бы не грузить ресурсы девайса лишней работой. Его можно просто включить [14].

Во вторых, да — на довольно внушительном количестве устройств он отключен во все или работает некорректно. В интернетах, поэтому поводу можно найти [15] много слёз и даже некоторые решения.

Однако, как показывает практика, проигрывать multicast видео на Android всё можно. В моём случае с этой задачей удачно справился недавно вышедший VLC Beta [16] для Android.

Кроме того с помощью VLC-сервера всегда можно свести воспроизведение multicast'a к HLS:

vlc -I dummy udp://@192.168.20.1:1234 vlc://quit --sout '#transcode{width=320,height=240,fps=25,vcodec=h264,vb=256,venc=x264{aud,profile=baseline,level=30,keyint=30,ref=1},acodec=mp3,ab=96}:std{access=livehttp{seglen=10,delsegs=false,numsegs=0,index=/path/to/web/server/root/multicast-porno.m3u8,index-url=http://localhost/multicast-porno/stream-########.ts},mux=ts{use-key-frames},dst=/path/to/web/server/root/multicast-porno/stream-########.ts}'

или RTSP:

new multicast-porno vod enabled

setup multicast-porno input udp://@192.168.20.1:1234

Попытать удачу с проигрыванием multicast'a на вашем устройстве вы можете, передав плееру URL вида udp://@192.168.20.1:1234.

Что выбрать

Если с форматом видео всё ясно (H.264 BP / MP4), то со спобом дистрибуции вопрос открыт. У каждого их них есть свои достоинства и недостатки.

Первым делом из рассмотрения я бы убрал обычный progressive streaming. Да он работает всегда и везде, но отсутствие перемотки и загрузка всего файла целиком — это уже слишком.

Следующим кандидатом на вылет является live streaming. Главным его недостатком является нативная поддержка в Android начиная с версии 3.0. А игнорирование более 80% пользователей [17] c версией 2.x — не вариант. Хотя тут можно посмотреть на сторонний плеер, или заняться собственной реализацией (свободных наработок для поддержки HLS я, увы, не нашёл).

И последним я бы вычеркнул RTSP. Да, это протокол, разработанный специально для видео. Да, его использование идейно верно. Но есть два момента. Во первых — необходимо постоянно обновлять конфигурацию сервера. Во вторых, HTTP открыт всегда и везде, чего нельзя сказать о RTSP/RTP.

Лично я бы остановился на pseudo streaming. Он позволяет осуществлять перемотку и при этом не скачивать весь файл полностью. От нас требуется только немного донастроить web-сервер.

Автор: krestjaninoff


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/android/12311

Ссылки в тексте:

[1] список: http://developer.android.com/guide/appendix/media-formats.html

[2] ffmpeg: http://ffmpeg.org/

[3] сайте одноимённого проекта: https://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide

[4] Baseline Profile: http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles

[5] CABAC: http://en.wikipedia.org/wiki/CABAC

[6] B-Frames: http://en.wikipedia.org/wiki/Video_compression_picture_types

[7] ngx_http_mp4_module: http://nginx.org/en/docs/http/ngx_http_mp4_module.html

[8] протокол: http://en.wikipedia.org/wiki/HTTP_Live_Streaming

[9] VLC: http://wiki.videolan.org/Documentation:Streaming_HowTo/Streaming_for_the_iPhone

[10] localhost/pornofilm.m3u8: http://localhost/pornofilm.m3u8

[11] Протокол: http://en.wikipedia.org/wiki/RTSP

[12] VLC: http://www.videolan.org/doc/streaming-howto/en/ch05.html

[13] RTP: http://en.wikipedia.org/wiki/Real-time_Transport_Protocol

[14] включить: http://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html

[15] найти: http://google.com/search?q=android+multicast+error

[16] VLC Beta: https://play.google.com/store/apps/details?id=org.videolan.vlc.betav7neon

[17] 80% пользователей: http://developer.android.com/about/dashboards/index.html