Skip to main content
Version: 3.x.x

Screen Share - Android

Screen sharing sends your phone's screen to the other participants in the room, for presentations, demos or walkthroughs.

How Screen share works?​

  • Screen sharing works in these steps:

    1. Your app asks the user for permission to capture the screen.
    2. If the user allows it, your app passes the result to enableScreenShare(). If not, nothing is shared.
    3. The SDK starts a foreground service, shows its notification and starts sharing the screen.
    4. The screen share stream arrives on onStreamEnabled(). Add it to a VideoView to display it.

enableScreenShare()​

  • Call room.enableScreenShare(data) with the result Intent of the screen capture prompt to start sharing the screen.

  • enableScreenShare() is a suspend function. Call it from a coroutine, such as lifecycleScope.launch (from androidx.lifecycle:lifecycle-runtime-ktx). It returns once sharing has started, which can take a moment.

  • It throws VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when the screen is already being shared, 3055 (ERROR_OPERATION_IN_PROGRESS) while another screen share call is running, and 3016 (ERROR_GET_DISPLAY_MEDIA_PERMISSION_DENIED) when the permission result can't be used.

  • Called before the room is joined, or while it is reconnecting, enableScreenShare() and disableScreenShare() do nothing and report 3035 (ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED) or 3065 (ERROR_ROOM_RECONNECTING) on onError instead of throwing.

  • You can pass a custom screen share track, created with VideoSDK.createScreenShareVideoTrack(), to enableScreenShare(). Learn more in Custom Screen Share Track.

  • The participant's screen share stream arrives on the onStreamEnabled event of ParticipantEventListener.

  • room.isScreenSharing is true while the local participant's screen share is published.

System Audio with Screen Share​

  • You can share the device's audio along with the screen.

  • enableAudio: Boolean: In the enableScreenShare() method, the second parameter, enableAudio, is a flag that controls whether system audio is shared. Set it to true to include system audio with the screen share, or false to share only the screen without audio. It defaults to false.

  • If you share a custom screen share track, set withAudio = true when you create it with VideoSDK.createScreenShareVideoTrack() instead.

  • Ensure that you request the necessary permissions (android.permission.RECORD_AUDIO) for screen sharing and system audio capture on the user's device. The required permissions may vary based on the Android version and device manufacturer.

  • Devices running Android 9 (API Level 28) or below will not support the System Audio sharing feature.

    note

    System audio is mixed into your microphone track, so it is sent only while the microphone is on, and muting the microphone mutes it too. No separate SHARE_AUDIO stream is sent, so other participants hear it in your microphone stream and getShareAudioStats() returns null for you.

Screenshare permission​

  • A participant’s Screen share stream is provided via the MediaProjection API.

  • Get an instance of the MediaProjectionManager and call the createScreenCaptureIntent() method in an activity. This initiates a prompt dialog for the user to confirm screen projection.

  • The user sees a dialog like this:

user permission

  • After permission is received from the user, you can call enableScreenShare() method.
private fun enableScreenShare() {
val mediaProjectionManager = application.getSystemService(
MEDIA_PROJECTION_SERVICE
) as MediaProjectionManager
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_PERMISSION_REQUEST_CODE
)
}

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != CAPTURE_PERMISSION_REQUEST_CODE) return
if (resultCode == RESULT_OK && data != null) {
lifecycleScope.launch {
try {
// Enabling screen share
room!!.enableScreenShare(data, enableAudio = true) // Enables system audio sharing
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Screen share failed: ${e.code} ${e.name}")
}
}
}
}

Screen share notification​

  • While the screen is being shared, the SDK runs a foreground service and shows its notification: "VideoSDK is sharing your screen".

  • This notification can't be customised. The notificationTitle, notificationContent and notificationIcon meta-data entries in AndroidManifest.xml have no effect.

  • On Android 13 (API level 33) and later, the notification shows in the notification drawer only if your app has the POST_NOTIFICATIONS permission. The screen share works either way.

  • If the user swipes your app away from the recent apps screen, the screen share ends.

disableScreenShare()​

  • Call room.disableScreenShare() to stop sharing the screen.

  • disableScreenShare() is a suspend function. It throws VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when nothing is being shared, and 3055 (ERROR_OPERATION_IN_PROGRESS) while another screen share call is running.

