屏幕共享
Web/Electron/Android/Windows/HarmonyOS 的采集方式、sourceId 选择、权限与常见问题。
屏幕共享在各端是不同的机制:浏览器走 getDisplayMedia(用户选源),桌面端走「枚举源 + 钉住 id」,移动端走系统投屏/录屏扩展。这一页按端给出确切的入口方法名,并说明权限与常见坑。
前置条件
- 已集成对应平台的 SDK,并完成 Token 鉴权。
- 浏览器:页面处于安全上下文(HTTPS 或 localhost),用户手势触发共享。
- 桌面端:系统屏幕录制权限已授予;移动端:系统投屏/录屏扩展可用。
各端入口(方法名取自公开接口定义)
| 平台 | 采集入口 | 停止 | 选源方式 |
|---|---|---|---|
| Web | AgoraRTC.createScreenVideoTrack(config, withAudio) | track.close() | 浏览器选择器;Electron 里用 electronScreenSourceId 钉住 |
| Electron | AgoraRTC.createScreenVideoTrack({ electronScreenSourceId }, 'disable') | track.close() | window.nimeetDesktop.getSources() 枚举 |
| Android | startScreenCapture(ScreenCaptureParameters) | stopScreenCapture() | MediaProjection 授权 |
| 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() | 平台原生能力 |
| 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() | 系统屏幕录制授权 |
| Cocos Creator | — | — | 无公开接口定义(见 Cocos 页面) |
完整签名(含参数类型)在同端 API 参考里,不要凭记忆写。
Web:最常见的三段代码
import AgoraRTC from '@nimeet/rtc-web';
const client = AgoraRTC.createClient({ mode: 'rtc' });
// 1) 只共享画面
const screenVideoTrack = await AgoraRTC.createScreenVideoTrack({ encoderConfig: '1080p_1' }, 'disable');
await client.publish(screenVideoTrack);
// 2) 同时共享画面 + 系统声音(Chrome/Edge 支持;Safari 不支持系统声音)
const [videoTrack, audioTrack] = await AgoraRTC.createScreenVideoTrack({ encoderConfig: '1080p_1' }, 'enable');
await client.publish([videoTrack, audioTrack]);
// 3) 用户点「停止共享」时(浏览器工具栏)
screenVideoTrack.on('track-ended', () => {
client.unpublish(screenVideoTrack);
screenVideoTrack.close();
});track-ended 一定要处理:用户从浏览器原生工具栏停止共享时,轨道会自己结束,不处理就会出现「还在发布一个死轨道」的状态。
Electron:钉住源(与浏览器的关键区别)
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);钉住 electronScreenSourceId 后,用户切换前台窗口不会改变共享内容——这是「共享某个 PPT 窗口」必须的行为。
移动端:权限模型不同
- Android:需要
MediaProjection授权弹窗 + 前台服务(Android 14 起还要求FOREGROUND_SERVICE_MEDIA_PROJECTION类型)。没有前台服务时,切后台会被立即回收。 - iOS:不能直接录屏,必须由 Broadcast Upload Extension 采集样本缓冲;App 与扩展通过 App Group 共享状态。用户从控制中心开始/停止广播。
- HarmonyOS:走系统屏幕录制授权,需要在
module.json5声明并在运行时请求。
编码参数怎么选
| 场景 | 建议 |
|---|---|
| 文档/代码演示 | 1080p / 15fps / 优先清晰度(degradationPreference: 'maintain-resolution') |
| 视频播放演示 | 1080p / 30fps / 提高码率上限 |
| 弱网 | 720p / 15fps / 优先帧率 |
| 低带宽移动端 | 屏幕共享仅订阅小流(见 大小流与回退) |
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 共享出来是黑屏 | 用户选了受保护窗口(DRM/隐私页) | 换整屏或可见窗口 |
| macOS 源列表为空 | 未授予「屏幕录制」权限 | 系统设置授权后重启应用 |
| 共享后对方看到镜像/倒置 | 屏幕轨道被当成摄像头轨道处理 | 检查渲染层是否按 sourceType 区分 |
| 共享系统声音失败 | Safari/Firefox 不支持 | 只共享画面,音频另走麦克风 |
| 共享期间帧率骤降 | 分辨率过高 + 弱网 | 降低 encoderConfig 或开启小流 |
| 停止共享后还在发送 | 没处理 track-ended / 未 unpublish | 见上面的第 3 段代码 |
下一步
- REST API 参考:完整端点、参数与 curl。
- 错误码:失败时按
code/ HTTP 状态定位。 - 常见问题:配额、限制与排障。