屏幕共享
更新时间: 2025/09/04 15:21:13
在大型会议或在线教育等场景中,为了满足提升沟通效率的需求,主讲人或老师需要将本端的屏幕内容分享给远端参会者或在线学生观看。NERoom 支持屏幕共享功能,帮助您实时分享本端设备的屏幕内容,满足在线教育、互动娱乐、金融面签、视频会议等低延时高互动场景的需求。
功能介绍
通过 NERoom SDK 可以在视频通话中实现屏幕共享,主播或连麦者可以将自己的屏幕内容,以视频的方式分享给远端参会者或在线观众观看,从而提升沟通效率,一般适用于多人视频聊天、在线会议以及在线教育场景。
-
视频会议场景中,参会者可以在会议中将本地的文件、数据、网页、PPT 等画面分享给其他与会者,让其他与会者更加直观的了解讨论的内容和主题。
-
在线课堂场景中,老师可以通过屏幕共享将课件、笔记、教学内容等画面展示给远端的其他学生观看,降低传统教学模式下的沟通成本,提升教育场景的用户体验。
NERoom SDK 以辅流的形式实现屏幕共享,即单独为屏幕共享开启一路上行的视频流,摄像头的视频流作为主流,屏幕共享的视频流作为辅流,两路视频流并行,主播同时上行摄像头画面和屏幕画面两路画面。
Android 注意事项
- 在开始屏幕共享前,请确保已在您的项目中实现基本的实时音视频功能。
- 您需要先调用
joinRoom
进入房间,再通过joinRtcChannel
加入音视频房间,才能使用屏幕共享功能。 - MediaProjection 等 API 需要 Android API level 21+,使用方法请参考 Google MediaProjection API 文档。
- Android 10 及以后的版本屏幕共享系统要求开启一个前台服务,因此需要在
AndroidManifest.xml
中添加 service,同时将 compileSdkVersion 设置为 29。请根据您的业务需求添加 service。
iOS 注意事项
- 在开始屏幕共享前,请确保已在您的项目中实现基本的实时音视频功能。
- 您需要先调用
joinRoom
进入房间,再通过joinRtcChannel
加入音视频房间,才能使用屏幕共享功能。 - 屏幕共享功能目前仅适用于 iOS 12.0 及以上版本的系统。
- 主 App 进程的屏幕共享任务和系统录屏需使用相同的 App Group 名称。
本端共享屏幕
Android端 配置步骤
- 在
AndroidManifest.xml
文件中添加前台服务ScreenShareService
。 - 通过
MediaProjection
创建ScreenCaptureIntent
请求屏幕共享权限,并将 intent 传递给startActivityForResult()
。 - 在加入房间之后调用
startScreenShare
方法开启屏幕共享,以辅流形式发送屏幕共享内容。调用此方法时,您需要传入请求权限后返回的screenShareResultData
,并设置MediaProjection.Callback()
以接收屏幕共享状态回调。 - 开启屏幕共享后,房间内其他成员会收到
MemberScreenShareStateChanged
回调,通知房间内其他成员关于您屏幕共享状态的变更。 - 若您要结束屏幕共享,请调用
stopScreenShare
方法关闭辅流形式的屏幕共享。
示例代码
dart//需要在 AndroidManifest.xml 中添加前台服务
<service
android:name=".services.ScreenShareService"
android:foregroundServiceType="mediaProjection">
<intent-filter>
<action android:name="com.netease.Yunxin.ScreenShare" />
</intent-filter>
</service>
public class RoomActivity extends AppCompatActivity {
private ScreenShareServiceConnection mServiceConnection;
private ScreenShareService mScreenService;
private class ScreenShareServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
if (service instanceof ScreenShareService.ScreenShareBinder) {
mScreenService = ((ScreenShareService.ScreenShareBinder) service).getService();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mScreenService = null;
}
}
private ActivityResultLauncher<Intent> launcher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
ALog.d(TAG, "onActivityResult,result:" + result.getResultCode());
if (result.getResultCode() == Activity.RESULT_OK) {
mScreenService.startScreenShare(currentRoomContext.getRtcController(), result.getData(),
new MediaProjection.Callback() {
@Override
public void onStop() {
super.onStop();
Log.d(TAG,"屏幕共享结束");
}
}, new NECallback<Unit>() {
@Override
public void onResult(int code, @Nullable String message, @Nullable Unit unit) {
ALog.d(TAG, "code:" + code);
if (code == NEErrorCode.SUCCESS) {
Log.d(TAG,"开始屏幕共享");
}
}
});
}
}
});
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
bindScreenService();
findViewById(R.id.startScreenShare).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
launcher.launch(mediaProjectionManager.createScreenCaptureIntent());
}
});
findViewById(R.id.stopScreenShare).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mScreenService.stopScreenShare(currentRoomContext.getRtcController(), new NECallback<Unit>() {
@Override
public void onResult(int code, @Nullable String message, @Nullable Unit unit) {
Log.d(TAG,"结束屏幕共享");
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindScreenService();
}
private void bindScreenService() {
Intent intent = new Intent();
intent.setClass(this, ScreenShareService.class);
mServiceConnection = new ScreenShareServiceConnection();
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
private void unbindScreenService() {
if (mServiceConnection != null) {
unbindService(mServiceConnection);
}
}
}
iOS 配置步骤
步骤一 创建 App Group
-
在 Certificates, Identifiers & Profiles 页面注册 App Group。
操作步骤请参考 注册 App Group
-
为您的 App ID 启用 App Group 功能。
操作步骤请参考 启用 App Group。
-
重新下载 Provisioning Profile 并配置到 XCode 中。
步骤二 创建 Extension 录屏进程
创建一个类型为 Broadcast Upload Extension 的 Target,用于存放屏幕共享功能的实现代码。
- 在 Xcode 中打开项目的工程文件。
- 在菜单中选择 Editor > Add Target。
- 在 iOS 页签中 选择 Broadcast Upload Extension,并单击 Next。
- 在 Product Name 中为 Extension 命名,然后单击 Finish。
步骤三 引入 NEScreenShareBroadcaster 库
在主工程的 podfile 文件中引入 NEScreenShareBroadcaster 库。
pod 'NEScreenShareBroadcaster', '0.5.0'
步骤四 创建继承自 NEScreenShareSampleHandler 的类
在 extension 中创建继承自 NEScreenShareSampleHandler 的类。
// .h文件内容
#import <ReplayKit/ReplayKit.h>
#import <NEScreenShareBroadcaster/NEScreenShareBroadcaster.h>
@interface SampleHandler : NEScreenShareSampleHandler
@end
// .m内容
@implementation SampleHandler
- (void)setupWithOptions:(NEScreenShareBroadcasterOptions *)options {
options.appGroup = @"your App Group";
options.frameRate = 10;
options.targetFrameSize = CGSizeMake(720, 0);
}
@end
观看远端屏幕共享
注意事项
必须在指定用户开启了屏幕共享辅流通道后,即收到 MemberScreenShareStateChanged
回调后,才能订阅该远端用户的辅流。
配置步骤
- 远端用户加入音视频房间。
- 收到
MemberScreenShareStateChanged
其他用户开启屏幕共享辅流通道的回调。 - 调用
subscribeRemoteSubStreamVideo
方法订阅远端的屏幕共享辅流视频,订阅之后才能接收远端的辅流视频数据。调用此方法时,您需要设置指定用户的 uid。 - 收到
MemberScreenShareStateChanged
其他用户关闭屏幕共享辅流通道的回调,结束屏幕共享。
示例代码
dartif (remoteMember.isSharingScreen()) {
NERoomService roomService = NERoomKit.instance.roomService;
NERoomContext? roomContext = roomService.getRoomContext(roomUuid);
roomContext?.rtcController.subscribeRemoteSubStreamVideo(remoteMember.uuid).then((result){
if(result.isSuccess()){
debugPrint('subscribeRemoteSubStreamVideo success');
}else{
debugPrint('subscribeRemoteSubStreamVideo error code ${result.code},msg:${result.msg}');
}
});
}
关闭某成员的屏幕共享
在会议等场景中,主持人可以关闭某成员的屏幕共享。
您可以在云信控制台上配置某角色是否具备该权限,只有具备该权限的角色才可以执行此操作。
配置步骤
-
调用
stopMemberScreenShare
关闭房间内某成员的屏幕共享。参数 说明 userUuid 待关闭屏幕共享的成员的 uuid。 callback 回调 -
房间内其他成员会收到
MemberScreenShareStateChanged
回调,通知房间内其他成员关于屏幕共享状态的变更。
示例代码
dartNERoomService roomService = NERoomKit.instance.roomService;
NERoomContext? roomContext = roomService.getRoomContext(roomUuid);
roomContext?.rtcController.stopMemberScreenShare(remoteMember.uuid).then((result){
if(result.isSuccess()){
debugPrint('stopMemberScreenShare success');
}else{
debugPrint('stopMemberScreenShare error code ${result.code},msg:${result.msg}');
}
});
监听共享状态示例代码如下:
dartNERoomService roomService = NERoomKit.instance.roomService;
NERoomContext? roomContext = roomService.getRoomContext(roomUuid);
roomContext?.addEventCallback(NERoomEventCallback(
memberScreenShareStateChanged: (member, isSharing, operator){
debugPrint('memberScreenShareStateChanged isSharing:$isSharing');
}
));
API 参考
方法 | 功能描述 |
---|---|
joinRoom |
加入 NERoom 的房间 |
joinRtcChannel |
加入音视频房间 |
startScreenShare |
开始屏幕共享 |
stopScreenShare |
结束屏幕共享 |
MemberScreenShareStateChanged |
成员屏幕共享状态回调 |
stopMemberScreenShare |
关闭房间内某成员的屏幕共享 |