private fun disableScreenShare() {
lifecycleScope.launch {
try {
// Disabling screen share
room!!.disableScreenShare()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stopping screen share failed: ${e.code} ${e.name}")
}
}
}

Events associated with enableScreenShare​

  • On every device in the room, onStreamEnabled() is called on the listener of that Participant, with the new RTCStream. Its kind is MediaType.SHARE.

  • Every Participant, including the one who shares, will receive onPresenterChanged() callback of the Room class with the participantId as presenterId who started the screen share.

Events associated with disableScreenShare​

  • On every device in the room, onStreamDisabled() is called on the listener of that Participant, with the RTCStream that stopped. Its kind is MediaType.SHARE.

  • Every Participant will receive onPresenterChanged() callback of the Room class with the presenterId as null indicating there is no presenter.

private fun setLocalListeners() {
room!!.localParticipant.addEventListener(object : ParticipantEventListener() {
//Callback for when the participant starts a stream
override fun onStreamEnabled(stream: RTCStream) {
if (stream.kind == MediaType.SHARE) {
Log.d("VideoSDK","Share Stream On: onStreamEnabled $stream")
}
}

//Callback for when the participant stops a stream
override fun onStreamDisabled(stream: RTCStream) {
if (stream.kind == MediaType.SHARE) {
Log.d("VideoSDK","Share Stream Off: onStreamDisabled $stream")
}
}
})
}

private val roomEventListener: RoomEventListener = object : RoomEventListener() {
//Callback for when the presenter changes
override fun onPresenterChanged(presenterId: String?) {
if(!TextUtils.isEmpty(presenterId))
{
Log.d("VideoSDK","$presenterId started screen share")
}else{
Log.d("VideoSDK","someone stopped screen share")
}
}
}

Rendering Screen Share Stream​

  • When the local participant shares their screen, onStreamEnabled() of the ParticipantEventListener is called with an RTCStream of kind MediaType.SHARE. Add its track to a VideoView to display it.

  • VideoTrack comes from the videosdk.org.webrtc package.

import videosdk.org.webrtc.VideoTrack

private fun setLocalListeners() {
room!!.localParticipant.addEventListener(object : ParticipantEventListener() {
override fun onStreamEnabled(stream: RTCStream) {
if (stream.kind == MediaType.SHARE) {
// display share video
val videoTrack = stream.track as? VideoTrack
shareView!!.addTrack(videoTrack)
}
}
override fun onStreamDisabled(stream: RTCStream) {
if (stream.kind == MediaType.SHARE) {
shareView!!.removeTrack()
}
}
})
}
  • When a participant shares their screen, the onPresenterChanged() function in the RoomEventListener is triggered on every participant's device, including the sharer's own, providing the presenterId of the screen sharer. room.getActivePresenterId() returns the same ID at any time. It is triggered with null when the share stops. The example below shows another participant's share; your own share arrives on the local participant's onStreamEnabled(), as shown above.
private val roomEventListener: RoomEventListener = object : RoomEventListener() {
override fun onPresenterChanged(presenterId: String?) {
updatePresenter(presenterId)
}
}

//Getting the stream from the participantId
private fun updatePresenter(participantId: String?) {
// find participant
val participant = room!!.participants.get(participantId) ?: return

// find share stream in participant
var shareStream: RTCStream? = null
for (stream: RTCStream in participant.streams.values) {
if (stream.kind == MediaType.SHARE) {
shareStream = stream
break
}
}
if (shareStream == null) return

// display share video
val videoTrack = shareStream.track as? VideoTrack
shareView!!.addTrack(videoTrack)

// listen for share stop event
participant.addEventListener(object : ParticipantEventListener() {
override fun onStreamDisabled(stream: RTCStream) {
if (stream.kind == MediaType.SHARE) {
shareView!!.removeTrack()
}
}
})
}
info
  • Here screenShare stream is displayed using VideoView.
  • To know more about VideoView, please visit here

API Reference​

The API references for all the methods and events utilised in this guide are provided below.

Got a Question? Ask us on discord