Skip to main content
Version: 3.x.x

Screen Share - Android

This feature enables hosts to start or stop sharing their screen with other hosts and audience members during the live stream. Only hosts (in SEND_AND_RECV mode) can broadcast their screen, while audience members (in RECV_ONLY mode) can view it in real time.

How Screen share works?​

  • The following steps show the flow of the screen sharing in android using VideoSDK:

    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()​

  • By using enableScreenShare() function of Room class, the host can share their mobile screen to other hosts and audience members.

  • 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.

  • Only SEND_AND_RECV mode can publish a screen share. Called by an audience member (RECV_ONLY mode), enableScreenShare() and disableScreenShare() throw VideoSDKException with 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE).

  • Called before the live stream 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.

  • To share the device's audio along with the screen, pass enableAudio = true: enableScreenShare(data, enableAudio = true). It needs Android 10 (API level 29) or later and the RECORD_AUDIO permission. The audio is mixed into the microphone track, so it is sent only while the mic is on, and muting the mic mutes it too.

  • You can pass customised screenshare track in enableScreenShare() by using Custom Screen Share Track. Create it with VideoSDK.createScreenShareVideoTrack(), and set withAudio = true there to share the device's audio.

  • Screen Share stream of the participant can be accessed from the onStreamEnabled event of ParticipantEventListener.

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 prompt 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
liveStream!!.enableScreenShare(data)
} 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()​

  • By using disableScreenShare() function of Room class, the host can stop sharing their mobile screen to other hosts and audience members.

  • 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
liveStream!!.disableScreenShare()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stopping screen share failed: ${e.code} ${e.name}")
}
}
}

Events associated with enableScreenShare​

  • Every participant receives onStreamEnabled() on the sharing Participant's listener, with an RTCStream of kind 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​

private fun setLocalListeners() {
liveStream!!.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","some one stopped screen share")
}
}
}

Rendering Screen Share Stream​

  • When a host participant shares their screen, the onStreamEnabled() function of the ParticipantEventListener is triggered, allowing the RTCStream to be added to a VideoView.

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

import videosdk.org.webrtc.VideoTrack

private fun setLocalListeners() {
liveStream!!.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 another host shares their screen, every participant, the audience included, receives onPresenterChanged() with the presenterId of that host. Use it to find the share stream and display it.
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 = liveStream!!.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()
}
}
})
}

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