Мы используем cookie, чтобы сайт был лучше для вас.
Внимание
У Вас отключена поддержка Cookie в браузере. Возможно некорректное отображение сайта!

Как мы решаем проблемы с RTMP-to-HLS-стримингом на iOS и Android

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

В предыдущих статьях мы рассказывали, как разработать приложения для стриминга на iOS и Android. А сегодня поделимся, с какими проблемами мы столкнулись в процессе и как их решали.

Используем современную стриминговую платформу

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

Как мы решаем проблемы с RTMP-to-HLS-стримингом на iOS и Android
Стриминг через платформу EdgeЦентр

Единственный минус стриминговой платформы — задержка. Трансляции — довольно сложный, комплексный процесс. Определённая задержка возникает на каждом этапе.

Наши разработчики смогли собрать стабильное, функциональное и быстрое решение, которому требуется 5 секунд для запуска всех процессов, а end-to-end-задержка при вещании в режиме Low latency занимает 4 секунды.

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

Как мы решаем проблемы с RTMP-to-HLS-стримингом на iOS и Android

Запустить трансляцию на стриминговой платформе EdgeЦентр можно за 5 минут. Но для этого у вас должен быть аккаунт. Зарегистрироваться можно, написав нашим менеджерам. У вас будет 14 дней бесплатного пробного периода, чтобы протестировать решение.

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

Уменьшаем размер GOP и ускоряем отправку и приём потока

Чтобы начать декодирование и обработку любого видеопотока, нужен iframe. Мы провели тесты и по их результатам выбрали для своих приложений оптимальный 2-секундный интервал I-кадров. Но в некоторых случаях его можно изменить на 1 секунду. За счёт уменьшения длины GOP декодирование, а значит, и начало обработки потока происходит быстрее.

iOS

Выставляем maxKeyFrameIntervalDuration = 2.

Android

Выставляем iFrameIntervalInSeconds = 2.

Стримим в фоне для поддержания непрерывности трансляции

Если во время стриминга нужны короткие паузы, например для переключения на другое приложение, стриминг можно продолжить в фоне и сохранить целостность видео. При этом мы не тратим время на инициализацию всех процессов и сохраняем минимальную end-to-end-задержку при возвращении в эфир.

iOS

Apple запрещает записывать видео в свёрнутом приложении, поэтому первоначальным решением было отключение камеры в соответствующий момент и подключение её при возвращении в эфир. Для этого мы подписались на оповещение от системы о входе/выходе из background state.

Это не сработало. Соединение не разрывалось, но библиотека не отправляла видео RTMP-потока. Поэтому мы решили внести правки в саму библиотеку.

Каждый раз, когда система отправляет буфер с аудио в AVCaptureAudioDataOutputSampleBufferDelegate, проверяется, отключены ли от сессии все устройства. Подключённым должен оставаться только микрофон. Если всё корректно, создаётся timingInfo. Он несёт информацию о duration, dts и pts фрагмента.

После этого вызывается метод pushPauseImageIntoVideoStream класса AVMixer, в котором идёт проверка на наличие картинки для паузы. А далее через метод pixelBufferFromCGImage создаётся CVPixelBuffer c данными картинки и через метод createBuffer — сам CMSampleBuffer, который отправляется в AVCaptureVideoDataOutputSampleBufferDelegate.

Расширение для AVMixer:

  • hasOnlyMicrophone проверяет, отключены ли от сессии все устройства, кроме микрофона.
  • func pushPauseImageIntoVideoStream берёт данные из буфера с аудио, создаёт буфер с видео и отправляет его в AVCaptureVideoDataOutputSampleBufferDelegate.
  • private func pixelBufferFromCGImage(image: CGImage) создаёт и возвращает CVPixelBuffer из картинки.
  • createBuffer(pixelBuffer: CVImageBuffer, timingInfo: inout CMSampleTimingInfo) создаёт и возвращает CMSampleBuffer из timingInfo и CVPixelBuffer.

В класс AVMixer добавляем свойство pauseImage:

В AVAudioIOUnit добавляем функционал в метод func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection): 

Android

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

Учитывая жизненный цикл компонента, в котором у нас инициализируется трансляция, мы решили инициализировать его во ViewModel — он остаётся живым на протяжении всех жизненных процессов компонента, к которому привязан (Activity, Fragment).

Как мы решаем проблемы с RTMP-to-HLS-стримингом на iOS и Android
Жизненный цикл ViewModel

В жизненном цикле ViewModel ничего не изменится, даже если произойдёт смена конфигурации, ориентации, переход в фон и так далее.

Но небольшая проблема всё-таки есть. Для стриминга нам нужно создать объект RtmpCamera2(), который зависит от объекта OpenGlView. Это элемент UI, а значит, он уничтожится при переходе приложения в фон, и трансляция прервётся.

Решение нашлось быстро. В библиотеке предусмотрена возможность заменять «на лету» View объекта RtmpCamera2. Мы можем заменить его объектом Context нашего приложения. А он живёт, пока приложение не уничтожено системой или пользователь сам его не закрыл.

Считаем индикатором перехода приложения в фон уничтожение объекта OpenGlView, а сигналом к возврату на передний план будет, соответственно, создание этого View. Нам нужно реализовать для этого соответствующий колбэк:

Дальше, как мы уже сказали, нужна замена объекта OpenGlView на Context при переходе в фон и обратно. Для этого во ViewModel мы определяем нужные методы. А ещё нам потребуется остановить трансляцию при уничтожении ViewModel.

А если нам нужно поставить трансляцию на паузу без перехода в фон, мы просто отключаем камеру и микрофон. Битрейт в таком режиме снижается до 70–80 Кбит/с, что даёт возможность не тратить лишний трафик.

Слушаем WebSocket и запускаем плеер в нужное время

Чтобы вовремя получить информацию о готовности контента к воспроизведению и мгновенно запустить трансляцию, используем WebSocket:

Используем адаптивные битрейт и разрешение

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

Как работает адаптивный битрейт
Как работает адаптивный битрейт

iOS

Для реализации адаптивного битрейта используется два метода RTMPStreamDelegate:

Пример реализации:

Адаптивное разрешение настраиваем по соотношению с битрейтом. За основу мы взяли вот такое соотношение:

Разрешение 1920×1080 1280×720 854×480 640×360
Битрейт видео 6 Мбит/c 2 Мбит/c 0,8 Мбит/c 0,4 Мбит/c

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

Android

Здесь для использования адаптивного битрейта нужно изменить реализацию интерфейса ConnectCheckerRtmp:

Подведём итоги

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

Конечно, проблемы при разработке возникают всегда. Мы надеемся, что наши решения помогут вам упростить этот процесс и решить поставленные задачи быстрее.

Подробнее о разработке приложений для стриминга на iOS и Android читайте в наших статьях:

Запускайте трансляции на мобильных устройствах без лишних проблем с помощью нашей стриминговой платформы.

Подпишитесь на полезную рассылку

Выгодные предложения и важные новости раз в месяц. Без спама

Нажимая кнопку «Подписаться», я даю согласие на получение рекламно-информационной рассылки согласно Политике