Live Streaming on iOS

В нашей работе нередко возникает необходимость реализовать отправку видеопотока с iOS-устройства в реальном – или близком к реальному – времени. Самый частый пример — использование iOS-устройства в качестве камеры слежения или создание стриминговых приложений наподобие Periscope. Как правило, при возникновении подобной задачи ставятся дополнительные условия — например, возможность проигрывания потока на другом устройстве (или в другом приложении) без возникновения лишних проблем в браузере или VLC-плеере, малые задержки (видеопоток должен транслироваться практически в режиме реального времени), низкая нагрузка на устройство (возможность длительной работы от батареи), отсутствие необходимости в специализированном медиасервере для обслуживания передачи потока, и т.п.

Подобная задача сама по себе не нова, но до сих пор не имеет однозначного решения. Точнее, она имеет целый спектр возможных решений:

  • HTTP Live Streaming (HLS)
  • HTTP Dynamic Streaming (HDS)
  • MPEG-DASH
  • fragmented MP4 (fMP4)
  • RTSP
  • WebRTC
  • и т.д.

Каждый подход обладает со своими плюсами и минусами. Например, в одной из наших прошлых, но не потерявшей актуальность, статье Реализация лайв-стриминга снимаемого видео в приложении для iOS, мы рассмотрели возможность генерации на устройстве готового HLS-потока. Это позволяет в принципе избавиться от необходимости в медиасервере, и сразу загружать контент в CDN (например, Amazon S3 + CloudFront). Однако такой, основанный на HLS, подход изначально имеет недостатки (о которых мы расскажем ниже), поэтому в этот раз мы предлагаем вам два новых варианта – генерацию на устройстве готового FMP4-потока и генерацию RTP-стрима с поддержкой локального RTSP-сервера.

HTTP Live Streaming

Протокол HLS появился в 2009 году и довольно быстро добился неплохой популярности. Этому способствовала его полная поддержка в экосистеме Apple и довольно понятная структура – есть текстовый мастер-файл, содержащий список ссылок на кусочки видеопотока, которые постоянно добавляются (в случае online-трансляции). В HLS также была предусмотрена возможность определять сразу несколько потоков для клиентов с разными требованиями к видео (быстрый канал/медленный канал и т.п.). Однако на практике такая двухступенчатая система и необходимость обновлять «мастер-файл» ограничивает сферу применения HLS. Как показывает практика, HLS слабо приспособлен для трансляций в режиме реального времени из-за следующих проблем:

  1. Трансляция нарезается на отдельные файлы небольшой длины (рекомендуется несколько секунд), в результате чего отставание трансляции от режима реального времени заложено в самой идее подобной «нарезки».
  2. При этом стандартная рекомендация по буферизации в видеоплеерах – наличие, как минимум, трех буферизованных сегментов, то есть на уровне плеера задержка из пункта 1 будет утроена;
  3. Кроме того, чтобы плеер узнал о появлении новых сегментов трансляции, ему необходимо регулярно перезапрашивать мастер-файл, что создает еще один фактор задержки (не обновив мастер-файл, плеер попросту не знает, что воспроизводить дальше).
  4. Каждый сегмент (chunk) образован как MPEG-TS файл, что означает существенный оверхед к основному медиаконтенту.

Для уменьшения задержек в HLS вполне естественно уменьшать размеры сегментов до минимума (1 секунда). Однако с такими маленькими размерами файлов на первый план выходят естественные нестабильности в работе сети. И в средних, «естественных» условиях добиться плавности воспроизведения практически невозможно — плеер больше оказывается занят перезапросами мастер-файла и очередного сегмента, чем показом видеопотока.

В попытке решить эти проблемы в 2011-2012 годах были предложены аналогичные подходы – MPEG-DASH от MPEG, Smooth Streaming от Microsoft и HDS от Adobe. Но ни одно из них не стало «стандартом де-факто» (хотя из этих трех MPEG-DASH является полноценным стандартом ISO) и на детальном уровне эти решения страдали теми же недостатками, а решения Adobe и Microsoft требовали, к тому же, особой поддержки на стороне сервера. Тут можно посмотреть таблицу сравнения между этими форматами.

