本文档用于指导用户集成Android屏幕分享功能,适用于Android屏幕内容以视频的方式实时分享到会议里,供会议内其他用户订阅查看的应用场景。屏幕分享时可指定视频分辨率、帧率、码流参数,且支持横屏、竖屏分享以及指定屏幕区域的分享。屏幕分享之前需要先加入房间(连接信令服务器、媒体服务器),还需动态申请用户屏幕录制权限。需要注意的是,相较于Android API 28及以下版本,API 29及以上版本(AndroidX)的屏幕分享方式会有所不同,在下文中会做详细的介绍。
iOS | Android | Mac OS | Windows | Electron | 微信小程序 | Chrome 浏览器 |
---|---|---|---|---|---|---|
✓ | ✓ | ✓ | ✓ | ✓ | × | ✓ |
设备屏幕显示的所有内容被实时完整的采集,经过编码后发布到会议里。主要的流程包括:通过 intent 调起屏幕管理服务,初始化RTC SDK屏幕分享模块,传入指定的视频分辨率、帧率后启动屏幕共享。在调起系统的屏幕管理服务后,会弹出屏幕分享权限对话框,在用户选择接受申请时才能开启屏幕共享功能。
在Activity中启动系统屏幕管理服务,调用 startActivityForResult 后会弹出屏幕分享对话框,需要用户接受申请后开能开启分享。
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 1);
在Activity中重载 onActivityResult(int requestCode, int resultCode, Intent data) 方法,在方法体内初始化RTC SDK的屏幕分享模块,并发布屏幕视频。
xxxxxxxxxx
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Room room = Room.obtain("roomId"); // 获取房间对象
/* 已加入过房间 */
if (room != null && room.isWorking()) {
MVideo mVideo = MVideo.getVideo(room); // 获取MVideo对象
mVideo.sendScreenIntent(data); // 初始化RTC SDK屏幕分享模块
MScreen mScreen = MScreen.getScreen(room); // 获取MScreen对象
/* 发布屏幕视频,指定分辨率为720P,帧率为20帧,是否发布视频选择是 */
mScreen.publishedScreens(MScreen.ScreenResolution.SCREEN_720P, 20, true);
}
}
停止发布屏幕视频
xxxxxxxxxx
MScreen screen = MScreen.getScreen(room);
screen.unpublishScreen(MScreen.ScreenType.SCREEN_FULL); // 关闭全屏共享视频
或 screen.unpublishedScreens(); // 关闭所有的屏幕视频(全屏分享和区域分享)
设置屏幕分享的事件通知,会议中用户 "发布/关闭屏幕视频"、"订阅/取消订阅其他用户屏幕视频" 等操作均会触发事件通知。需要注意的是,屏幕事件通知需要在操作屏幕分享之前设置,否则会丢失之前的事件通知。
xMScreen mScreen = MScreen.getScreen(room); // 获取MScreen对象
/* 设置屏幕事件通知 */
mScreen.setListener(new MScreen.Listener() {
public void onScreenStatusNotify(Device.DeviceStatus status, String fromId) {
/* 会议中用户屏幕状态改变事件通知 */
}
public void onScreenDataNotify(int level, String description, String fromId) {
/* 屏幕视频的描述改变事件通知 */
}
public void onPublishScreenNotify(MScreen.ScreenWindow screen) {
/* 发布屏幕视频事件通知 */
}
public void onUnpublishScreenNotify(MScreen.ScreenWindow screen) {
/* 关闭屏幕视频事件通知 */
}
public void onSubscribeResult(int result, String fromId) {
/* 订阅屏幕视频后的事件通知 */
}
public void onUnsubscribeResult(int result, String fromId) {
/* 取消订阅屏幕视频后的事件通知 */
}
});
在收到其他用户屏幕分享通知后(onPublishScreenNotify),需要订阅该用户的屏幕视频,并会异步收到订阅事件通知 ”onSubscribeResult(int result, String fromId)“ ,此方法 result 为 0 代表订阅成功。在订阅成功后,需要对屏幕视频做渲染才能显示这路视频。同理,在收到用户关闭屏幕分享时(onUnpublishScreenNotify),需要停止订阅该屏幕视频并取消订阅这路视频。
xxxxxxxxxx
MScreen mScreen = MScreen.getScreen(room); // 获取MScreen对象
MVideo mVideo = MVideo.getVideo(Room); // 获取MVideo对象
/* 设置屏幕事件通知 */
mScreen.setListener(new MScreen.Listener() {
public void onScreenStatusNotify(Device.DeviceStatus status, String fromId) {
/* 会议中用户屏幕状态改变事件通知 */
}
public void onScreenDataNotify(int level, String description, String fromId) {
/* 屏幕视频的描述改变事件通知 */
}
public void onPublishScreenNotify(MScreen.ScreenWindow screen) {
/* 发布屏幕视频事件通知 */
mScreen.subscribe(screen.getId()); // 订阅视频
}
public void onUnpublishScreenNotify(MScreen.ScreenWindow screen) {
/* 关闭屏幕视频事件通知 */
mScreen.unsubscribe(screen.getId()); // 取消订阅
}
public void onSubscribeResult(int result, String fromId) {
/* 订阅屏幕视频后的事件通知 */
if (result == 0) {
mScreen.attachRender(fromId, videoView.getRender()); // 渲染屏幕视频,videoView:视频渲染窗口
}
}
public void onUnsubscribeResult(int result, String fromId) {
/* 取消订阅屏幕视频后的事件通知 */
if (result == 0) {
mScreen.detachRender(fromId); // 取消渲染视频
}
}
});
暂停/继续 屏幕采集的控制,暂停时共享屏幕视频会卡在最后一帧,继续时会恢复视频。基于私密安全的考虑,当应用从前台切换到后台时,停止屏幕采集,可以避免用户屏幕的私密内容被暴露给会议中其他用户。
xxxxxxxxxx
mScreen.pauseCapture(); // 暂停屏幕采集
mScreen.resumeCapture(); // 继续屏幕采集
Android API 29 以上版本如果导入 AndroidX 来替代Support库,则需要将发布屏幕视频代码(mScreen.publishedScreens)放到前台服务service 的 onStartCommand 方法中执行,而其他部分代码不用修改。
前台服务代码
xxxxxxxxxx
public class MyForegroundService extends Service {
public IBinder onBind(Intent intent) {
return null;
}
public int onStartCommand(Intent intent, int flags, int startId) {
String CHANNEL_ONE_ID = "CHANNEL_ONE_ID";
String CHANNEL_ONE_NAME= "CHANNEL_ONE_ID";
NotificationChannel notificationChannel;
/* API 26 即 Android 8.0 以上的判断 */
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationChannel= new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(notificationChannel);
}
}
PendingIntent pendingIntent= PendingIntent.getActivity(this, 0, intent, 0);
Notification notification= new Notification.Builder(this, CHANNEL_ONE_ID).setChannelId(CHANNEL_ONE_ID)
.setTicker("Nature")
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("屏幕共享的通知消息")
.setContentText("正在进行屏幕共享")
.setContentIntent(pendingIntent)
.build();
notification.flags|= Notification.FLAG_NO_CLEAR;
startForeground(1, notification);
screen.publishedScreens(MScreen.ScreenResolution.SCREEN_720P, 20, true); // 发布共享屏幕视频
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
}
AndroidManifest.xml 文件中注册 MyForegroundService 服务
xxxxxxxxxx
<service
android:name=".service.MyForegroundService"
android:enabled="true"
android:foregroundServiceType="mediaProjection"/>
Activity 中启动服务(发布屏幕共享视频)
xxxxxxxxxx
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0) {
Toast.showToast(this, "共享屏幕权限未通过", false);
return;
}
mVideo.sendScreenIntent(data);
Intent intent = new Intent(this, MyForegroundService.class);
startForegroundService(intent); // 启动前台服务
}
Activity 中停止前台服务
xxxxxxxxxx
stopService(new Intent(this, MyForegroundService.class));
区域屏幕共享是指在开启全屏共享后,在全屏共享的这路视频上裁剪指定的区域,并用于共享一路新的视频。区域共享需要创建区域对象ScaleZone,指定矩形区域的左上角这一点的x/y坐标值,以及区域的宽和高的值,最后再调用区域共享接口发布视频。需要注意的是,区域共享是基于全屏共享的视频数据。因此在区域共享之前,需要先启动全屏共享。
xxxxxxxxxx
ScaleScreenCapturer.ScaleZone scaleZone = new ScaleScreenCapturer.ScaleZone(startX, startY, width, height); // 创建区域对象
mScreen.publishScaleScreen(scaleZone); // 发布区域共享视频
屏幕共享后,会议中其他用户观看屏幕视频出现模糊不清问题:
排查发布端设置的屏幕分辨率是否太低(640P)、配置的视频码流太低(1200kbps以下)导致,可以参考《RTC SDK Android版开发指南》中5.16和5.17章节排查。
排查接收端网络质量是否流畅,接收端观看他人摄像头视频是否有卡顿现象,接收端实时码流是否偏低(小于100kb)。
为了方便排查,也可加入第三方用户用于比对验证屏幕视频是否正常。
屏幕共享后,服务器后台录制视频模糊不清问题:
确认房间其他用户观看视频是否也有相同的模糊不清问题。如果其他用户正常,则排查后台服务配置的录制参数是否有问题;如果其他用户也有类似的模糊问题,则需要发布端排查视频配置(分辨率、码流)是否正常。
屏幕共享后黑屏或视频卡顿:
确认共享屏幕时动态权限申请用户是否通过或者当前是否网络波动断线重连导致;共享屏幕后是否存在编码器异常报错问题,也可将sdk日志提供给我方做进一步的排查。