Реализация HLS-стриминга с мобильного устройства
HLS-стриминг с мобильного — это не то же самое что воспроизведение HLS. Здесь телефон выступает источником: записывает видео сегментами, загружает их на сервер и обновляет m3u8-плейлист. Это менее распространённый сценарий, чем RTMP-стриминг, но нужный для интеграций с CDN-сервисами, которые принимают origin-push через HTTP.
Как работает HLS-push с устройства
Классическая схема: телефон → HTTP PUT/POST TS-сегментов + обновление .m3u8 → origin HTTP сервер → CDN → зрители. Альтернатива: телефон → RTMP → медиасервер → HLS для зрителей (тогда HLS генерирует сервер, не телефон).
Прямой HLS-push с устройства поддерживают: Apple Media Stream Segmenter (только macOS), CloudFront с WebDAV, Akamai Media Services, собственный nginx с ngx_http_dav_module.
iOS: ReplayKit + HLS Segmenter
На iOS нет встроенного HLS-энкодера для live-стриминга — только AVAssetExportSession для файлов. Для live HLS с устройства строим pipeline вручную:
AVCaptureSession → AVCaptureVideoDataOutput → VideoToolbox (encode H.264) → накапливаем NAL units в TS-сегменты по 2-4 секунды → URLSession.uploadTask на сервер → обновляем m3u8 манифест.
Сборка MPEG-TS из NAL units — это ручная упаковка PES пакетов с PAT/PMT таблицами. Готовой библиотеки на Swift нет. Поэтому на практике:
Вариант 1: FFmpegKit. FFmpegKit.executeAsync с HLS output:
-f avfoundation -i 0:0 -c:v h264_videotoolbox -b:v 2M -hls_time 2 -hls_list_size 5 -hls_flags delete_segments -method PUT http://origin/stream/index.m3u8
h264_videotoolbox — аппаратный кодировщик. -method PUT загружает сегменты и плейлист на сервер. Работает, нагружает CPU умеренно.
Вариант 2: RTMP → сервер → HLS. Телефон пушит RTMP через HaishinKit, медиасервер (Nginx-RTMP, MediaMTX) конвертирует в HLS. Надёжнее, но добавляет 1-2 секунды задержки.
Android: MediaMuxer + HLS
Android MediaMuxer поддерживает output format MUXER_OUTPUT_MPEG_4 — это mp4, не TS. Для HLS нужен либо TS (MPEG-2 Transport Stream), либо fMP4 (fragmented MP4, поддерживается HLS v7+).
fMP4 сегменты с MediaMuxer: записываем в ByteArrayOutputStream через кастомный MediaMuxer с MUXER_OUTPUT_MPEG_4, устанавливаем fragmented MP4 флаг через MediaFormat.KEY_IS_ADTS + специфичный для производителя путь. Это нестандартно и ненадёжно.
Надёжный путь: FFmpegKit — тот же подход что на iOS, только для Android. h264_mediacodec вместо h264_videotoolbox:
-f android_camera -i 0 -c:v h264_mediacodec -b:v 2M -hls_time 2 -hls_list_size 5 -method PUT http://origin/stream/index.m3u8
Low-Latency HLS (LL-HLS)
Стандартный HLS — задержка 6-30 секунд. LL-HLS (HLS Part) снижает до 2-4 секунд. Для этого сервер и клиент должны поддерживать EXT-X-PART директивы (Apple WWDC 2019).
С мобильного устройства генерировать LL-HLS части (0.2-0.5 сек part duration) — крайне сложно без специализированного энкодера. Практичнее: устройство пушит RTMP с малым буфером, медиасервер (Nimble Streamer, Wowza с LL-HLS плагином) генерирует LL-HLS для зрителей.
Сценарий применения: запись и одновременный стриминг
Часто нужно: снимаем видео → записываем на устройство + одновременно стримим. AVCaptureSession с двумя output: AVCaptureMovieFileOutput (локальная запись) и AVCaptureVideoDataOutput (захват для стриминга). Оба работают параллельно — процессор телефона тянет оба потока без проблем на современном железе (A15+).
Сроки
Реализация HLS-стриминга с мобильного через FFmpegKit с загрузкой на origin-сервер — 2-3 дня. С кастомным TS-сегментером и LL-HLS — 1-2 недели.







