Реалізація прямої трансляції з камери мобільного пристрою
Натиснув кнопку — глядачі бачать тебе живцем через 2–3 секунди. За цими секундами: захист з камери, апаратне кодування H.264/H.265, складання в контейнер, відправка на медіа-сервер і доставка до CDN. Кожний етап має своє API і свої способи зломатися.
Захист відео та аудіо
iOS. AVCaptureSession — точка входу. Налаштовуємо встановлення якості (sessionPreset = .hd1280x720), додаємо AVCaptureDeviceInput для камери і мікрофона, додаємо AVCaptureVideoDataOutput і AVCaptureAudioDataOutput з делегатами. captureOutput(_:didOutput:from:) видає CMSampleBuffer — сирі кадри з камери в реальному часі.
Орієнтація: AVCaptureConnection.videoOrientation потрібно оновлювати при повороті пристрою. Якщо цього не робити, глядачі бачать відео набік при запуску трансляції в портреті.
Android. Camera2 API або CameraX (рекомендується). CameraX.bindToLifecycle() з Preview + VideoCapture use case. VideoCapture.output.prepareRecording() — нативний запис. Для стримінга потрібен сирий потік: ImageAnalysis use case з setOutputImageFormat(OUTPUT_IMAGE_FORMAT_YUV_420_888), обробляємо кадри вручну через MediaCodec.
Апаратне кодування
Програмний FFmpeg на телефоні — перегрів і розряджена батарея за 20 хвилин. Використовуємо апаратний кодировщик.
iOS: VideoToolbox — VTCompressionSession. Створюємо сесію з kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder: kCFBooleanTrue. Callback outputCallback отримує CMSampleBuffer з закодованим H.264/H.265.
Важливі параметри:
-
kVTCompressionPropertyKey_RealTime: kCFBooleanTrue— режим реального часу -
kVTCompressionPropertyKey_ProfileLevel: kVTProfileLevel_H264_High_AutoLevel -
kVTCompressionPropertyKey_AverageBitRate: 2_000_000(2 Мбіт/с для 720p) -
kVTCompressionPropertyKey_MaxKeyFrameInterval: 60(keyframe кожні 2 сек при 30 fps)
Android: MediaCodec. MediaFormat.createVideoFormat("video/avc", width, height). configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE). У dequeueOutputBuffer отримуємо закодовані NAL units.
Для аудіо: AudioRecord → MediaCodec з audio/mp4a-latm (AAC-LC). Sample rate 44100 Гц, бітрейт 128 кбіт/с.
Упаковка в RTMP або SRT
З CMSampleBuffer/MediaCodec output отримуємо NAL units H.264 і AAC frames. Потребує упакування в транспортний протокол.
Готові бібліотеки позбавляють від ручної реалізації:
-
iOS:
HaishinKit(Swift) — RTMP, SRT, HLS. Нативний Swift без FFmpeg залежності.RTMPStream,SRTStream. -
Android:
rtmp-rtsp-stream-client-java(Pedro Vicente) — RTMP і RTSP push.RtmpCamera2з CameraX.SRTStreamдля SRT. -
Flutter:
rtmp_streaming(обгортка над нативними бібліотеками) або нативний platform channel. -
Кросплатформа з FFmpeg:
ffmpeg-kit-ios/ffmpeg-kit-android—FFmpegKit.executeAsync("-f avfoundation -i 0:0 -c:v libx264 -preset ultrafast -f flv rtmp://..."). Працює, але більше навантажує CPU ніж апаратний кодировщик.
Адаптивний бітрейт при погіршенні з'єднання
Користувач іде в метро — з'єднання розривається. Без адаптації: буфер переповняється, стрим падає.
Моніторинг: RTMPStream.info.byteCount у HaishinKit, NetworkInfo callback у rtmp-rtsp-stream-client-java — стежимо за швидкістю відправки. Якщо реальна пропускна здатність падає нижче поточного бітрейту:
- Знижуємо
videoBitrate(HaishinKit:stream.videoSettings.bitRate) - Знижуємо framerate з 30 до 15 fps
- Знижуємо розширення з 720p до 480p
Не робимо це занадто часто — максимум кожні 5–10 секунд, інакше якість постійно стрибає.
Превью і UI
Превью з камери — AVCaptureVideoPreviewLayer (iOS) / PreviewView CameraX (Android). Поверх накладаємо оверлей: індикатор «В ефірі», лічильник глядачів, бітрейт і рівень сигналу.
Переключення фронтальна/тильова камера без прерування стрима: iOS — AVCaptureSession.beginConfiguration() → видаляємо старий input → додаємо новий → commitConfiguration(). HaishinKit підтримує в одну рядок: stream.captureSettings.isVideoMirrored.
Терміни
Реалізація стримінга з камери (RTMP або SRT) на одну платформу з превью і базовим UI — 3–5 днів. Кросплатформа з адаптивним бітрейтом, переключенням камер і інтеграцією конкретного медіа-сервера — 1–2 тижні.