Fragmented MP4

Время шло, и в итоге для простой организации трансляций приобрел популярность несколько другой подход – fMP4 (fragmented MP4). Это было небольшое (но существенное) расширение известного и хорошо поддерживаемого формата MP4, который уже тогда воспроизводился практически везде, поэтому широкая поддержка fMP4 тоже не заставила себя ждать.

Вся разница между обычным файлом MP4 и fMP4 заключается в расположении элементов, описывающих видео- и аудиостримы. В обычном MP4-файле подобные элементы расположены в конце, а в fMP4 – в начале. И так как MP4 изначально мог содержать несколько стримов, поделенных на отдельные блоки данных, – то это несложное изменение позволило создать “бесконечный файл” с точки зрения плеера.

Именно этим свойством fMP4 и пользуются при лайв-стриминге. Плеер, прочитав описание видео- или аудиопотока, начинает ждать данные и показывать (проигрывать) их по мере поступления. И если блоки с кадрами генерировать на лету, плеер автоматически будет проигрывать realtime-стрим без дополнительных усилий.

И это действительно работает! Конечно, при реализации генератора fMP4 могут возникнуть некоторые проблемы. Давайте рассмотрим их на конкретном примере – приложении DemoFMP4.

fMP4 Live Streaming on iOS

Это демонстрационное приложение берет кадры с камеры и отправляет в виде fMP4 любому зрителю, подключившемуся к устройству. Для подключения достаточно запросить у устройства виртуальный файл “MP4”, который устройство будет автоматически генерировать по адресу: http://<ip-адрес>:7000/index.mp4.
Для этого приложение запускает небольшой web-сервер на базе GCDWebServer, который слушает порт 7000 и отвечает на запросы на загрузку файла index.mp4.

Здесь есть ряд проблем, с которыми можно столкнуться.

  1. Во-первых, видеокамера выдает “сырые” кадры, которые нельзя просто так отправить плееру — они должны быть сжаты и собраны в правильном формате. К счастью, начиная с версии iOS 8.0, компания Apple открыла программный доступ к аппаратному сжатию видео, которое умеет генерировать H.264-блоки на лету. Для этого используются семейства функций VTCompressionSessionCreate и VTCompressionSessionEncodeFrame из фреймворка VideoToolbox.
    1. Аналогично сжимается и аудио   функциями семейства AudioConverterNewSpecific / AudioConverterFillComplexBuffer из фреймворка AudoToolbox — в результате чего мы
      получаем блоки данных в формате AAC.
  2. Во-вторых, камера выдает кадры с довольно высокой скоростью. Чтобы не терять их, мы сохраняем кадры в кольцевых буферах (CBCircularData), из которых блоки кадров по мере заполнения отправляются на сжатие. Эти же кольцевые буфера используются для генерации chunked-ответа, поэтому приложение не хранит в памяти больше наперед заданного числа кадров (иначе для бесконечной трансляции потребовался бы бесконечный объем памяти).
  3. В третьих, для корректной работы fMP4 необходимо правильно устанавливать начальные данные потока (включая Sps/Pps) в блоке moov MP4-файла. Для этого приложение просматривает H.264-блоки, которые генерирует hardware-кодировщик, находит очередной ключевой кадр и вытаскивает значения Sps/Pps. А при генерации moov-заголовка использует их, чтобы заставить плеер отсчитывать поток от правильного момента во времени. Таким образом, с точки зрения плеера файл всегда показывается “с самого начала”.
  4. Есть еще одна проблема — формат MP4 имеет свои требования к данным, и не может включать в себя блоки H.264 без правильного оформления. Мы решили эту проблему, подключив замечательную библиотеку Bento4, которая позволяет перепаковывать блоки H.264 в корректные MP4-atoms на лету.

flowchart-livestreaming-ru
Таким образом у нас получилось приложение, способное транслировать стрим с видеокамеры устройства практически без задержек, в реальном времени, отправляя “бесконечный MP4 файл” любому стандартному HTTP-клиенту. Такой подход дает довольно небольшое отставание: 1-2 секунды. В сочетании с широкой поддержкой fMP4 и простотой организации подобной трансляции (не требуется отдельный сервер) – генерация fMP4 на клиенте становится простым и надежным решением.

