Skip to main content

Screen sharing

Publish your screen with LiveKit.

Overview

LiveKit supports screen sharing natively across all platforms. Your screen is published as a video track, just like your camera. Some platforms support local audio sharing as well.

The steps are somewhat different for each platform:

// The browser will prompt the user for access and offer a choice of screen, window, or tab
await room.localParticipant.setScreenShareEnabled(true);

On iOS, LiveKit integrates with ReplayKit in two modes:

  1. In-app capture (default): For sharing content within your app
  2. Broadcast capture: For sharing screen content even when users switch to other apps

In-app capture

The default in-app capture mode requires no additional configuration, but shares only the current application.

localParticipant.setScreenShare(enabled: true)

Broadcast capture

To share the full screen while your app is running in the background, you'll need to set up a Broadcast Extension. This will allow the user to "Start Broadcast". You can prompt this from your app or the user can start it from the control center.

The full steps are described in our iOS screen sharing guide, but a summary is included below:

  1. Add a new "Broadcast Upload Extension" target with the bundle identifier <your-app-bundle-identifier>.broadcast.
  2. Replace the default SampleHandler.swift with the following:
import LiveKit
#if os(iOS)
@available(macCatalyst 13.1, *)
class SampleHandler: LKSampleHandler {
override var enableLogging: Bool { true }
}
#endif
  1. Add both your main app and broadcast extension to a common App Group, named group.<your-app-bundle-identifier>.
  2. Present the broadcast dialog from your app:
localParticipant.setScreenShare(enabled: true)

On Android, screen capture is performed using MediaProjectionManager:

// Create an intent launcher for screen capture
// This *must* be registered prior to onCreate(), ideally as an instance val
val screenCaptureIntentLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode != Activity.RESULT_OK || data == null) {
return@registerForActivityResult
}
lifecycleScope.launch {
room.localParticipant.setScreenShareEnabled(true, data)
}
}
// When it's time to enable the screen share, perform the following
val mediaProjectionManager =
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
screenCaptureIntentLauncher.launch(mediaProjectionManager.createScreenCaptureIntent())
room.localParticipant.setScreenShareEnabled(true);

On Android, you would have to define a foreground service in your AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
...
<service
android:name="de.julianassmann.flutter_background.IsolateHolderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
</application>
</manifest>

On iOS, follow this guide to set up a Broadcast Extension.

yield return currentRoom.LocalParticipant.SetScreenShareEnabled(true);

Sharing browser audio

Note

Audio sharing is only possible in certain browsers. Check browser support on the MDN compatibility table.

To share audio from a browser tab, you can use the createScreenTracks method with the audio option enabled:

const tracks = await localParticipant.createScreenTracks({
audio: true,
});
tracks.forEach((track) => {
localParticipant.publishTrack(track);
});

Testing audio sharing

Publisher

When sharing audio, make sure you select a Browser Tab (not a Window) and ☑️ Share tab audio, otherwise no audio track will be generated when calling createScreenTracks:

Popup window for choosing to share entire screen, a specific window, or a Chrome tab, with options to share audio and action buttons.

Subscriber

On the receiving side, you can use RoomAudioRenderer to play all audio tracks of the room automatically, AudioTrack or your own custom <audio> tag to add the track to the page. If you don't hear any sound, check you're receiving the track from the server:

room.getParticipantByIdentity('<participant_id>').getTrackPublication('screen_share_audio');