Робота з медіа в мобільних додатках: камера, аудіо, відео, стриминг
Додатки, де користувачі знімають, слухають або дивляться — технічно одні з найвимогливіших. Не тому що API складні, а тому що залізо різне: на Pixel 7 усе працює, на Redmi Note 8 з нестандартним Camera HAL — уже ні. На iPhone 14 стабілізація працює через VideoToolbox, на iPhone SE 1-го покоління того ж API немає. Ці платформенні різниці визначають 80% складності медіа-розробки.
Камера: CameraX проти Camera2 та AVFoundation
На Android довгий час Camera2 API був єдиним адекватним варіантом для кастомних камер. Це низькорівневий API з CaptureRequest, CameraCharacteristics, ImageReader — потужний, але многословний. Один тільки preview з правильним aspect ratio та орієнтацією займав кілька сотень рядків коду.
CameraX (Jetpack) — обгортка поверх Camera2 з адаптацією під пристрій. Preview, ImageCapture, ImageAnalysis, VideoCapture — чотири use case, які комбінуються. CameraX вирішує за вас проблему орієнтації, aspect ratio та lifecycle: прив'язуєте до LifecycleOwner і не думаєте про те, що камера не закрилася при згортанні. У 2023–2024 роках 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:) на videoDataOutputQueue. Саме тут CoreML-моделі отримують кадри для інференсу.
Типова помилка з AVFoundation: конфігурування сесії на main thread. beginConfiguration() / commitConfiguration() мають викликатися на фоновому потоці. Якщо на main — preview фризить під час налаштування, користувач бачить секундну заморозку інтерфейсу.
Відео: воспроізведення та стриминг
ExoPlayer (тепер Media3 ExoPlayer) — стандарт для Android. Підтримує HLS, DASH, SmoothStreaming, прогресивне воспроізведення. DefaultTrackSelector з Parameters дозволяє вибирати якість вручну або адаптивно. DRM через DefaultDrmSessionManager з Widevine L1/L3.
Проблема, з якою зустрічаються майже всі: ExoPlayer у RecyclerView при швидкому скролі. Потрібен PlayerPool — пул переиспользуємих плеєрів, які переключаються між видимими елементами. Без пула кожен новий ExoPlayer створює MediaCodec інстанс, що дорого та приводить до MediaCodec$CodecException: Error -19 на деяких Android 10 пристроях при >3 одночасних інстансах.
AVPlayer / AVPlayerViewController на iOS — для воспроізведення. Для кастомного UI з контролами — AVPlayerLayer + ваші власні кнопки. HLS стриминг працює нативно через AVPlayer(url:) з m3u8-посиланням. FairPlay DRM потребує серверної частини: AVContentKeySession, CKC-відповідь від KSM-сервера, кастомний AVAssetResourceLoaderDelegate.
Для Flutter — video_player плагін як базовий шар, chewie для готового UI. Для серйозних задач — platform channel до нативного ExoPlayer/AVPlayer, тому що video_player не підтримує DRM та має обмеження по субтитрам.
Аудіо: запис, воспроізведення, фоновий режим
На iOS AudioSession категорії визначають поведінку: playback — для плеєрів (продовжує грати при заблокованому екрані), record — для запису з відключенням інших джерел, playAndRecord — для голосових сообіщень та VoIP. Неправильна категорія — й додаток заглушає фонову музику користувача при старті, що одразу викликає негатив.
AVAudioEngine — сучасний API для обробки аудіо: граф нод (мікшери, еквалайзери, pitch-shifting), tap-и для захоплення аудіобуфера в реальному часі. Для speech recognition у реальному часі — SFSpeechRecognizer + AVAudioEngine.inputNode.installTap.
На Android AudioFocus — механізм координації між додатками. AudioManager.requestAudioFocus() з OnAudioFocusChangeListener. Якщо не обробляти AUDIOFOCUS_LOSS_TRANSIENT (паузувати) та AUDIOFOCUS_LOSS (зупиняти) — ваш додаток буде грати поверх телефонного дзвінка. Це гарантований поганий відзив на Google Play.
Для запису з шумоподавленням на Android — NoiseSuppressor.isAvailable() + NoiseSuppressor.create(audioRecord.audioSessionId). Працює не на всіх пристроях, потрібен graceful fallback.
Стриминг: RTMP, WebRTC, HLS
| Протокол | Затримка | Застосування |
|---|---|---|
| RTMP | 2–5 сек | Стриминг на YouTube/Twitch |
| HLS | 6–30 сек | VOD, широковещательний стриминг |
| DASH | 6–30 сек | VOD з адаптивним бітрейтом |
| WebRTC | < 500 мс | Відеозвонки, стримі P2P |
| SRT | 1–4 сек | Професійний стриминг |
WebRTC на мобільних — через WebRTC.framework (iOS) та libwebrtc.aar (Android) або flutter_webrtc плагін. Реальна складність — не в самому WebRTC, а у сигналінгу та TURN-серверах. Без TURN клієнти за симетричними NAT не встановлять з'єднання — це ≈15–20% трафіку. Coturn — стандартний open-source TURN сервер.
RTMP публікація на мобільних: LFLiveKit для iOS (Swift обгортка, підтримує H264+AAC), HaishinKit як більш сучасна альтернатива. На Android — rtmp-rtsp-stream-client-java або через FFmpeg з JNI-обгорткою. Останнє дає максимальну гнучкість, але розмір бінарника зростає на 10–15 МБ.
Обробка медіа: компресія, трансодування
Відео, знято на iPhone 15 Pro в ProRes, може займати 6 ГБ/хвилину. Перед завантаженням потрібна компресія. На iOS — AVAssetExportSession з AVAssetExportPreset1920x1080 або кастомний AVVideoComposition для тонкої настройки. VideoToolbox для апаратного H264/HEVC кодування напряму — швидше та економніше по батареї, ніж програмний кодек.
На Android — MediaCodec напряму або через Transformer (Media3) — високорівневий API для трансформацій відео без написання encoder-decoder pipeline вручну. Transformer умеє обрізку, ресайз, наложення ефектів через GlEffectsFrameProcessor.
Для зображень на Android — BitmapFactory.Options.inSampleSize для завантаження з даунсемплингом, Glide / Coil для кеширування та трансформацій. Coil написаний на Kotlin Coroutines, добре вписується в Compose. Завантажувати оригінал 12 МП в ImageView 200×200dp — класичний спосіб отримати OutOfMemoryError на пристроях з 2 ГБ RAM.
Процес розробки медіафункціональності
Складність медіа-задач нелінійна: базове воспроізведення відео — 1–2 дні, кастомна камера з обробкою кадрів у реальному часі та стримингом — 3–5 тижнів. Починаємо з прояснення вимог: потрібен ли DRM, які формати, мінімальна версія OS, підтримка фонових режимів.
Тестування на залізі обов'язково — емулятор не відтворює проблеми з Camera HAL, апаратним кодеком та AudioFocus. Мінімальний набір тестових пристроїв: останній iPhone, iPhone SE, флагман Samsung, бюджетний Android (Xiaomi/Redmi), Android Go якщо цільова аудиторія — розвиваючі рики.







