1- WebRTC传输基本知识
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1- WebRTC传输基本知识相关的知识,希望对你有一定的参考价值。
参考技术A NAT穿越可以分为四种类型:完全锥型、地址限制型、端口限制型、对称型内网打穿后生成公网地址和端口,任意外网用户访问都可以访问,没有限制。
内网主机IP和端口、 NAT穿越后生成公网IP和端口、要请求的主机IP,其它主机的ip地址不是内网主机要请求的地址会失败
在地址限制型的基础上,增加了端口限制,如果请求的主机返回的端口也不对,请求也不成功
NAT穿越后会生成多个IP地址和端口号, 请求的主机对应一个ip地址和端口,内外穿到外网会生成不同的ip地址和端口号给不同的主机
NAT穿越的原理
前提要在服务端部署STUN服务器,并且有两个IP地址和端口
目的:NAT穿越,主机访问STUN服务器,返回一个公网ip地址
stun是典型的客户端/服务器响应模式,客户端发送请求,服务端响应。
规范:
RFC3489:通过UDP进行NAT穿越
RFC5389:通过UDP、TCP进行NAT穿越
由于UDP会有失败的情况,RFC5489引入TCP。
stun 5389格式
0b00:表示是一个请求
0b01:表示一个指示
0b10:表示一个请求成功的响应
0b11:表示一个请求失败的响应
最右侧是c0,
网络中使用大端模式,左边的优先被接收到,右边的最后接收到。
其中USERNAME、PASSWORD最重要,用于STUN服务器验证用户合法性
属性的什么时候使用
N/A 不支持,O是可选的,M是服务端,C是客户端
turn使用的传输协议
turn client - turn server :UDP、TCP、TLS over TCP
TURN server to peer: UDP
在STUN无法接通时,这时就需要公网的服务器作为一个中继,对来往的数据进行转发。这个转发的协议就被定义为TURN。TURN和其他中继协议的不同之处在于,它允许客户端使用同一个中继地址(relay address)与多个不同的peer进行通信。
使用TURN协议的客户端必须能够通过中继地址和对等端进行通讯,并且能够得知每个peer的的IP地址和端口(确切地说,应该是peer的服务器反射地址)。
TURN协议被设计为ICE协议的一部分,relay地址会作为一个候选,由ICE在多个候选中进行评估,选取最合适的通讯地址。一般来说中继relay的优先级都是最低的。
TURN协议本身是STUN的一个拓展,因此绝大部分TURN报文都是STUN类型的,作为STUN的一个拓展,TURN增加了新的方法(method)和属性(attribute)。
在典型的情况下,TURN客户端连接到内网中,并且通过一个或者多个NAT到达公网,TURN服务器架设在公网中,不同的客户端以TURN服务器为中继和其他peer进行通信,如下图所示:
在上图中,左边的TURN Client是位于NAT后面的一个客户端(内网地址是10.1.1.2:49721),连接公网的TURN服务器(默认端口3478)后,
服务器会得到一个Client的反射地址(Reflexive Transport Address, 即NAT分配的公网IP和端口)192.0.2.1:7000,
此时Client会通过TURN命令创建或管理ALLOCATION,allocation是服务器上的一个数据结构,包含了中继地址的信息。
服务器随后会给Client分配一个中继地址,即图中的192.0.2.15:50000,另外两个对等端若要通过TURN协议和Client进行通信,
可以直接往中继地址收发数据即可,TURN服务器会把发往指定中继地址的数据转发到对应的Client,这里是其反射地址。
Server上的每一个allocation都唯一对应一个client,并且只有一个中继地址,因此当数据包到达某个中继地址时,服务器总是知道应该将其转发到什么地方。
但值得一提的是,一个Client可能在同一时间在一个Server上会有多个allocation,这和上述规则是并不矛盾的。
ICE包括了NAT、STUN、TURN。
主要工作:
第一步:找出端与端的所有路径:网卡的路径,NAT穿越后的公网ip、中继服务、多网卡、vpn等
第二步:相互传给对方路径,找出能通的路径
基本概念
Android WebRTC 入门教程 -- 模拟p2p本地视频传输
Android WebRTC 入门教程(一) – 使用相机
Android WebRTC 入门教程(二) – 模拟p2p本地视频传输
源码工程: https://github.com/LillteZheng/WebRTCDemo
今天要实现的效果:
上一章中,我们学习了 PeerConnectionFactory ,VideoCapturer 等知识实现了相机的预览。
这一章,学习一个比较重要的知识,Peerconnection 点对点传输,把本地摄像头的数据,采集之后,通过 Peerconnection 建立连接,把摄像头数据,传递给另一个 surface 显示,
效果如下:
动态太大,看视频连接 https://live.csdn.net/v/242968
device-2022-09-30-142040
一. PeerConnection
PeerConnection也就是Peer-to-Peer connection(P2P) ,顾名思义,这个类代表点对点之间的连接,以及音视频数据的传输。
PeerConnection 会通过 Sdp(SessionDescription) 和 ICE(IceCandidate) 进行媒体协商,实际就是你设备支持哪些音视频编解码格式,如果两者都支持,那么就算协商成功了。
此时通道就已经建立成功,可以从 Peerconnection 中的 MediaStream 去拿数据流了。
这里先熟悉几个只是点:
1.1 SDP (SessionDescription)
会话描述符,像 RTSP 这类协议,都需要进行媒体协商,际就是你的设备所支持的音视频编解码器、使用的传输协议、SSRC等信息…通过信令服务器透传给对方。如果双方都支持,那么就算协商成功了。
- Offer: 呼叫方发送端的 SDP 消息叫做 offer
- Answer:被呼叫方发送端的 SDP 消息叫做 Answer
1.2 ICE(IceCandidate)
进行媒体协商后,通道此时还未建立,就需要交换 IceCandidate,其实是一个整合STUN和TURN的框架, 给它提供STUN和TURN服务器地址, 它会自动选择优先级高的进行NAT穿透,选择一条稳定的通道。
二. 实现
首先,先创建两个 SurfaceViewRender ,一个用来摄像头渲染,一个用来接收:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="trans"
android:text="开始本地传输"
/>
<org.webrtc.SurfaceViewRenderer
android:id="@+id/localRender"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<org.webrtc.SurfaceViewRenderer
android:id="@+id/remoteRender"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_weight="1"/>
</androidx.appcompat.widget.LinearLayoutCompat>
而 remoteRender 的配置,因为我们需要等待数据渲染,所以只需要初始化等待即可:
/**
* 配置接收端的渲染surface
*/
fun configRemoteRender(eglBaseContext:EglBase.Context)
remoteRender.holder.addCallback(object:SurfaceHolder.Callback
override fun surfaceCreated(holder: SurfaceHolder)
//等待接收数据,这里只要初始化即可
remoteRender.init(eglBaseContext,null)
remoteRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
remoteRender.setEnableHardwareScaler(false)
override fun surfaceChanged(
holder: SurfaceHolder,
format: Int,
width: Int,
height: Int
)
override fun surfaceDestroyed(holder: SurfaceHolder)
)
为了方便解释,这里的发送端使用 local,接收端使用 remote,他们的流程图如下:
2.1 添加 MediaStream
获取相机数据,上一章中,我们已经实践过了,代码不变,还未看过的可以参考上一章。
Android WebRTC 入门教程(一) – 使用相机
代码如下:
createCameraCapture()?.let camera->
....
// 添加渲染接收端器到轨道中,画面开始呈现
videoTrack.addSink(localRender)
// 创建 mediastream
localMediaStream = peerConnectionFactory.createLocalMediaStream("MediaStream")
// 将视频轨添加到 mediastram 中,等待连接时传输
localMediaStream.addTrack(videoTrack)
2.2 创建 Peerconnection
要进行点对点传输,就需要创建 Peerconnection ,使用 PeerconnectionFactory 创建,如
val iceServers: List<IceServer> = ArrayList()
//内部会转成 RTCConfiguration
localPeerConnection = peerConnectionFactory.createPeerConnection(iceServers,object:PeerObserver()
override fun onAddStream(strem: MediaStream?)
super.onAddStream(strem)
override fun onIceCandidate(p0: IceCandidate?)
super.onIceCandidate(p0)
)
remotePeerConnection = peerConnectionFactory.createPeerConnection(iceServers,object :PeerObserver()
override fun onAddStream(strem: MediaStream?)
super.onAddStream(strem)
override fun onIceCandidate(p0: IceCandidate?)
super.onIceCandidate(p0)
)
其中 PeerObserver 继承 PeerConnection.Observer ,减少一些方法的重写。
2.2 Create offer
此时,他们还没有任何联系。
根据时序图,我们需要先创建 sdp offer,并发送给接收端。
首先先添加媒体流
localPeerConnection?.addStream(localMediaStream)
然后创建 offer,此时就要进行 sdp 媒体交换:
这个是双方协商的过程,因此会设计到两个比较特殊的方法,setLocalDescription 和setRemoteDescription,双方在创建sdp 之后,都需要使用 setLocalDescription 把 sdp 保存到本地,然后对方使用 setRemoteDescription,把对方的 sdp 保存到远程。
具体方法如下:
//客户端创建 offer
localPeerConnection?.createOffer(object:SdpObserver("创建local offer")
override fun onCreateSuccess(p0: SessionDescription?)
super.onCreateSuccess(p0)
Log.d(TAG, "创建 local offer success: ")
/**
* 此时调用setLocalDescription()方法将该Offer保存到本地Local域,
* 然后将Offer发送给对方
*/
localPeerConnection?.setLocalDescription(SdpObserver("local 设置本地 sdp"),p0)
//2. 同时,接收端 setRemoteDescription 把 offer 保存到远端域
remotePeerConnection?.setRemoteDescription(SdpObserver("把 sdp 给到 remote"),p0)
//3. 此时remote 已经接收端了 offer ,所以创建 answer
remotePeerConnection?.createAnswer(object:SdpObserver("创建 remote answer")
override fun onCreateSuccess(p0: SessionDescription?)
super.onCreateSuccess(p0)
Log.d(TAG, " 创建 remote answer success: ")
/**
* 4. 此时调用setLocalDescription()方法将该 answer 保存到本地 remote 域,
* 然后将 answer 发送给对方
*/
remotePeerConnection?.setLocalDescription(SdpObserver("remote 设置本地 sdp"),p0)
//5. local 把 answer 保存到它的 remote 端,此时 sdp 交换结束
localPeerConnection?.setRemoteDescription(SdpObserver("把 sdp 给到 local"),p0)
, MediaConstraints())
, MediaConstraints())
我们以连接上的Client端为呼叫方先发起Offer请求,通过createOffer()创建一个Offer SDP。创建成功后,会在SdpObserver中收到onCreateSuccess回调,此时调用setLocalDescription()方法将该Offer保存到本地Local域,然后将Offer发送给对方。
被呼叫方接收到Offer后,通过setRemoteDescription()方法将Offer保存到它的Remote域,并通过createAnswer()创建Answer SDP,创建成功后同样调用setLocalDescription()方法将Answer消息保存到本地的Local域,然后回复给呼叫方。
最后,呼叫方将收到Answer消息,并通过setRemoteDescription()方法,将Answer保存到它的Remote域。至此,整个媒体协商的过程结束。
其中 SdpObserver 继承 webrtc 的 SdpObserver ,减少一些方法的重写。
三. 建立点对点连接
媒体协商结束,我们的点对点并没有真正的简历。此时 PeerConnection.Observer 会回调 onIceCandidate ,我们需要将各自的 Candidate 添加给对方。
同时,因为 local 作为视频流的一方,onAddStream 不需要理会,但 remote 作为接收方,则需要把 MediaStream 中的流拿出来,放到 SurfaceViewRender 去渲染。
代码如下:
localPeerConnection = peerConnectionFactory.createPeerConnection(iceServers,object:PeerObserver()
override fun onAddStream(strem: MediaStream?)
super.onAddStream(strem)
override fun onIceCandidate(p0: IceCandidate?)
super.onIceCandidate(p0)
//透传给另外一端
remotePeerConnection?.addIceCandidate(p0)
)
remotePeerConnection = peerConnectionFactory.createPeerConnection(iceServers,object :PeerObserver()
override fun onAddStream(strem: MediaStream?)
super.onAddStream(strem)
//拿到视频流,添加 sink ,SurfaceViewRender 会直接渲染
strem?.let
runOnUiThread
it.videoTracks[0].addSink(remoteRender)
override fun onIceCandidate(p0: IceCandidate?)
super.onIceCandidate(p0)
//透传给另外一端
localPeerConnection?.addIceCandidate(p0)
)
基本上学完这样,我们已经可以进行不同端传输音视频了。
只是sdp 的协商,改成其他信令服务器。
参考:
https://codezjx.com/posts/webrtc-android-demo/#more
以上是关于1- WebRTC传输基本知识的主要内容,如果未能解决你的问题,请参考以下文章