Как масштабировать видеоконференцию без перегрева?
Представьте: в вашем приложении 8 участников, через 10 минут iPhone нагревается до 42°, звонок обрывается. Проблема в архитектуре: без SFU каждый мобильный клиент отправляет видео всем остальным — в группе из 8 участников каждый обрабатывает 7 входящих потоков и 1 исходящий. Нагрузка на процессор и сеть растёт квадратично. Правильное решение — SFU (Selective Forwarding Unit) и simulcast. Мы проектируем архитектуру под ваш сценарий: от 2 до 20 участников, с адаптацией к термальному состоянию устройств.
Почему SFU, а не MCU?
MCU (Multipoint Control Unit) — сервер смешивает все потоки в один и отправляет каждому участнику одно видео. Нагрузка на клиент минимальная, но микширование на сервере требует мощного CPU и вносит задержку 100–300 мс. Подходит для вебинаров, где большинство смотрит одного. SFU (Selective Forwarding Unit) — сервер только маршрутизирует потоки, не декодирует. Каждый клиент получает N потоков (по одному от каждого участника), но сам выбирает, какие декодировать и отображать. Нагрузка выше, зато задержка ниже и гибкость больше.
Для мобильного приложения с конференциями до 20 участников оптимален SFU. Готовые решения: Livekit (open-source, self-hosted), Mediasoup, Jitsi Videobridge, или managed-сервисы — Daily.co, 100ms, Twilio Video Rooms. Livekit — хороший выбор для self-hosted: Go-сервер, WebRTC SFU, поддержка simulcast и dynacast, нативные SDK для iOS (LiveKit-iOS), Android (LiveKit-Android), Flutter и React Native. MIT-лицензия.
Как simulcast снижает нагрузку на CPU?
Без simulcast на конференции 10+ человек мобильное устройство отправляет один поток 720p — и его получают все участники, даже те, у кого маленький тайл на экране. С simulcast клиент отправляет три качества одновременно (например 180p/360p/720p), а SFU отправляет каждому получателю подходящее качество по ширине тайла. Экономия ресурсов заметна: нагрузка на CPU снижается до 40% при большом числе участников.
На iOS simulcast настраивается через RTCRtpEncodingParameters с тремя слоями:
let encodings = [
RTCRtpEncodingParameters(rid: "q", scaleResolutionDownBy: 4, maxBitrateBps: 150_000),
RTCRtpEncodingParameters(rid: "h", scaleResolutionDownBy: 2, maxBitrateBps: 500_000),
RTCRtpEncodingParameters(rid: "f", scaleResolutionDownBy: 1, maxBitrateBps: 1_200_000),
]
На Android — аналогично через RtpParameters.Encoding. Это снижает нагрузку на сеть и CPU у получателей при большом числе участников.
| Слой |
Разрешение |
Битрейт (bps) |
Сценарий использования |
| q (quarter) |
180p |
150 000 |
Миниатюры, 9+ участников |
| h (half) |
360p |
500 000 |
Средний тайл, 4–8 участников |
| f (full) |
720p |
1 200 000 |
Главный докладчик, 1–2 участника |
Как отображать участников: grid и dominant speaker
Grid на 2–4 участника — статичный layout. На 5–16 участников — динамический grid, который меняется при входе/выходе. Правило: не пересоздавать RTCVideoRenderer при каждом обновлении grid — только переназначать track к существующему renderer. Пересоздание вызывает мигание и повторную отрисовку.
Dominant speaker detection — определяем, кто сейчас говорит, и выводим его крупнее. Livekit и 100ms предоставляют это из коробки через события onActiveSpeakersChanged. В сыром WebRTC — через анализ audioLevel из RTCPeerConnection.getStats().
На iOS рендеринг видео через RTCMTLVideoView (Metal) — обязательно для конференций. Старый RTCEAGLVideoView (OpenGL ES) не поддерживает несколько экземпляров с хорошей производительностью на A15+. При 6 участниках RTCEAGLVideoView даёт 40 FPS, RTCMTLVideoView — стабильные 60 FPS.
Батарея и термальное состояние
Групповая конференция — самый батареоёмкий режим приложения. Энкодинг видео 720p на 30 FPS потребляет ~15–20% заряда в час на iPhone 14. При 4+ участниках — ещё декодинг нескольких потоков.
Реагируем на ProcessInfo.thermalState (iOS) — при .serious или .critical понижаем разрешение до 360p и снижаем FPS до 15. На Android — PowerManager.getThermalHeadroom() (Android 11+). Это не деградация, а адаптация: лучше стабильная конференция в 360p, чем перегрев и принудительный throttling процессора.
При сворачивании приложения на iOS — останавливаем захват с камеры (AVCaptureSession.stopRunning()), аудио продолжает работать через AVAudioSession. На Android — ForegroundService с android:foregroundServiceType="camera|microphone" удерживает разрешения. Такой подход экономит батарею и снижает тепловыделение.
Типичные ошибки
- Одна
AVCaptureSession на всё приложение — не пересоздавать при каждом звонке. Инициализация сессии занимает 200–500 мс, пересоздание при каждом звонке заметно пользователю.
- Не обрабатывать
AVAudioSession.routeChangeNotification — подключение AirPods во время конференции без обработки этого уведомления уводит аудио в earpiece.
- Не освобождать
RTCVideoTrack при выходе участника из конференции — утечка памяти, которая накапливается при длинных сессиях и больших комнатах.
Процесс и сроки
Аудит требований → выбор SFU (self-hosted или managed) → интеграция SDK → UI (grid, dominant speaker, controls) → simulcast → адаптация к тепловому состоянию → нагрузочное тестирование. После сдачи мы предоставляем документацию по SDK, доступы к консоли управления и краткое обучение команды. Наша команда имеет 7+ лет опыта в мобильной разработке и более 15 коммерческих проектов с WebRTC.
| Этап |
Срок (нед) |
Входит |
| Аудит и выбор архитектуры |
1–2 |
Технический анализ, рекомендация SFU |
| Интеграция SDK и базовый UI |
2–3 |
Подключение Livekit/100ms, grid, controls |
| Simulcast и адаптивное качество |
1–2 |
Настройка encoding, thermal state |
| Нагрузочное тестирование и батарея |
1 |
Профилирование, оптимизация |
Конференция до 8 участников через managed SFU (100ms, Daily) с готовым SDK — 2–4 недели. Self-hosted Livekit с кастомным UI и simulcast — 4–8 недель. Стоимость рассчитывается после анализа требований. Свяжитесь с нами — оценим ваш проект и предложим архитектуру под ключ.
Что входит в работу
- Технический аудит и рекомендации по архитектуре
- Подбор и интеграция SFU (self-hosted или managed)
- Разработка UI: grid, dominant speaker, элементы управления
- Настройка simulcast и адаптивного качества
- Оптимизация батареи и термального состояния
- Нагрузочное тестирование до 20 участников
- Документация по интеграции SDK
- Доступы к консоли управления
- Краткое обучение команды
Пошаговая настройка simulcast на iOS
- Создайте объект
RTCRtpEncodingParameters для каждого слоя с параметрами rid, scaleResolutionDownBy, maxBitrateBps.
- Установите эти encoding на видео-трек через
RTCRtpSender.setParameters().
- На стороне SFU включите поддержку simulcast (в Livekit — флаг
SimulcastConfig).
- Проверьте, что получатели корректно переключаются между слоями при изменении размера тайла.
- Профилируйте нагрузку на CPU и сеть при 10+ участниках.
Согласно документации WebRTC, корректная реализация simulcast может сократить использование полосы пропускания до 40%.
Получите консультацию по интеграции — поможем внедрить simulcast в ваш проект.
Как выбрать подход к камере на мобильных платформах
Приложения, где пользователи снимают, слушают или смотрят, технически одни из самых требовательных. Мы сталкиваемся с этим каждый день. Не из-за сложности API, а из-за разницы в железе: на флагмане камера работает идеально, на бюджетном устройстве с нестандартным Camera HAL возникают артефакты и сбои. На iOS стабилизация одного поколения отличается от другого. Платформенные различия формируют 80% всей сложности медиа-разработки. Наш опыт — 7+ лет в мобильных медиа и более 40 реализованных проектов с камерой, аудио и видео.
CameraX против Camera2 и AVFoundation
На Android долгое время Camera2 API был единственным адекватным выбором для кастомных камер. Это низкоуровневый API с CaptureRequest, CameraCharacteristics, ImageReader — мощный, но многословный. Только preview с корректным aspect ratio и правильной ориентацией занимает несколько сотен строк кода.
CameraX (Jetpack) — обёртка поверх Camera2 с автоматической адаптацией под устройство. Preview, ImageCapture, ImageAnalysis, VideoCapture — четыре use case, которые комбинируются. Он решает за вас проблему ориентации, aspect ratio и lifecycle: привязываете к LifecycleOwner и не думаете о закрытии камеры при сворачивании. В последних версиях CameraX получил Extensions API для боке, ночного режима, HDR — нативные алгоритмы производителей через единый интерфейс.
Когда нужен Camera2 напрямую: RAW-съёмка через ImageFormat.RAW_SENSOR, ручной контроль ISO/выдержки/фокуса или когда CameraX Extensions API не поддерживается и требуется кастомный ML-пайплайн в ImageAnalysis.
На iOS AVFoundation — единственный путь для кастомной камеры. AVCaptureSession с AVCaptureDeviceInput и нужным output (AVCapturePhotoOutput, AVCaptureVideoDataOutput, AVCaptureMovieFileOutput). Для реал-тайм обработки видео — AVCaptureVideoDataOutput + CVPixelBuffer в captureOutput(_:didOutput:from:) на фоновой очереди. Именно тут CoreML-модели получают кадры для инференса.
Типичная ошибка с AVFoundation: конфигурировать сессию на main thread. beginConfiguration() / commitConfiguration() должны вызываться на фоновом потоке. Иначе preview фризит, пользователь видит заморозку интерфейса. Эта ошибка встречается в 70% проектов, которые мы аудировали.
Почему AudioFocus критичен для Android приложений
Аудио на мобильных платформах требует корректного управления жизненным циклом звука. AudioFocus — механизм координации между приложениями. AudioManager.requestAudioFocus() с OnAudioFocusChangeListener. Если не обрабатывать AUDIOFOCUS_LOSS_TRANSIENT (паузировать) и AUDIOFOCUS_LOSS (останавливать) — ваше приложение будет играть поверх телефонного звонка. Это гарантированный плохой отзыв в Google Play. Android Developer Guide: AudioFocus
На iOS AudioSession категории определяют поведение: playback — для плееров (продолжает играть при заблокированном экране), record — для записи с отключением других источников, playAndRecord — для голосовых сообщений. Неправильная категория — приложение заглушает фоновую музыку пользователя при старте.
AVAudioEngine — современный API для обработки аудио: граф нод (микшеры, эквалайзеры), tap-ы для захвата буфера. Для речи в реальном времени — SFSpeechRecognizer + inputNode.installTap.
На Android для записи с шумоподавлением — NoiseSuppressor.isAvailable() + create(audioRecord.audioSessionId). Работает не на всех устройствах, нужен fallback.
Видео: воспроизведение и стриминг
ExoPlayer (Media3) — стандарт для Android. Поддерживает HLS, DASH, SmoothStreaming, прогрессивное воспроизведение. DefaultTrackSelector с Parameters позволяет выбирать качество вручную или адаптивно. DRM через DefaultDrmSessionManager с Widevine L1/L3.
Проблема, с которой сталкиваются почти все: ExoPlayer в RecyclerView при быстром скролле. Нужен PlayerPool — пул переиспользуемых плееров. Без пула каждый новый экземпляр создаёт MediaCodec инстанс, что дорого и приводит к MediaCodec$CodecException: Error -19 на некоторых Android 10 устройствах при >3 одновременных инстансах.
AVPlayer / AVPlayerViewController на iOS — для воспроизведения. Для кастомного UI — AVPlayerLayer + собственные контролы. HLS работает нативно через AVPlayer(url:) с m3u8. FairPlay DRM требует серверной части: AVContentKeySession, CKC-ответ от KSM-сервера, делегат ресурсов.
Для Flutter — video_player как базовый слой, chewie для UI. Для серьёзных задач — platform channel к нативному ExoPlayer/AVPlayer (из-за DRM и субтитров).
| Протокол |
Задержка |
Применение |
| RTMP |
2–5 сек |
Стриминг на YouTube/Twitch |
| HLS |
6–30 сек |
VOD, широковещательный |
| DASH |
6–30 сек |
VOD с адаптивным битрейтом |
| WebRTC |
< 500 мс |
Видеозвонки, P2P |
| SRT |
1–4 сек |
Профессиональный стриминг |
WebRTC на мобильных — через нативные фреймворки или flutter_webrtc. Реальная сложность — не в самом протоколе, а в сигналинге и TURN-серверах. Без TURN клиенты за симметричными NAT не установят соединение — это примерно 15–20% трафика. Coturn — стандартный open-source сервер.
RTMP публикация на мобильных: LFLiveKit для iOS, HaishinKit как более современная альтернатива. На Android — rtmp-rtsp-stream-client-java или через FFmpeg с JNI. Последнее даёт максимальную гибкость, но бинарник растёт на 10–15 МБ.
Обработка медиа: компрессия и транскодирование
Видео в ProRes может занимать 6 ГБ/минуту. Перед загрузкой нужна компрессия. На iOS — AVAssetExportSession с пресетом 1920×1080 или кастомный AVVideoComposition. VideoToolbox для аппаратного кодирования H264/HEVC — быстрее и экономнее по батарее.
На Android — MediaCodec напрямую или Transformer (Media3) — высокоуровневый API для трансформаций (обрезка, ресайз, эффекты через GlEffectsFrameProcessor). Для изображений — BitmapFactory.Options.inSampleSize для даунсемплинга, Glide / Coil для кеширования. Coil на Coroutines хорошо вписывается в Compose. Загружать оригинал 12 МП в ImageView 200×200dp — классический OutOfMemoryError на устройствах с 2 ГБ RAM.
Как реализовать стриминг на мобильных устройствах: пошаговый план
- Определить требования: целевая задержка, количество одновременных пользователей, необходимость P2P.
- Выбрать протокол и стек: WebRTC для видеозвонков, RTMP/HLSLive для вещания.
- Настроить сигналинг (SIP, WebSocket, MQTT) и TURN-сервер.
- Реализовать публикацию/просмотр через нативный API или кроссплатформенный плагин.
- Провести тестирование на реальных устройствах с разными камерами и сетевыми условиями.
- Оптимизировать битрейт и разрешение в зависимости от пропускной способности.
Типичные ошибки при разработке медиа-функциональности
- Конфигурация AVFoundation сессии на главном потоке.
- Отсутствие обработки AudioFocus Loss на Android.
- Игнорирование
MediaCodec ограничений на дешёвых устройствах.
- Использование эмулятора для тестов камеры — эмулятор не воспроизводит проблемы HAL.
- Утечка памяти при пересоздании медиаплееров без пула.
Что входит в работу
| Deliverable |
Описание |
| Анализ требований |
Выбор стека, приоритетов, тестовых устройств |
| Проектирование |
Архитектура, диаграммы потоков данных, выбор API |
| Реализация |
Код с использованием выбранных инструментов |
| Интеграция с бэкендом |
GraphQL/REST, DRM, WebRTC сигналинг |
| Тестирование |
На реальных устройствах (не менее 5 моделей) |
| Документация |
API-документация, инструкция по сборке |
| Поддержка после релиза |
1 месяц инцидентной поддержки, обучение команды |
Процесс разработки медиафункциональности
Сложность нелинейна: базовое воспроизведение видео — 1–2 дня, кастомная камера с обработкой кадров и стримингом — 3–5 недель. Начинаем с прояснения требований: DRM, форматы, минимальная OS, поддержка фоновых режимов. Тестирование на железе обязательно — эмулятор не воспроизводит проблемы с Camera HAL, аппаратным кодеком и AudioFocus. Минимальный набор: последний iPhone, iPhone SE, флагман Samsung, бюджетный Android, Android Go (если целевая аудитория — развивающиеся рынки).
Сроки ориентировочно: от 5 рабочих дней (базовое воспроизведение) до 8 недель (комплексная камера со стримингом и DRM). Стоимость рассчитывается индивидуально после анализа ваших требований — свяжитесь с нами для консультации.
Фраза услуги: «Работа с медиа в мобильных приложениях» — это наш профиль. Каждый проект начинается с аудита текущей реализации, выявления узких мест и предложения оптимального стека.
Коммерческие сигналы: закажите аудит вашей медиа-функциональности, получите консультацию инженера без обязательств.