阅读 677

WebRTC-Android 探索 - 创建 P2P 连接中的那些东西都是啥

在上一篇文章介绍在 Android 中创建简单音视频通话程序时提到了创建连接的流程,其中里边含有 SDP、IceCandidate 等概念。本篇文章将会介绍这些概念都是些啥,切均以 Android 开发者的角度进行阐述(其实是我的 C++ 还正在学习中,具体 Native 我也很多不明白的地方,希望大家多交流 :D )。

创建 P2P 连接的过程

引用我上篇文章的一幅图:


很明显,创建 P2P 连接进行通话无非就是以下流程:

  • 创建 offer 并以信令的方式发送
  • 接受 offer 并创建 answer 且以信令的方式发送
  • 接受 answer
  • 双方以信令的形式交换 candidate
  • 创建 P2P 连接成功,可开始数据交换

其中创建的 offer 和 answer 其实就是 SDP, SDP 其实就是一个字符串,其在 Android 中其实就是由 PeerConnection 的 MediaConstraints 以及之前的 VideoTrack、 AudioTrack 等一些东西决定的,在这些影响下创建初始 SDP 成功时在 SdpObserver#onCreateSuccess 会将编解码相关信息添加进去, setLocalDescription 和 setRemoteDescription 就是将 offer 和 answer 的 SDP 设置到本地,并以此来创建 IceCandidate。

什么是 SDP

SDP 的全称为 Session Description Protocol,即会话描述协议,其在 IETF RFC 4566 中定义。WebRTC 使用 SDP 来协商会话的参数,当然由于 WebRTC 是没有信令的,这部分内容需要通过信令服务器传输。SDP 在 WebRTC 会话的设置中起着核心作用。SDP 是用于在 SIP 端点之间交换媒体信息的协议,并且 IETF 和 W3C 也选择它用于在 WebRTC 中交换媒体信息。WebRTC 使用 SDP 通知另一端在媒体会话中使用哪些传输协议、端口、编解码器和其他参数。以下是一个 SDP 的样例:

v=0
o=-
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS 273ddd94-6b94-4120-9c9a-15fbf94e621d f2a32375-1e7f-4c11-b4ec-557cf1302001
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:G3gC
a=ice-pwd:UomU3F+mw85MIbCtEvgcdaD6
a=ice-options:trickle renomination
a=fingerprint:sha-256 48:85:66:B4:F4:9F:58:05:99:D2:0F:91:63:A6:B0:B4:78:DD:EA:2A:FA:EE:95:B2:06:86:B2:E9:D3:85:DE:CC
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:1844961268 cname:BKLU8fq6dBaXdEIX
a=ssrc:1844961268 msid:273ddd94-6b94-4120-9c9a-15fbf94e621d ARDAMSa0
a=ssrc:1844961268 mslabel:273ddd94-6b94-4120-9c9a-15fbf94e621d
a=ssrc:1844961268 label:ARDAMSa0
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:G3gC
a=ice-pwd:UomU3F+mw85MIbCtEvgcdaD6
a=ice-options:trickle renomination
a=fingerprint:sha-256 48:85:66:B4:F4:9F:58:05:99:D2:0F:91:63:A6:B0:B4:78:DD:EA:2A:FA:EE:95:B2:06:86:B2:E9:D3:85:DE:CC
a=setup:actpass
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=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:10 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07
a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=sendrecv
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:97 rtx/90000
a=fmtp:97 apt=96
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=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 red/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 927647579 3068521629
a=ssrc:927647579 cname:BKLU8fq6dBaXdEIX
a=ssrc:927647579 msid:f2a32375-1e7f-4c11-b4ec-557cf1302001 ARDAMSv0
a=ssrc:927647579 mslabel:f2a32375-1e7f-4c11-b4ec-557cf1302001
a=ssrc:927647579 label:ARDAMSv0
a=ssrc:3068521629 cname:BKLU8fq6dBaXdEIX
a=ssrc:3068521629 msid:f2a32375-1e7f-4c11-b4ec-557cf1302001 ARDAMSv0
a=ssrc:3068521629 mslabel:f2a32375-1e7f-4c11-b4ec-557cf1302001
a=ssrc:3068521629 label:ARDAMSv0复制代码

SDP 由文本行组成,每行内容都是 <type>=<value> , type 是一个字母, value 是结构化文本,格式由 type 决定。 具体可见:tools.ietf.org/html/rfc456…

在此我介绍一下主要的 SDP Attributes:

rtpmap: a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]

rtcp-fb: 支持的 RTCP Feedback 类型

fmtp: a=fmtp:<format> <format specific parameters>

msid: a=msid-semantic: WMS <label> 

candidate: 定义 ICE 连接的候选地址和端口 a=candidate:<foundation> <component-id> <transport> <proiority> <connection-address> <port> <cand-type> <network-id> <network-cost>

ssrc: a=ssrc:<attribute> a=ssrc:<attribute>:<value> 这里代表一路 media stream

什么是 IceCandidate

在现实中有很多原因能导致简单的端到端直连不能直接完成,需要绕过阻止建立连接的防火墙,给设备分配一个唯一可见的地址(通常情况下我们的大部分设备没有一个固定的公网地址),如果路由器不允许主机直连,还得通过一台服务器转发数据。ICE 就是用于简历连接的 NAT 穿透协议,它会利用 TURN/STUN 协议进行穿透,ICE 服务可以让客户端拿到 TURN/ STUN 地址信息即可。

TURN 是通过服务器中转所有数据来达到连通的一种协议,显然服务器开销是非常大的,所以我们一般在 STUN 没法做到连通的时候才选择它来进行连通。

An interaction between two users of a WebRTC application involving STUN and TURN servers.

STUN 是一个允许位于 NAT 后的客户端找出自己的公网地址来判断路由器阻止直连的限制方法的协议。客户端通过给公网的STUN服务器发送请求获得自己的公网地址信息,Google提供了一个 STUN/ TURN 的实现Coturn。一个免费可用的 STUN 服务:stun:stun.l.google.com:19302

An interaction between two users of a WebRTC application involving a STUN server.

关于 TURN/ STUN/ ICE 更多详细阅读:www.52im.net/thread-557-…

IceCandidate 会在 SDP 中拼上候选的地址和端口并在交换后进行连通测试。连通测试通过后会在 PeerConnection.Observer#onIceConnectionChange 进行回调当前状态。Candidate 就是候选地址和端口了,双方交换后会以此检查连通性。检查连通性完毕后,就可以开始端对端直接交换音视频数据信息啦

关于更多

WebRTC 创建连接的关键就在于 SDP 的创建了,其实 SDP 就是一个字符串而已,所以如果想要做更多自定义的东西,可以直接自己拼接字符串。可能看文档会觉得很难理解,大家可以访问 webrtchacks.com/sdp-anatomy… 来理解 SDP。

WebRTC 的核心就在于端对端 P2P 的连接,大家可以去查阅相关 P2P 的资料来理解 WebRTC 的创建原理,比如 NAT 打洞原理。欢迎更多朋友一起交流 WebRTC 相关知识,若有错误也希望各位指正,我的邮箱/QQ:me@york1996.com。