Android实现两台手机屏幕共享和远程控制
1 屏幕共享功能介绍
屏幕共享是指在视频通话或互动直播过程中将屏幕内容以视频的方式分享给其他的观众,以增强互动体验,提高沟通效率。屏幕共享解决方案提升了用户实时视频通话的沟通效率。
屏幕共享在如下场景中应用广泛:
- 视频会议场景中,屏幕共享可以将讲话者本地的文件、数据、网页、PPT 等画面分享给其他与会人;
- 在线课堂场景中,屏幕共享可以将老师的课件、笔记、讲课内容等画面展示给学生观看。
2 屏幕共享示例源码下载
请参考 下载示例源码 获取源码。
相关源码请查看 “/ZegoExpressExample/Others/src/main/java/com/example/others/screensharing” 目录下的文件。
others
...
├── screensharing
│ ├── CaptureScreenService.java //此文件实现了系统 Service 接口
│ ├── ScreenSharingActivity.java // 此文件主要完成了通过 ZegoExpress SDK 将屏幕画面数据流推送到远端的工作
│ ├── VideoCaptureScreen.java //此文件用于通过安卓系统接口创建 VirtualDisplay 实例,获取屏幕数据,并发送给 ZEGO Express SDK
│ └── ZegoVideoCaptureCallback.java //此文件实现了 ZegoExpress 的 IZegoCustomVideoCaptureHandler
...
3 屏幕共享功能实现准备工作-集成屏幕共享SDK
在实现屏幕共享功能之前,请确保:
- 已在项目中集成 ZEGO Express SDK,实现基本的实时音视频功能,详情请参考 快速开始 - 集成 和 快速开始 - 实现视频通话。
- 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理 中的“项目信息”。
4 屏幕共享实现流程-即构屏幕共享SDK
我们需要结合 Android 系统 API 和 ZEGO Express SDK 的自定义视频采集来进行屏幕分享。
下图展示了 Android 平台实现屏幕共享的数据流转:
4.1 获取用户录制屏幕授权
在录制屏幕前需要获取用户的授权,不同版本下需要获取的权限如下:
- Android 4.4 及之前版本必须获取到 root 权限后才能实现屏幕录制,由于目前大部分设备的系统版本都高于 4.4,该场景此处不做赘述。
- Android 5.0 及以上版本,可以使用系统提供的 MediaProjection 和 MediaProjectionManager 进行屏幕录制。该版本下可以不获取 root 权限,但会弹窗提示用户是否允许应用录制屏幕,需要用户授权。
- Android 10.0 及以上版本,屏幕录制使用系统 API 时需要用到前台服务,详情请参考 官方文档。
public static MediaProjectionManager mMediaProjectionManager;
if (Build.VERSION.SDK_INT < 21) {
Toast.makeText(ZGVideoCaptureOriginUI.this, getString(R.string.record_request), Toast.LENGTH_SHORT).show();
finish();
} else {
// 5.0及以上版本
// 请求录屏权限,等待用户授权
mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
}
4.2 屏幕共享SDK-创建 MediaProjection 实例
- 在 AndroidManifest.xml 中添加相关配置。
为实现 Android 10.0 及以上版本应用的屏幕录制,需要在代码中开启前台服务,并在 AndroidManifest.xml 中注册 Service,添加 foregroundServiceType 属性。
<application>
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureDemoUI" />
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureOriginUI"></activity>
<service android:name=".service.CaptureScreenService"
android:enabled="true"
android:foregroundServiceType="mediaProjection"/>
</application>
- 用户授权后创建 MediaProjection 实例。
- 对于 Android 10.0 以下版,直接在授权成功后获取 MediaProjection
- 对于 Android 10.0 及以上版本,MediaProjection 实例的创建需要在前台服务的 onStartCommand 方法中执行。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q){
//Target版本高于等于10.0需要使用前台服务,并在前台服务的onStartCommand方法中创建MediaProjection
service=new Intent(ZGVideoCaptureOriginUI.this, CaptureScreenService.class);
service.putExtra("code",resultCode);
service.putExtra("data",data);
startForegroundService(service);
}else {
//Target版本低于10.0直接获取MediaProjection
mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
}
}
}
创建一个类,实现 Service
接口,在 onStartCommand
中创建 MediaProjection
实例。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CaptureScreenService extends Service {
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
···
//在这里获取MediaProjection
ZGVideoCaptureOriginUI.mMediaProjection = ZGVideoCaptureOriginUI.mMediaProjectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
return super.onStartCommand(intent, flags, startId);
}
···
}
4.3 屏幕共享SDK-开启 ZegoExpress SDK 的自定义视频采集功能
调用 ZegoExpress SDK 的 enableCustomVideoCapture 开启自定义采集功能,详情请参考 自定义视频采集。
//VideoCaptureScreen继承IZegoCustomVideoCaptureHandler,用于监听自定义采集onStart和onStop回调
VideoCaptureScreen videoCapture = new VideoCaptureScreen(ZGVideoCaptureOriginUI.mMediaProjection, DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT, mSDKEngine);
//监听自定义采集开始停止回调
mSDKEngine.setCustomVideoCaptureHandler(videoCapture);
ZegoCustomVideoCaptureConfig videoCaptureConfig=new ZegoCustomVideoCaptureConfig();
//使用SurfaceTexture类型进行自定义采集
videoCaptureConfig.bufferType=ZegoVideoBufferType.SURFACE_TEXTURE;
//开始自定义采集
mSDKEngine.enableCustomVideoCapture(true, videoCaptureConfig, ZegoPublishChannel.MAIN);
4.4 屏幕共享SDK-登录房间并开始推流
调用 loginRoom 接口,传入房间 ID 参数 “roomID” 和用户参数 “user”,登录房间。
调用 startPublishingStream 接口,传入流 ID 参数 “streamID”,向远端用户发送本端的音视频流。
/** 创建用户 */
ZegoUser user = new ZegoUser("user1");
/** 开始登录房间 */
mSDKEngine.loginRoom("room1", user);
/** 开始推流 */
mSDKEngine.startPublishingStream("stream1");
4.5 创建 VirtualDisplay 并给 ZEGO Express SDK 发送屏幕数据-
1、创建 ZegoVideoCaptureCallback 类继承 IZegoCustomVideoCaptureHandler。
2、创建 VideoCaptureScreen 类继承 ZegoVideoCaptureCallback。
当收到 onStart 回调后,开发者可以通过 MediaProjection 创建 VirtualDisplay 实例,用于获取屏幕数据,并发送给 ZEGO Express SDK。
3、通过 createVirtualDisplay 系统 API 将虚拟显示器的内容渲染到 Surface。
//ZegoVideoCaptureCallback继承于IZegoCustomVideoCaptureHandler
class VideoCaptureScreen extends ZegoVideoCaptureCallback {
@Override
//当收到onStart回调后,就可以通过MediaProjection创建VirtualDisplay,并给ZEGO SDK塞屏幕数据
public void onStart(ZegoPublishChannel channel) {
if (mZegoEngine != null && !mIsCapturing && mMediaProjection != null) {
mIsCapturing = true;
//通过ZEGO API getCustomVideoCaptureSurfaceTexture获取SurfaceTexture,该接口默认使用主路通道进行推流
SurfaceTexture texture = mZegoEngine.getCustomVideoCaptureSurfaceTexture();
texture.setDefaultBufferSize(mCaptureWidth, mCaptureHeight);
//通过获取的SurfaceTexture创建Surface
mSurface = new Surface(texture);
//通过mSurface,完成将录屏数据塞给ZEGO SDK
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
mCaptureWidth, mCaptureHeight, 1,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
}
}
}
至此,我们已完成采集屏幕数据并通过 ZegoExpress SDK 分享到远端的操作。
5 观看远端屏幕共享-远程控制
完成以上步骤之后,其他用户可以使用 startPlayingStream
接口拉取屏幕共享流,详细步骤可以参考 快速开始。
// 同样的,拉流播放的用户首先需要初始化 SDK 并登陆同一个房间
...
...
// 拉流播放,需传入发起屏幕共享的用户推流时所用的 streamID
mSDKEngine.startPlayingStream(streamID, new ZegoCanvas(playView));
6 获取屏幕共享SDK更多帮助
获取本文的Demo、开发文档、技术支持,访问即构文档中心
近期有开发规划的开发者可上即构官网查看,恰逢即构七周年全线音视频产品1折的优惠,联系商务获取RTC产品优惠;