True Real-Time Live Streaming

Однако как быть, если нам нужен “настоящий реалтайм”, как в Skype? Для таких ситуаций fMP4, увы, уже не очень подходит. Несмотря на отсутствие мастер-файла (как в HLS) и небольшие размеры блоков, на которые делится видеопоток, эти блоки все еще присутствуют внутри MP4-файла. И пока такой блок не будет собран целиком, клиент никак не сможет получить кадры, что неизбежно создаст небольшую задержку при воспроизведении.

RTSP Live Streaming on iOS

Поэтому для полноценного реалтайма – когда плеер получает кадр практически сразу после его генерации в камере – больше подходит другой формат, изначально разработанный для стриминга. Речь идет о RTSP, который также обладает хорошей поддержкой среди плееров (к примеру, легко проигрывается популярным плеером VLC) и относительно не сложен в реализации. Рассмотрим пример приложения, которое обеспечивает трансляцию по стандарту RTSP – DemoRTSP.

В отличии от HLS и fMP4, которые полагаются для обмена данными на HTTP-протокол, – RTSP изначально использует свой собственный формат поверх “голых сокетов”. Кроме этого, для работы RTSP требуется два канала — один сигнальный, по которому клиент и сервер обмениваются управляющей информацией, а второй “для данных”, по которому сервер отправляет исключительно сжатые данные. Это немного усложняет схему обмена, однако позволяет добиться минимальных задержек – клиент по определению получает данные сразу после того, как сервер отправляет их по сети. В RTSP просто не предусмотрено никаких промежуточных звеньев!

DemoRTSP реализует минимальный набор для такого обмена. При запуске приложение начинает слушать сервисный порт (554) на предмет обращений от клиентов-плееров. При получении обращения, DemoRTSP отправляет в ответ простую строчку с указанием кодека, который будет использован для сжатия видео и аудио (для iOS это стандартная пара H.264/AAC) и номера порта данных, по которому сервер будет отправлять сжатые кадры. После этого клиент-плеер подключается к “порту данных” и проигрывает все, что получает от сервера. В этой схеме сервер никогда ничего не ждет и все сжатые кадры сразу уходят плееру, гарантируя минимальную задержку из возможных.

Надо сказать, что на современных iOS-устройствах сжатие и работа с сетью уже не отнимают много ресурсов. Поэтому, кроме простой трансляции, вполне возможна любого рода дополнительная обработка потока перед отправкой. Например, несложно организовать поверх видео дополнительные оверлеи с информацией, наложить на видео эффект или возможность автоматически менять видео в соответствии с нуждами приложения. Для этого можно использовать как постобработку буфера от камеры, так и более эффективный подход с использованием OpenGL. Остановимся на этом поподробнее.

Live Video Effects on iOS

В нашем демо-приложении DemoRTSP использован весьма простой подход на базе PBVision, но в ваших приложениях вы можете использовать существенно более продвинутые решения на базе фреймворка GPUImage, который позволяет применять к видеопотоку целую цепочку OpenGL эффектов, при этом сведя задержку практически к нулю.
В DemoRTSP можно увидеть пример наложения на стрим простого Blur-эффекта.
Кто сказал, что делать Prisma-like video на лету невозможно!?

Можно пойти еще дальше, и использовать наиболее “быстрый” способ работы с изображениями в iOS на текущий момент — Metal, пришедший на смену OpenGL (в последних версиях iOS). Для примера можно посмотреть в MetalVideoCapture, где показано, как с помощью API-интерфейса CVMetalTextureCache передать результат захвата камеры в Metal Render Pass.

Кроме Metal, iOS последних версий появилась еще несколько интересных возможностей. Одна из них — ReplayKit, позволяющий в несколько строчек организовать трансляцию экрана устройства. Это довольно интересная платформа, однако на текущий момент она не позволяет “вмешиваться” в процесс создания кадра, не позволяет записывать изображение с камеры (только с экрана!), а получение записи ограничено штатными средствами. На текущий момент этот фреймворк не предназначен для полноценного создания управляемых трансляций, и больше похож на “задел на будущее”. Аналогично можно сказать и про другую популярную “новинку”, iOS-поддержку нового языка программирования Swift. К сожалению, он недостаточно хорошо интегрируется с низкоуровневыми возможностями, требуемыми для работы с видео и данными, поэтому в ближайшей перспективе его вряд ли можно будет использовать в подобных задачах (исключая интерфейс).

