Реалізація демонстрації екрану (Screen Sharing) у мобільному застосунку
Screen sharing на мобільних платформах — принципово інша задача, ніж на десктопі. iOS та Android не дають застосунку прямий доступ до буфера екрану. Потрібно використовувати спеціальні системні механізми, кожен зі своїми обмеженнями.
iOS: ReplayKit та Broadcast Upload Extension
На iOS захват екрану працює тільки через ReplayKit. Не можна отримати контент екрану напрямки з основного процесу застосунку — це обмеження sandbox.
Для трансляції в реальному часі використовуємо RPSystemBroadcastPickerView (iOS 12+), який показує системний picker для запуску broadcast. Трансляція йде через Broadcast Upload Extension — окремий таргет у проекті, який запускається у власному процесі (com.apple.broadcast-services-upload). Extension та основне застосунок не мають спільної пам'яті — обмін даними тільки через App Group (спільні UserDefaults, спільний FileManager, або Darwin notify для сигналів).
Структура extension:
class SampleHandler: RPBroadcastSampleHandler {
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer,
with sampleBufferType: RPSampleBufferType) {
switch sampleBufferType {
case .video:
// Передаємо CMSampleBuffer у WebRTC/Agora/Livekit
videoSource?.capturer(capturer, didCapture: toRTCVideoFrame(sampleBuffer))
case .audioApp:
// Системний аудіо
case .audioMic:
// Мікрофон (доступен тільки при явному дозволі в Info.plist)
}
}
}
Extension має ліміт пам'яті 50 МБ (на відміну від основного застосунку). Якщо підключити важкий SDK — перевищимо ліміт та отримаємо NSExtensionRequestExpiredException без внятного сообщения про помилку. Agora та Livekit пропонують облегчённі версії SDK спеціально для Broadcast Extension.
Комунікація extension → основне застосунок через CFNotificationCenter (Darwin notifications): extension надсилає сповіщення, основне застосунок встановлює WebRTC peer connection та починає ретранслювати кадри.
Розрішення та FPS обмежені ReplayKit: максимум 1080p, до 60 FPS. На практиці для screen sharing достатньо 720p/15fps — екрани мобільних пристроїв невеликі, та при меншому FPS якість тексту не страждає, зато бітрейт нижче.
Android: MediaProjection API
На Android захват екрану — через MediaProjection API (Android 5.0+). Користувач повинен явно дозволити запис:
val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
REQUEST_MEDIA_PROJECTION
)
У onActivityResult отримуємо Intent з дозволом — з нього створюємо MediaProjection. VirtualDisplay з MediaProjection.createVirtualDisplay() дає Surface, на який система малює контент екрану.
Для передачи через WebRTC використовуємо ScreenCapturerAndroid (з org.webrtc): він обёртує MediaProjection та поставляє кадри у VideoSource.
З Android 10+ при запуску screen capture через ForegroundService потрібен android:foregroundServiceType="mediaProjection" у маніфесті. З Android 14 — додатково явний ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION при старті сервісу через startForeground(). Без цього — SecurityException при спробі запустити ForegroundService з mediaProjection на Android 14+.
При розриві з'єднання MediaProjection.Callback.onStop() викликається — потрібно обробити та сповістити користувача, а не просто логувати. Користувач може завершити screen sharing з шторки сповіщень, та застосунок повинен коректно перейти в режим без демонстрації.
Передача кадрів в real-time
І на iOS, і на Android кадри захопту екрану — це CMSampleBuffer (iOS) або Bitmap/Image через ImageReader (Android). Для передачи по мережі через WebRTC конвертуємо у RTCVideoFrame з YUV420PlanarBuffer (iOS) або JavaI420Buffer (Android). Конвертація YUV з BGRA — дорогая операція, робимо у нативному коді (C++) або через апаратний конвертер.
Для Agora — AgoraRtcKit.startScreenCapture() приймає конфігурацію з contentHint: .text для оптимізації кодека під текстовий контент (менше motion, висока резкість).
Строки
iOS (ReplayKit + Broadcast Extension + WebRTC) — 3–5 днів при готовому сигналингу. Android (MediaProjection + WebRTC) — 2–3 дня. Обидві платформи з коректним lifecycle, фоновим режимом та UX сповіщень — 1–1.5 тижні. Вартість розраховується індивідуально.







