Розроблення відеозвонків у мобільному застосунку
Відеозвонок — це не просто WebRTC-з'єднання. Це управління камерою та мікрофоном, lifecycle на рівні системи, обробка перериваннь (входящий звонок, сповіщення, блокування екрану), корректна робота у фоні та на низькому заряді батареї. Кожна з цих деталей — окрема задача з нетривіальними розв'язаннями на iOS та Android.
Вибір транспорту: WebRTC vs готові SDK
Чистий WebRTC — це RTCPeerConnection, RTCSessionDescription, ICE/STUN/TURN сервери, getUserMedia, сигналинг через WebSocket. Повна реалізація з нуля займає 3–6 тижнів тільки на транспортний шар, без UI.
Більшість проектів вибирають SDK поверх WebRTC: Twilio Video, Daily.co, 100ms, Livekit або Agora. Вони беруть на себе ICE negotiation, codec negotiation (VP8/VP9/H.264/AV1), адаптивний бітрейт, та дають готові нативні обёртки для iOS та Android.
Якщо вимоги до кастомізації невисокі та важлива швидкість запуску — вибираємо SDK. Якщо потрібен повний контроль над кодеком, шифруванням або мінімальним вагом бінарника — чистий WebRTC через GoogleWebRTC (iOS) або org.webrtc (Android).
Нативна реалізація на iOS: AVCaptureSession та CallKit
Відео з камери захоплюється через AVCaptureSession. Правильна ініціалізація:
let session = AVCaptureSession()
session.sessionPreset = .hd1280x720
let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
let input = try AVCaptureDeviceInput(device: camera!)
let output = AVCaptureVideoDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
AVCaptureVideoDataOutput з делегатом дає сирі CMSampleBuffer — передаємо у WebRTC через RTCVideoSource. Важливо: AVCaptureSession.startRunning() викликається строго у фоновому потоці, не на головному — інакше UI замерзне на 200–400 мс.
Переключення фронтальної/основної камери — AVCaptureSession.beginConfiguration() + видалення старого input + додавання нового + commitConfiguration(). Без begin/commit навколо цих операцій — мигання на відео під час переключення.
CallKit — обов'язковий компонент для iOS-застосунків з відеозвонками. Без нього входящий звонок відображається як push-сповіщення, яке легко пропустити. З CallKit — повноекранний системний екран звонку, інтеграція з Bluetooth/AirPods, правильна обробка переривання аудіо. Реалізуємо через CXProvider та CXCallController. VoIP push через PKPushRegistry — єдиний спосіб розбудити застосунок для входящого звонку.
Нативна реалізація на Android: Camera2 та ConnectionService
На Android Camera2 API (CameraManager.openCamera()) дає доступ до ImageReader з форматом YUV_420_888 — стандартний формат для WebRTC на Android. Для більшості задач хватає рівня CameraDevice.StateCallback + CaptureRequest.Builder.
ConnectionService — Android-аналог CallKit. Реєструємо PhoneAccount, через TelecomManager керуємо станом звонку. Без цього на Android 10+ застосунок не може працювати у foreground під час звонку без постійного сповіщення.
Для збереження з'єднання при мініміззції — ForegroundService з типом mediaProjection або phoneCall. На Android 14 це потребує явного вказання в маніфесті: android:foregroundServiceType="camera|microphone".
Управління камерою та мікрофоном
Mute мікрофона — не вимкнення пристрою, а заміна аудіо-трека на тишину. У WebRTC: RTCAudioTrack.isEnabled = false. Повне вимкнення мікрофона на рівні AVAudioSession — ломає echo cancellation.
Flip камери під час звонку — через RTCCameraVideoCapturer.stopCapture() + зміна пристрою + startCapture(with:fps:). Без повної зупинки — крах на деяких пристроях Huawei та Xiaomi через race condition у Camera2.
Поворот екрану — RTCVideoTrack сам по собі не враховує орієнтацію. Потрібно передавати RTCVideoRotation у RTCVideoFrame при захопленні з камери, інакше собеседник бачить повернене на 90° відео на пристроях з portrait-lock.
Якість з'єднання та адаптація
Адаптивний бітрейт — базова функція WebRTC (GCC алгоритм). Але потрібно явно задати діапазон: RTCRtpEncodingParameters з minBitrateBps: 100_000 та maxBitrateBps: 1_500_000. Без обмеження зверху на Wi-Fi WebRTC спробує використовувати 4–8 Мбіт/с — це некомфортно для інших користувачів мережі.
Індикатор якості з'єднання через RTCPeerConnection.getStats() — парсимо RTCInboundRtpStreamStats для framesPerSecond та packetsLost. Втрати > 5% — показуємо попередження користувачу.
Процес
Аудит вимог → вибір транспорту (WebRTC або SDK) → реалізація сигналинга → інтеграція камери/мікрофона → CallKit/ConnectionService → UI (превью локального відео, віддалене відео, кнопки керування) → тестування на реальних пристроях.
Базовий відеозвонок 1-на-1 через SDK (Twilio/Agora/100ms) з управлінням камерою, mute та flip — 1–2 тижні. Чистий WebRTC з сигналингом, CallKit, фоновий режим — 3–5 тижнів. Вартість розраховується після аналізу вимог.