Ссылки

  1. iOS Live Streaming
  2. HLS
  3. HDS
  4. MPEG-DASH
  5. MP4/fMP4
  6. RTSP
  7. WebRTC
  8. Сравнение HLS и MPEG-DASH
  9. Metal app
  10. ReplayKit
  11. DemoFMP4 и DemoRTSP
  12. VideoToolbox
  13. AudioToolbox
  14. Bento4
  15. GCDWebServer
  16. PBJVision

Реализация лайв-стриминга снимаемого видео в приложении для iOS

Под лайв-стримингом мы понимаем функцию приложения, которая позволяет передавать видео по сети одновременно со съемкой, не дожидаясь полного окончания записи видеофайла. В этой статье мы хотим рассказать о том, как мы подошли к ее решению в ходе разработки Видеокамеры Together для iOS.

Continue reading

Возможности Screen Capture в iOS

Функция Screen Capture, или, попросту говоря, «снятие изображения экрана», в мобильном приложении представляет собой мощный вспомогательный инструмент и может использоваться в самых разнообразных задачах. В этой статье мы подробно рассмотрим механизмы, доступные для реализации данного функционала. Continue reading

HTTP Live Streaming: лучшие рецепты

ios-video-hls

Создание идеальных с технической точки зрения приложений, как правило, является чрезвычайно сложной и трудоемкой задачей. При этом полезная информация зачастую рассеяна по множеству источников. Это относится, в том числе, к разработке видеоприложений для iOS. В данной статье собрана наиболее важная и полезная информация, позволяющая качественно использовать весь спектр возможностей HTTP Live Streaming, а также список первоисточников. Данные материалы будут полезны всем читателям, заинтересованным в создании качественных и удобных для пользователей видеосервисов. Continue reading

HTTP Streaming: балансировка нагрузки

балансировка-http-streaming
Современные системы доставки видеоконтента в большинстве случаев строятся на основе технологии HTTP Streaming. В рамках данной технологии видео доставляется пользователю в виде серии кусочков (чанков) контента по несколько секунд каждый. Наиболее популярными форматами доставки видео являются Apple HTTP Live Streaming и Adobe HTTP Dynamic Streaming. В скором времени, наверное, станет популярен MPEG DASH. Преимуществами данной технологии являются более стабильная работа видеоплатформы при доставке контента через неоднородную публичную сеть Интернет и возможность использования существующих механизмов традиционных CDN сетей (кеширование HTTP чанков). Но часто традиционных средств недостаточно, в крупных онлайн видеосервисах, как правило, требуется более гибкое управление доставкой видеоконтента. В своих проектах мы используем наш Video Load Balancer. В этой статье мы расскажем об основных принципах его работы. Мы надеемся, что они могут принести пользу вашим проектам, независимо от технологии, которая будет использоваться для распределения нагрузки. Continue reading

Воспроизведение видео в iOS приложениях

iOS video player

Мобильные устройства Apple на операционной системе iOS заслуженно снискали всеобщую популярность среди покупателей. За каждым событием компании следят с большим интересом, ожидая появления новых и улучшения существующих функций. Но обычные пользователи могут лишь поверхностно оценивать внешнюю составляющую продукта. Взгляду разработчиков же открывается гораздо больше. Из всей четырехдневной конференции WWDC публично освещается обычно всего один доклад — keynote, кратко рассказывающий об основных нововведениях. Все остальные доклады, а всего их около ста, посвящены разработчикам. Это соотношение позволяет оценить скрытую часть айсберга, которая не менее интересна, чем то, что видно на поверхности. Начиная цикл статей о разработке под iOS, мы расскажем о функциональности встроенных в устройства Apple фреймворков для работы с онлайн-видео. Continue reading