Android屏幕共享-基于WebRTC实现
WebRTC实践
- WebRTC简介
- 基本流程
- 架构图
- 通话过程
- 名词解释
- 数据示例
- 应用实践
- 架构图
- 模块说明
- P2P连接时序图
- WebRTC的使用
- 业务指令
WebRTC简介
WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。
WebRTC的实现是完全开源的,其核心代码用C++编写,实现了WebRTC标准里定义的API。代码库里包含了Android和iOS/macOS平台的SDK封装,分别提供Java和Objective-C接口,便于这些平台的开发者调用,Windows和Linux平台则可以直接调用WebRTC的C++接口进行开发。
基本流程
架构图
2. 基础概念
- Signaling Servier:用于实现用户的管理,以及WebSocket连接消息。
- ICE Server:用于下发STUN/TURN Server的url、用户名、密码信息。
- TURN Server:ICE协议里的TURN Server,供客户端获取公网IP,也能提供媒体数据中转服务。
客户端会依次访问这3中服务器,完成加入会话、获取TURN Server配置、执行ICE协议过程。
通话过程
步骤1:发起端创建本地PeerConnection(简称PC)对象,并创建offer
步骤2:发起端通过Signaling Server把offer发送到应答端
步骤3:应答端创建本地PC对象,把发起端的offer设置给PC,然后获得answer
步骤4:应答端通过Signaling Server把answer发给发起端
步骤5:发起端把应答端的answer设置给PC
步骤6:两端都收集本地PC的ICE Candidate(包括访问TURN Server),通过Signaling Server发送给对端,对端把ICE Candidate设置给本地的PC
步骤7:两端开始建立P2P的socket,并首发音视频和数据。
名词解释
- offer、answer和SDP
offer、answer都属于SDP(Session Description Protocol),是用于描述连接的多媒体内容的标准,例如分辨率、格式、编解码器、加密等,以便在数据传输时双方都可以相互理解。这本质上是描述内容的元数据,而不是媒体内容本身。
一个典型的SDP消息:
v=0
o=- 2883690436762308021 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video data
a=msid-semantic: WMS
m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 127 97 99 101
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:fj2x
a=ice-pwd:GmiInyo0PRBeJcP+KU6PkmQS
a=ice-options:trickle
a=fingerprint:sha-256 23:AF:7C:FC:03:36:50:93:E2:A4:B7:9F:85:2A:FC:0B:A0:69:F1:67:81:73:10:E8:A4:C3:51:CB:83:2E:2A:4C
a=setup:active
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:100 red/90000
a=rtpmap:127 ulpfec/90000
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
a=ice-ufrag:fj2x
a=ice-pwd:GmiInyo0PRBeJcP+KU6PkmQS
a=ice-options:trickle
a=fingerprint:sha-256 23:AF:7C:FC:03:36:50:93:E2:A4:B7:9F:85:2A:FC:0B:A0:69:F1:67:81:73:10:E8:A4:C3:51:CB:83:2E:2A:4C
a=setup:active
a=mid:data
a=sctpmap:5000 webrtc-datachannel 1024
- ICE
Interactive Connectivity Establishment,是一个实现P2P连接的框架。由于Peer A与Peer B的直连需要绕过防火墙,如果设备没有公共IP,它会提供一个唯一的IP。如果路由器不支持直接与对等方连接,则需要通过服务器中继数据。ICE使用STUN和/或TURN完成此操作。 - STUN
Session Traversal Utilities for NAT,是一种协议,用于发现您的公共地址并确定路由器中任何会阻止与对等方直接连接的限制。客户端将向 Internet 上的 STUN 服务器发送请求,该服务器将回复客户端的公共地址以及客户端是否可以通过路由器的 NAT 访问。
- NAT
Network Address Translation,用于为您的设备提供公共 IP 地址。路由器将有一个公共 IP 地址,连接到路由器的每个设备都将有一个私有 IP 地址。请求将从设备的私有 IP 转换为具有唯一端口的路由器的公共 IP。这样您就不需要为每台设备设置一个唯一的公共 IP,但仍然可以在 Internet 上被发现。 - TURN
Traversal Using Relays around NAT,一些使用 NAT 的路由器采用称为“对称 NAT”的限制。这意味着路由器将只接受来自您之前连接的对等方的连接。通过打开与 TURN 服务器的连接并通过该服务器中继所有信息来绕过对称 NAT 限制。终端将创建与 TURN 服务器的连接,并告诉所有对等方将数据包发送到服务器,然后将其转发给终端。这会带来一些开销,因此只有在没有其他选择的情况下才使用它。
数据示例
offer:
{"from": "JX121110700032_APP","to": "JX121110700032_WEB","message": {"data": {"description": "v=0\r\no=- 578772191408603257 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE video\r\na=msid-semantic: WMS ARDAMS\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 127 97 99 101\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:9dh8\r\na=ice-pwd:+NzqON1cRjJfJ0Sdf9AP2hn5\r\na=ice-options:trickle renomination\r\na=fingerprint:sha-256 32:2D:6D:0B:FF:96:01:88:40:4A:D4:7F:74:82:4D:54:B2:D6:BF:9F:C6:44:7E:75:91:50:85:B3:AB:4A:24:D1\r\na=setup:actpass\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:4 urn:3gpp:video-orientation\r\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtpmap:100 red/90000\r\na=rtpmap:127 ulpfec/90000\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\na=ssrc-group:FID 2579110363 988490045\r\na=ssrc:2579110363 cname:XnDr/Jk7DTRP7WWu\r\na=ssrc:2579110363 msid:ARDAMS ARDAMSv0\r\na=ssrc:2579110363 mslabel:ARDAMS\r\na=ssrc:2579110363 label:ARDAMSv0\r\na=ssrc:988490045 cname:XnDr/Jk7DTRP7WWu\r\na=ssrc:988490045 msid:ARDAMS ARDAMSv0\r\na=ssrc:988490045 mslabel:ARDAMS\r\na=ssrc:988490045 label:ARDAMSv0\r\n","label": 0,"type": "offer"},"type": "offer"}
}
answer:
{"from": "JX121110700032_WEB","to": "JX121110700032_APP","message": {"data": {"description": "v=0\r\no=- 7563495410314281449 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE video\r\na=msid-semantic: WMS\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 127 97 99 101\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:2G/F\r\na=ice-pwd:oUee5+4N4Dzg3bXWOEtMGZIR\r\na=ice-options:trickle\r\na=fingerprint:sha-256 7F:A7:28:67:77:F2:F4:C3:54:69:E8:3C:61:1B:7F:C2:A4:D5:1E:38:DC:86:F5:CB:8A:24:B8:87:25:DE:E6:0A\r\na=setup:active\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:4 urn:3gpp:video-orientation\r\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=recvonly\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=fmtp:98 profile-id=0\r\na=rtpmap:100 red/90000\r\na=rtpmap:127 ulpfec/90000\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\n","type": "answer"},"type": "answer"}
}
candidate:
{"from": "JX121110700032_WEB","to": "JX121110700032_APP","message": {"data": {"candidate": "candidate:369914011 1 udp 2122260223 10.13.159.140 62792 typ host generation 0 ufrag 3y6A network-id 1 network-cost 10","label": 0,"id": "video"},"type": "candidate"}
}
应用实践
架构图
模块说明
MainActivity:App主页,通过Binder与录屏Service和悬浮Service通信
录屏Service:是一个前台服务,用于实现录屏、socket通信、P2P连接,包含VideoCapture、WebSocket、Peer、多个RTC流程模块。
悬浮Service:用于承载悬浮窗,app退出时,仍可悬浮于其他应用之上。
P2P连接时序图
WebRTC的使用
- WebRTC配置
gradle dependencies配置:
implementation "org.java-websocket:Java-WebSocket:1.5.2"implementation files('libs/audio_device_java.jar')implementation files('libs/autobanh.jar')implementation files('libs/base_java.jar')implementation files('libs/libjingle_peerconnection.jar')
混淆配置:
-keep class org.webrtc.** { *; }
权限配置:
<uses-permission android:name="android.permission.INTERNET" />
WebRTC的使用
客户端和服务端通信,采用WebSocket,wss协议。创建WebSocket
String host = "ws://ws.xx.com/server/xx";URI serverURI = URI.create(host);mWebSocketClient = new WebSocketClient(serverURI) {@Override public void onOpen(ServerHandshake handshakedata) {Log.d(TAG, "socket state connect");}@Override public void onMessage(String message) {Log.d(TAG, "web socket onMessage:" + message);try {TestBean bean = new Gson().fromJson(message, TestBean.class);if (bean != null && bean.message != null) {if ("init".equals(bean.message.type)) {Log.d(TAG, "初始化成功");if (!mIsReady) {mIsTerminalEnd = false;mIsReady = true;messageHandler.onMessage("ready", id, payload);}} else if ("answer".equals(bean.message.type)) {Log.d("收到answer");if (bean.message.data != null) {JSONObject payload = new JSONObject();payload.put("type", bean.message.data.type);payload.put("description", bean.message.data.description);messageHandler.onMessage("answer", id, payload);}} else if ("candidate".equals(bean.message.type)) {Log.d("收到candidate");if (bean.message.data != null) {JSONObject payload = new JSONObject();payload.put("id", bean.message.data.id);payload.put("label", bean.message.data.label);payload.put("candidate", bean.message.data.candidate);messageHandler.onMessage("candidate", id, payload);}}}} catch (Exception e) {Log.e(TAG, "WebSocket连接异常:" + e.getLocalizedMessage());}}@Override public void onClose(int code, String reason, boolean remote) {Log.d(TAG, "web socket onClose code:" + code + " reason:" + reason + " remote:" + remote);}@Override public void onError(Exception ex) {Log.d(TAG, "web socket onError:" + ex.getLocalizedMessage());}};mWebSocketClient.connect();
- 创建PeerConnectionFactory
PeerConnectionFactory.initializeAndroidGlobals(mContext, params.videoCodecHwAcceleration);factory = new PeerConnectionFactory(null);
- 初始化音视频
WebScoket建立连接,接收到Web端连接成功的消息后,初始化本地音视频,并开始录屏。
private void initScreenCaptureStream() {mLocalMediaStream = factory.createLocalMediaStream("ARDAMS");mVideoSource = factory.createVideoSource(videoCapturer);// 开始录屏videoCapturer.startCapture(mPeerConnParams.videoWidth, mPeerConnParams.videoHeight, mPeerConnParams.videoFps);// 添加VideoTrackmLocalMediaStream.addTrack(factory.createVideoTrack("ARDAMSv0", mVideoSource));// 添加AudioTrack//AudioSource audioSource = factory.createAudioSource(new MediaConstraints());//mLocalMediaStream.addTrack(factory.createAudioTrack("ARDAMSa0", audioSource));}
- 创建DataChannel
P2P连接建立后,两端通过DataChannel通信,不再依赖WebSocket。收到Web端连接成功的消息后,客户端创建Peer,并创建DataChannel。
public Peer(String id, int endPoint) {Log.d(TAG, "new Peer: " + id + " " + endPoint);this.pc = factory.createPeerConnection(iceServers, mPeerConnConstraints, this);this.dataChannel = pc.createDataChannel("testDataChannel", new DataChannel.Init());this.dataChannel.registerObserver(dataObserver);this.id = id;this.endPoint = endPoint;pc.addStream(mLocalMediaStream); //, new MediaConstraints()
}
DataChannel.Observer dataObserver = new DataChannel.Observer() {@Override public void onBufferedAmountChange(long l) {}@Override public void onStateChange() {}@Override public void onMessage(DataChannel.Buffer buffer) {Charset charset = StandardCharsets.UTF_8;CharBuffer charBuffer = charset.decode(buffer.data);String data = charBuffer.toString();try {JSONObject jsonObject = new JSONObject(data);String type = jsonObject.optString("key");String operate = jsonObject.optString("value");// TODO 执行adb命令... ...} catch (JSONException e) {e.printStackTrace();}}};
业务指令
建立P2P连接后,Web端的操作,通过DataChannel发送事件。客户端通过adb命令模拟点击、触屏、滑动事件。
点击事件:
示例:
客户端响应:adb shell input keyevent 3
触摸事件:
示例:
客户端响应:adb shell input tap 100 200
滑动事件:
示例:
客户端响应:adb shell input swipe 100 200 300 200
注:客户端执行adb命令,需系统签名,否则无权限。
Android屏幕共享-基于WebRTC实现相关推荐
- H5视频会议、WebRtc视频会议--基于webrtc视频通话及屏幕共享
目前基于webrtc的视频通话功能已经成为视频会议市场的主要技术手段. 它也是我们会议系统的重要组成部分. 目前真正能够实现这么的案例源码,非常少. 所以我们开放出来供大家学习 . https://e ...
- 七牛云 RTN:基于 WebRTC 零基础搭建实时音视频平台
近年来,在线教育.狼人杀.在线抓娃娃.线上 KTV 等多人视频互动模式不断涌现,实时音视频通信风头正劲,实时音视频技术 WebRTC 也因此受到了广泛关注.相关数据显示,2017-2021 年期间,全 ...
- 基于 WebRTC 实现自定义编码分辨率发送
导读:2020年如果问什么技术领域最火?毫无疑问:音视频.2020年远程办公和在线教育的强势发展,都离不开音视频的身影,视频会议.在线教学.娱乐直播等都是音视频的典型应用场景. 文|何敬敬 网易云信客 ...
- 使用基于 WebRTC 的 JavaScript API 在浏览器环境里调用本机摄像头
HTML5,JavaScript 和现代浏览器这套三驾马车的组合,使得传统的 Web 应用较之过去能实现更多更丰富的同用户交互的功能.摄像头如今已成为智能手机的标配,前端 Web 应用也出现了越来越多 ...
- Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨
背景 随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求.目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务 ...
- 基于Webrtc和Janus的多人视频会议系统开发1--系统架构
目前业界如教育行业,直播行业,低延迟音视频连麦方案基本采用声网,即构,腾讯等第三方方案,采用第三方方案最大的优点就是接入快捷,可以迅速搭建自己的产品,缺点就是完全受制于第三方,另外费用比较高,公司规模 ...
- 【复】基于 WebRTC 的音视频在线监考模块的设计与实现(上)
文章目录 前言 什么是 WebRTC? WebRTC 架构 WebRTC 通讯内容 WebRTC 通讯协议 WebRTC 连接建立过程 后记 前言 最近在做关于考试系统的项目,其中有一项需求分析是要做 ...
- 基于webrtc的可视对讲系统
基于webrtc的可视对讲系统 本可视对讲会议系统可以提供全球高质量低延迟的实时音视频通讯能力,为项目或开发者提供快速构建语音通话.视频通话.转码.录制等丰富场景功能. 1-系统概述 本系统的主要功能 ...
- html5 视频语音对讲,一种基于WebRTC的多人语音视频通话方法及系统与流程
本发明涉及视频通话领域,特别涉及一种基于WebRTC的多人语音视频通话方法及系统. 背景技术: 随着互联网技术和通信技术的快速发展,人们的交流方式与交流内容得到了极大的丰富和发展.在节奏越来越快的信息 ...
- 基于 WebRTC 的 RTSP 视频实时预览
WebRTC相关视频讲解: 什么是WebRTC WebRTC入门到精通该怎么学? WebRTC框架剖析 音视频流媒体高级开发:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高 ...
最新文章
- 测试发现equals和hashCode与书上描述的不一样
- msfpescan用法
- 实例1 -- 判断输入年份是否为闰年
- 解决vs新建项目是模板丢失的问题
- golang中文文档_【译】Go 语言源码贡献官方指导文档
- 关于nginx,你不可不知的几大特色功能
- CSS3 变换/变形(transform)
- 新手网管升级之路(三)
- JS中的Error对象
- 智慧≥智能+应用 2016智能交通展的科达不等式
- 铝板展开插件_铝板行业排料Rhino插件
- Python下载文件到本地
- Linux中运行shell脚本的几种方式及其区别(source . ./myscript.sh sh myscript.sh)
- 聚石塔服务器 微信,聚石塔云服务器
- 蚂蚁安全键盘空白或应该显示安全键盘却显示的系统键盘的问题(iOS中时间佛历如何转换为公历)
- 如何在简历中使用STAR法则
- 测试代理ip是否有效
- 春风得意的 jQuery
- 亚马逊真的是让我又爱又恨
- IDEA中新导入的项目找不到maven project解决办法
热门文章
- MySQL下载安装教程
- arduino atmega328P MCP4725 proteus 仿真 程序
- [C++] intptr_t
- 拼音模糊查询+java,jquery拼音模糊查询
- ASCII码16进制对照表
- 简单实用的易语言短信接口demo
- java分页及返回数据封装实例
- 如何批量将 Word 文档转为 Svg 格式
- 使用Arduino Pro Mini和BC95-B5连接物联网
- 荣耀盒子显示服务器繁忙错误码7,【沙发管家】华为荣耀盒子的三种解决方式...