Как добавить скриншаринг в Android-приложение в 2023 году: примеры кода
![Как добавить скриншаринг в ваше Android-приложение [2023] + примеры кода — обложка](https://cdn.prod.website-files.com/647d86e4fc32c113a4d42852/6a4e21691ffc864fae1341d2_6a4e21668bb86bbe42a8040d_skrinsharing-v-android-prilozhenii-2023_v1.png)
Скриншаринг (screen sharing — демонстрация экрана во время видеозвонка) — уже практически базовая функция в платформах для видеосвязи. Skype, WhatsApp, Telegram, Teams, Google Meet — во всех этих сервисах она есть.
Мы уже рассказывали, как реализовать скриншаринг в мобильном iOS-приложении. В этой статье объясним, как сделать это на Android. С примерами кода.
Также в других статьях нашего Android-цикла узнаете, как сделать кастомное уведомление о звонке и в целом, как мы разрабатываем WebRTC-системы для звонков.
Реализация скриншаринга
Скриншаринг можно включить сразу при создании нового видеозвонка — ещё до его начала.
Однако рассмотрим наиболее распространённую ситуацию — включение скриншаринга во время видеозвонка, через N секунд после его начала.
Для упрощения описания реализации скриншаринга допустим, что у нас уже есть готовое приложение с WebRTC звонками. Читайте про реализацию видеозвонка WebRTC подробнее.
Шаги реализации:
- Получение доступа к содержимому экрана
- Создание видеотрека с изображением с экрана
- Замена видеотрека с камеры на видеотрек с экрана
- Отображение уведомления о том, что идёт скриншот
Теперь всё по порядку:
Получение доступа к содержимому экрана
Сначала получим возможность захватывать экран и звук устройства с помощью Media Projection API:
val screenSharingPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
// Handle request result
val screenSharingIntent = result.data
if (screenSharingIntent != null) {
// Success request
}
}
val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val intent = mediaProjectionManager.createScreenCaptureIntent()
screenSharingPermissionLauncher.launch(intent)
При вызове screenSharingPermission Launcher.launch(intent) отобразится диалог, в котором пользователю объяснят, что media projection получит доступ ко всей информации, отображаемой на экране.
В результате успешного получения доступа к содержимому экрана мы получим screenSharingIntent.
Создание видеотрека с изображением с экрана
Создадим videoCapturer, который будет захватывать изображение с экрана:
val mediaProjectionCallback = object : MediaProjection.Callback() {
override fun onStop() {
// screen capture stopped
}
}
val videoCapturer = ScreenCapturerAndroid(screenSharingIntent, mediaProjectionCallback)
Далее создадим localVideoTrack:
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.eglBaseContext)
val videoSource = peerConnectionFactory.createVideoSource(/* isScreencast = */ true)
videoCapturer.initialize(surfaceTextureHelper,context, videoSource.capturerObserver)
videoCapturer.startCapture(displayWidth, displayHeight, fps)
val localVideoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource)
Замена видеотрека с камеры видеотреком с экрана
Для корректной замены видеотрека реализуем логику renegotiation у обоих участников звонка. При изменении локальных медиа треков WebRTC вызывает метод onRenegotiationNeeded. В нем повторяем процесс обмена sdp:
val peerConnectionObserver = object : PeerConnection.Observer {
...
override fun onRenegotiationNeeded() {
// Launch sdp exchange
peerConnection.createOffer(...)
}
}
val peerConnection = peerConnectionFactory.createPeerConnection(iceServers, peerConnectionObserver)
Приступим к замене видеотрека. Удалим из локального медиа-потока видеотрек с камеры:
localMediaStream.removeTrack(cameraVideoTrack)
Остановим захват видео с камеры:
cameraVideoCapturer.stopCapture()
Добавим видеотрек скриншаринга:
localMediaStream.addTrack(screenVideoTrack)
Отображение уведомления о текущем скриншаринге
При запуске скриншара нужно запустить Foreground Service с уведомлением о записи экрана.
Создадим ScreencastService и добавим его в AndroidManifest.xml. Также укажем параметр foregroundServiceType:
service
android:name=".ScreencastService"
android:foregroundServiceType="mediaProjection"
android:stopWithTask="true" /
Перед заменой видеотрека с камеры на видеотрек скриншота запустим ScreencastService:
val intent = Intent(this, ScreencastService::class.java)
ContextCompat.startForegroundService(this, intent)
Далее в ScreencastService, например в onStartCommand(), нужно вызвать метод startForeground:
startForeground(NOTIFICATION_ID, notification)
Частые проблемы при реализации
Приложение падает на устройствах с Android 10+ с ошибкой “Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION”
Эта ошибка возникает, когда скриншаринг запускается без фонового сервиса. Начиная с Android 10 система требует, чтобы во время скриншаринга работал фоновый сервис с параметром “android:foregroundServiceType="mediaProjection”».
Foreground Service гарантирует, что система не «убьёт» приложение во время скриншота. Уведомление Foreground Service сообщит пользователю о текущем скриншоте и позволит быстро вернуться в приложение.
Как решить: не забыть показать уведомление о начале скриншаринга :)
Не происходит замена видеотрека с камеры на видеотрек с экрана
Проблема замены видеотрека может возникать, если на стороне одного (или обоих) участников видеозвонка не реализована или реализована неправильно логика renegotiation.
Как решить: переопределить метод onRenegotiationNeeded в PeerConnection.Observer (название метода на других платформах может отличаться). При вызове onRenegotiationNeeded должен запускаться процесс обмена SDP.
Заключение
В этой статье мы рассмотрели реализацию скриншаринга в видеозвонке:
- Получили доступ к содержимому экрана с помощью MediaProjection API
- Захватили содержимое экрана с помощью ScreenCapturerAndroid
- Создали локальный видеотрек с изображением с экрана
- Заменили видеотрек с камеры на видеотрек со скриншота
- Реализовали Foreground Service для отображения уведомления о скриншоте
Посмотреть готовую реализацию скриншаринга можно в нашем приложении.
