Screen Sharing
Capture methods, sourceId selection, permissions and common issues per platform.
Screen sharing uses a different mechanism on every platform: browsers go through getDisplayMedia (the user picks a source), desktop apps enumerate sources and pin an id, and mobile platforms go through system screen mirroring or recording extensions. This page gives the exact entry-point method names per platform, and covers permissions and common pitfalls.
Prerequisites
- The SDK for your platform is integrated and token authentication is working.
- Browser: the page runs in a secure context (HTTPS or localhost) and the user gesture starts sharing.
- Desktop: screen-recording permission is granted; mobile: the system screen-mirroring/recording extension is available.
Entry points per platform (method names from the public interface definition)
| Platform | Capture entry point | Stop | Source selection |
|---|---|---|---|
| Web | AgoraRTC.createScreenVideoTrack(config, withAudio) | track.close() | Browser picker; in Electron pin the source with electronScreenSourceId |
| Electron | AgoraRTC.createScreenVideoTrack({ electronScreenSourceId }, 'disable') | track.close() | Enumerate with window.nimeetDesktop.getSources() |
| Android | startScreenCapture(ScreenCaptureParameters) | stopScreenCapture() | MediaProjection consent |
| iOS | startScreenCapture: | stopScreenCapture | ReplayKit Broadcast Upload Extension |
| macOS | startScreenCaptureByDisplayId:regionRect:captureParams: / startScreenCaptureByWindowId:... | stopScreenCapture | getScreenCaptureSourcesWithThumbSize:iconSize:includeScreen: |
| Windows (C++) | startScreenCaptureByWindowId / startScreenCaptureByScreenRect / startScreenCaptureByDisplayId | stopScreenCapture | getScreenCaptureSources |
| Flutter | startScreenCapture(...) | stopScreenCapture() | getScreenCaptureSources() |
| React Native | startScreenCapture(...) | stopScreenCapture() | Native platform capabilities |
| Unity | StartScreenCaptureByDisplayId / StartScreenCaptureByWindowId | StopScreenCapture | GetScreenCaptureSources |
| Unreal (C++) | startScreenCaptureByDisplayId / startScreenCaptureByWindowId | stopScreenCapture | getScreenCaptureSources |
| Unreal (Blueprint) | StartScreenCaptureByDisplayId / StartScreenCaptureByWindowId | StopScreenCapture | GetScreenCaptureSources |
| C# | StartScreenCaptureByWindowId / StartScreenCaptureByScreenRect | StopScreenCapture | GetScreenCaptureSources |
| HarmonyOS | startScreenCapture(parameters) | stopScreenCapture() | System screen-recording permission |
| Cocos Creator | — | — | No public interface definition (see the Cocos page) |
Full signatures (including parameter types) live in the API reference for each platform; do not write them from memory.
Web: the three snippets you need most
import AgoraRTC from '@nimeet/rtc-web';
const client = AgoraRTC.createClient({ mode: 'rtc' });
// 1) Share video only
const screenVideoTrack = await AgoraRTC.createScreenVideoTrack({ encoderConfig: '1080p_1' }, 'disable');
await client.publish(screenVideoTrack);
// 2) Share video and system audio together (Chrome/Edge support this; Safari does not support system audio)
const [videoTrack, audioTrack] = await AgoraRTC.createScreenVideoTrack({ encoderConfig: '1080p_1' }, 'enable');
await client.publish([videoTrack, audioTrack]);
// 3) When the user clicks "Stop sharing" (in the browser toolbar)
screenVideoTrack.on('track-ended', () => {
client.unpublish(screenVideoTrack);
screenVideoTrack.close();
});Always handle track-ended: when the user stops sharing from the browser's native toolbar the track ends by itself, and without a handler you end up "still publishing a dead track".
Electron: pinning the source (the key difference from the browser)
const sources = await window.nimeetDesktop.getSources({ types: ['screen', 'window'] });
const primary = sources.find((source) => source.isScreen);
const track = await AgoraRTC.createScreenVideoTrack({ electronScreenSourceId: primary.id }, 'disable');
await client.publish(track);Once electronScreenSourceId is pinned, switching the foreground window does not change what is being shared — this is the behaviour you need for "share a specific PPT window".
Mobile: a different permission model
- Android: requires the
MediaProjectionconsent dialog plus a foreground service (from Android 14 onwards theFOREGROUND_SERVICE_MEDIA_PROJECTIONtype is also required). Without a foreground service, moving to the background gets the capture reclaimed immediately. - iOS: direct screen recording is not possible; a Broadcast Upload Extension must capture the sample buffers, and the app and the extension share state through an App Group. The user starts and stops the broadcast from Control Center.
- HarmonyOS: uses the system screen-recording permission, which must be declared in
module.json5and requested at runtime.
Choosing encoder parameters
| Scenario | Recommendation |
|---|---|
| Documentation and code demos | 1080p / 15fps / prefer sharpness (degradationPreference: 'maintain-resolution') |
| Video playback demos | 1080p / 30fps / raise the bitrate ceiling |
| Poor networks | 720p / 15fps / prefer frame rate |
| Low-bandwidth mobile clients | Subscribe to the low stream only for screen sharing (see Stream control and fallback) |
Common issues
| Symptom | Cause | Handling |
|---|---|---|
| The shared content is black | The user picked a protected window (DRM or a private page) | Switch to the whole screen or to a visible window |
| The macOS source list is empty | The "Screen Recording" permission was not granted | Grant it in System Settings and restart the app |
| The other side sees a mirrored or upside-down image | The screen track is handled as a camera track | Check whether the render layer distinguishes by sourceType |
| Sharing system audio fails | Safari/Firefox do not support it | Share video only and route audio through the microphone |
| Frame rate collapses while sharing | Resolution too high plus a poor network | Lower encoderConfig or enable the low stream |
| Still sending after sharing has stopped | track-ended was not handled / unpublish was not called | See snippet 3 above |
Next steps
- REST API reference: complete endpoints, parameters and curl.
- Error codes: locate failures by
code/ HTTP status. - FAQ: quotas, limits and troubleshooting.