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

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



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


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


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

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


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


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


    Для нас это значит, что если мы будем использовать эти фичи при кодировании видео, то 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.


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


    Live streaming


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


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


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

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

    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.


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


    Real Time Streaming Protocol (RTSP)


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


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


    Стоит заметить, что сам протокол RTSP не определяет способ передачи данных, а делегирует это другим протоколам. Например, RTP. Для вещания файла по протоколу 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. Это не совсем так.


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


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


    Однако, как показывает практика, проигрывать multicast видео на Android всё можно. В моём случае с этой задачей удачно справился недавно вышедший VLC Beta для 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% пользователей c версией 2.x — не вариант. Хотя тут можно посмотреть на сторонний плеер, или заняться собственной реализацией (свободных наработок для поддержки HLS я, увы, не нашёл).


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


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

    Ну. И что?
    Реклама
    Комментарии 15
      +3
      без чувства юмору ни куда «pornofilm.mpg» (с)
      Спасибо за статью
        +1
        Если не секрет, что за проект требовал акой задачи?
          0
          Спасибо вам за статью. Простите за то что трачу ваше время, но наболело. В своем приложение для проигрывания видео с сервера я использовал RTSP (раздавалось через wowza), но некоторые устройства (в частности галакси 2 отказывались проигрывать эти видео, в то время как htc sony ericcson, вполне успешно и без прерываний проигрывали видео). Тогда я попробовал псевдо стриминг. Однако время от времени независимо от файла, пользователи на некоторых девайсах могут получаю ошибку воспроизведения видео, я отловил эту ошибку:MediaPlayer: Error (1,-1004). Погуглив выяснил, что это скорее всего проблема I/O и что связана именно с сервером. Но найти решение как устранить проблему возникновения этой ошибки мне до сих пор не удалось. Быть может у вас есть какие-то рекомендации?
            0
            У нас имеется такая же проблема, только видео отдаётся обычным progressive download. И тоже пока не удалось найти решения.
              0
              Сам с этой проблемой не сталкивался, так что решения у меня нет. Можно попробовать поменять web-сервер (взять тот же nginx), поэкспериментировать с настройками раздачи контента, убедится в устойчивости конекта между девайсом и сервером…
              0
              Мы задачу потоковой передачи видео на Android решили следующим образом: Как надёжно доставить видео на Andriod

              Отмечу, что статья изобилует подробностями реализации и некоторыми неточностями ввиду дальнейшего развития проекта.
                0
                скажите, пожалуйста, а у вас не возникало проблем с RTSP. Я сколько не тестировал, на галакси с2 не проигрывался у меня.
                  0
                  С RTSP — нет. В нём главное правильно сформировать SDP-описание, но за вас это делает Wowza, поэтому проблема скорее всего в профиле, которым закодировано видео. Обычный Baseline может не подойти, нужен Constrained Baseline.
                    0
                    спасибо, буду проверять! :)
                0
                Я бы еще упомянул html5, даже несмотря на то, что для этого был бы нужен браузер
                  0
                  Спасибо за статью. К сожалению, у нас ситуация другая: мы используем сторонний сервис, отдающий видео по http и стандартный андроидовый компонент воспроизведения часто теряет поток и отказывается его воспроизводить. Быть может кто сталкивался и поделится рецептом встраивания стороннего плеера в свое приложение?
                    0
                    Из сторонних плееров/фреймворков, которые можно встроить в своё apk при помощи SDK довелось работать с vitamio — vov.io/vitamio/ ( сейчас сайт лежит ).

                    Работает отлично, SDK(набор классов для плеера и остального) распространяется свободно, но, есть одно но. Для его работы нужно устанавливать на устройство vitamio-plugin, который невозможно встроить в приложение и нужно устанавливать из маркета.
                    Ещё один минус — он использует софтовое декодирование, поэтому на слабых смартфонах производительность никакая.

                    В вашем случае я бы грешил не на плеер, потому как обычный прогрессив даунлод вроде как все фреймворки реализовали хорошо, а на сторонний сервис.
                      0
                      Спасибо. Не самый удобный способ, но по крайней мере. С сервиса на компе всё играется без проблем, а на девайсе обрывается.

                      Кстати, а почему все (многие?) сторонние плееры используют софтовое декодировние? Логично испоьзовать софтовый демуксинг, а декодировать аппаратно.
                        0
                        Возможно, что сторонние плееры просто не знают как декодировать аппаратно. Об это же только вендор знает, который готовит родную прошивку и внедряет в неё фреймворк.
                    +1
                    Если я правильно понял, в статье описывается хоть и стриминг, но стриминг файла. А если мне нужно стримить не файл, а именно поток? Ну, к примеру, изображение с камеры наблюдения?

                    Какие будут рекомендации в этом случае?

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое