笔者在最近参与的云游戏项目中,调研了基于 WebRTC 的方案,并实现了一个基于 WebRTC 的 Android 设备屏幕分享的 demo。Demo 地址
WebRTC 是一个免费、开源的,通过简单的 API 提供浏览器和移动应用实时通信功能的项目。目前由 Google 维护支持,致力于让开发者为浏览器、移动平台和物联网设备开发出丰富,高质量的实时通信应用,并且通过共同的协议相互通信。
该项目主要由建立 P2P 连接通道和 Android 设备录屏这两部分组成。
建立 P2P 连接通道
该项目的信令协议参考了 ProjectRTC,并且采用了 ProjectRTC 的信令服务器,运行该项目之前需要先运行 ProjectRTC 的信令服务器。ProjectRTC 地址
信令交换
WebRTC 是基于 P2P 的,但在端与端之间的连接通道还没建立起来之前,我们需要通过一个信令服务器为端与端之间传递信令建立通道。信令服务器要做的东西很简单,就是将一端的信息透传给另一端,步骤如下:我们启动 A 端与 B 端,通过 SocketIO 连接到信令服务器,我们以 A 作为发送端,B 为响应端。
- A 向服务器发出 init 请求
- 服务器将 A 的 init 请求转发给连接上服务器的其他端
- B 收到 init 请求后,调用 peerConnection.createOffer() 方法创建一个包含 SDP 的 offer 信令
- offer 信令创建成功后会调用 SdpObserver 监听中的 onCreateSuccess() 回调方法,在此处 B通过 peerConnection.setLocalDescription() 方法将 SDP 赋予自己的 PeerConnection 对象,同时将 offer 信令发送给服务器
- 服务器将 offer 信令转发给 A 端
- A 收到 offer 信令后,调用 peerConnection.setRemoteDescription() 方法将 B 发过来的 SDP 赋予自己的 PeerConnection 对象,并调用 peerConnection.createAnswer() 方法创建一个 answer 信令
- answer 信令创建成功后同样会调用 SdpObserver 监听中的 onCreateSuccess() 回调方法,在此处 A 同样通过 peerConnection.setLocalDescription() 方法将 SDP 赋予自己的 PeerConnection 对象,同时将 answer 信令发送给服务器
- 服务器将 answer 信令转发给 B 端
- B 收到 A 的 answer 信令后,利用 peerConnection.setRemoteDescription() 方法将 A 发过来的 SDP 赋予自己的 PeerConnection 对象
设置 Candidate
- PeerConnection.Observer 监听会调用 onIceCandidate() 回调方法并提供 IceCandidate 对象。然后将 IceCandidate 对象组成 candidate 信令发送给服务器
- 服务器将 candidate 信令转发给连接上服务器的其他端
- 收到 candidate 信令后调用 peerConnection.addIceCandidate() 回调方法将 IceCandidate 赋予自己的 PeerConnection 对象
至此 Peer-to-Peer 的连接已经建立起来了
Android 设备录屏
录屏功能是基于 MediaProjection 实现的,MediaProjection 是 Android SDK 提供的用于截取屏幕内容和录制系统音频的 API(SDK version 大于等于21)。录屏功能的实现分为以下几步:
- 获取 MediaProjectionManager,并请求开始录屏:
MediaProjectionManager mediaProjectionManager =
(MediaProjectionManager) getApplication().getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_PERMISSION_REQUEST_CODE);
- 获取 MediaProjectionManager 返回码和返回数据:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != CAPTURE_PERMISSION_REQUEST_CODE)
return;
mMediaProjectionPermissionResultCode = resultCode;
mMediaProjectionPermissionResultData = data;
}
- 根据 mediaProjectionPermissionResultData 创建 VideoCapture,用于 WebRTC 创建 VideoSource:
@TargetApi(21)
private VideoCapturer createScreenCapturer() {
if (mMediaProjectionPermissionResultCode != Activity.RESULT_OK) {
report("User didn't give permission to capture the screen.");
return null;
}
return new ScreenCapturerAndroid(
mMediaProjectionPermissionResultData, new MediaProjection.Callback() {
@Override
public void onStop() {
report("User revoked permission to capture the screen.");
}
});
}
网友评论