Запись прямой трансляции в мобильном приложении: iOS и Android

Мы часто сталкиваемся с задачей: клиент хочет одновременно транслировать видео на сервер и сохранять локальную копию. Записывать стрим «на лету» — значит параллельно кодировать один видеопоток в два места: на сервер по RTMP/SRT и в локальный файл MP4/MOV. Это не просто «сохранить то, что транслирует

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Запись прямой трансляции в мобильном приложении: iOS и Android
Средний
~3-5 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    896
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1003
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Мы часто сталкиваемся с задачей: клиент хочет одновременно транслировать видео на сервер и сохранять локальную копию. Записывать стрим «на лету» — значит параллельно кодировать один видеопоток в два места: на сервер по RTMP/SRT и в локальный файл MP4/MOV. Это не просто «сохранить то, что транслируется» — у RTMP и файловой записи разные требования к GOP-структуре, битрейту и опорным кадрам. Наша команда предлагает проверенное решение, которое гарантирует синхронность аудио и видео с дельтой не более 40 мс. Оценим ваш проект за 1-2 дня и предложим оптимальную архитектуру. Пишите — поможем избежать типовых ошибок.

Ключевая сложность — аппаратный энкодер VideoToolbox на iOS не может обслуживать два потребителя одновременно. Если подключить к одной VTCompressionSession два AVAssetWriter, получите ошибку -12401. Мы обходим это, используя fanout через CMSampleBuffer: закодированный кадр отправляется и в RTMP-очередь, и в локальный writer. На Android задача решается через MediaCodec с повторным использованием буфера. Подробности — ниже.

Как разделить поток без потери кадров?

На iOS наивный подход — запустить второй AVAssetWriter параллельно с энкодером трансляции. Не работает: VideoToolbox-сессию (VTCompressionSession) нельзя использовать одновременно в двух consumer-ах. При попытке получаете -12401 kVTVideoEncoderNotAvailableNowErr.

Правильный подход: один VTCompressionSession → закодированные CMSampleBuffer → fanout к двум writer-ам. Для этого после получения энкодированного буфера в VTCompressionOutputCallback пишем его и в RTMP-очередь, и в AVAssetWriter.

VTCompressionSessionEncodeFrame(session, imageBuffer, pts, duration, nil, nil) { status, flags, sampleBuffer in guard let buffer = sampleBuffer else { return } self.rtmpQueue.enqueue(buffer) // → стрим self.fileWriterInput.append(buffer) // → локальный файл } 

Оба вызова не должны быть синхронными на одном потоке — если RTMP-очередь заблокирована (сеть упала), fileWriterInput.append не должен ждать. Используем две независимые DispatchQueue. Такой подход в 3 раза стабильнее последовательной записи.

Почему синхронизация аудио и видео — главный вызов?

Типичная проблема: аудио в MP4-файле съезжает относительно видео. Причина — AVAudioEngine и AVCaptureVideoDataOutput работают на разных таймлайнах. CMSampleBuffer из камеры использует kCMClockType_System, аудиобуферы — AVAudioTime с hostTime.

Решение: нормализуем все временные метки относительно CACurrentMediaTime() при старте записи, используем его как базовый clock. Для аудио — AVAudioSourceNode с явным AVAudioTime, для видео — CMSampleBufferGetPresentationTimeStamp минус смещение старта.

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

Запись через ReplayKit: когда оправдано?

Если стрим идёт через ReplayKit (RPBroadcastSampleHandler), запись организуется иначе: обработчик получает RPSampleBufferType.video и RPSampleBufferType.audioApp — их можно параллельно писать в AVAssetWriter без собственного энкодера.

Ограничение: ReplayKit добавляет задержку 2–5 секунд к захвату. Для стрима экрана приемлемо, для камерного стрима — нет. Стоимость разработки с ReplayKit обычно ниже, но качество и задержка хуже прямого захвата.

Управление хранилищем: практические советы

Перед стартом записи проверяем доступное место:

let attrs = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()) let freeSpace = attrs[.systemFreeSize] as? Int64 ?? 0 let estimatedSize = Int64(bitrate / 8) * expectedDurationSeconds guard freeSpace > estimatedSize * 2 else { /* предупреждение */ } 

Коэффициент 2 — запас под временные файлы AVAssetWriter и OS. При 4 Мбит/с час стрима занимает ~1.8 ГБ.

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

Таблица соотношения битрейта и размера файла
Битрейт Длительность Размер файла
2 Мбит/с 1 час ~900 МБ
4 Мбит/с 1 час ~1.8 ГБ
8 Мбит/с 1 час ~3.6 ГБ
Подход Задержка Качество Сложность
Прямой захват <100 мс Оригинальное Высокая
ReplayKit 2–5 с Сжатое Низкая

Как мы реализуем параллельную запись: пошагово

  1. Анализ требований: битрейт, разрешение, необходимость синхронизации A/V.
  2. Выбор стека: Swift 5.9 + VideoToolbox для iOS, Kotlin + MediaCodec для Android.
  3. Настройка VTCompressionSession с параметрами для RTMP и локальной записи.
  4. Реализация fanout-очереди на двух DispatchQueue.
  5. Синхронизация аудио и видео через общий clock.
  6. Интеграция с хранилищем и сегментацией.
  7. Тестирование на реальных устройствах (iPhone 14, Pixel 7, Samsung S23).

Что входит в работу

  • Проектирование архитектуры и выбор оптимизированных параметров кодирования.
  • Реализация параллельной записи с fanout и синхронизацией A/V.
  • Управление хранилищем: проверка места, сегментирование, автоматическая очистка.
  • Интеграция с вашим бэкендом (RTMP-сервер, облачное хранилище).
  • Документация по интеграции и эксплуатации.
  • Поддержка на этапе тестирования и выкладки в App Store / Google Play.

За время работы мы реализовали более 20 проектов с мобильным стримингом, включая записи прямых эфиров для крупных медиа. Гарантируем стабильность записи даже при нестабильной сети. Используем сертифицированные подходы (App Store Review Guidelines Section 4.2). Стоимость разработки варьируется, но инвестиции окупаются за счёт снижения расходов на пост-продакшн и повторные стримы.

Сроки и стоимость

Базовая параллельная запись (iOS, один поток): 1–1.5 недели. Полная реализация с синхронизацией A/V, управлением хранилищем, сегментацией, Android-поддержкой: 3–4 недели. Стоимость рассчитывается индивидуально. Свяжитесь с нами, чтобы получить консультацию и оценку вашего проекта. Закажите разработку под ключ с индивидуальным подходом.