淘先锋技术网

首页 1 2 3 4 5 6 7

先看看大神的解析

下面主要是介绍WebRTC自带的一个传输模块,实际应用中您可以根据自己的需求注册不同的传输模块。

        

其中:udp_socket_wrapper.h主要负责Socket相关操作,如Socket创建、启动、端口绑定、停止。

       udp_socket2_windows.h主要负责windows平台上的Socket相关操作,与之对应的就是linux平台上的udp_socket_posix.h

       udp_transport.h主要负责包的发送和接收,如果你想实现自己的数据包收发逻辑,可重写该类,如他里面的LoopBack方式就是通过重写该模块来实现的。

对客户端调用来说主要就是做四件事情:

  1、设置音视频远端地址和端口(包括远端音视频的RTP、RTCP端口和本地接收音视频的RTP、RTCP端口)。

  2、启动音视频数据的发送。

  3、启动音视频数据的接收。

  4、启动音视频数据的播放。

具体代码结构如下:

复制代码
1 public void start() {
2         this.setRemoteIp(WebRTCClient.str_remote_ip);WebRTCClient.str_to);
3         if (audioEnabled) {
4             startVoE();
5         }
6         if (receiveVideo || sendVideo) {
7             startViE();
8         }
9     }
复制代码
复制代码
1 public void startVoE() {
2         check(!voeRunning, "VoE already started");
3         check(voe.startListen(audioChannel) == 0, "Failed StartListen");
4         check(voe.startPlayout(audioChannel) == 0, "VoE start playout failed");
5         check(voe.startSend(audioChannel) == 0, "VoE start send failed");
6         voeRunning = true;
7     }
复制代码
复制代码
 1 public void startViE() {
 2         check(!vieRunning, "ViE already started");
 3 
 4         if (receiveVideo) {
 5             if (viewSelection == context.getResources().getInteger(R.integer.openGl)) {
 6                 svRemote = ViERenderer.CreateRenderer(context, true);
 7             } else if (viewSelection == context.getResources().getInteger(R.integer.surfaceView)) {
 8                 svRemote = ViERenderer.CreateRenderer(context, false);
 9             } else {
10                 externalCodec = new MediaCodecVideoDecoder(context);
11                 svRemote = externalCodec.getView();
12             }
13             if (externalCodec != null) {
14                 check(vie.registerExternalReceiveCodec(videoChannel, VCM_VP8_PAYLOAD_TYPE,
15                         externalCodec, true) == 0, "Failed to register external decoder");
16             } else {
17                 check(vie.addRenderer(videoChannel, svRemote, 0, 0, 0, 1, 1) == 0,
18                         "Failed AddRenderer");
19                 check(vie.startRender(videoChannel) == 0, "Failed StartRender");
20             }
21             check(vie.startReceive(videoChannel) == 0, "Failed StartReceive");
22         }
23         if (sendVideo) {
24             startCamera();
25             check(vie.startSend(videoChannel) == 0, "Failed StartSend");
26         }
27         vieRunning = true;
28     }


RTCPeerConnection + signaling: offer, answer and candidate

RTCPeerConnection就是webrtc应用程序用来创建客户端连接和视频通讯的API.为了初始化这个过程 RTCPeerConnection有两个任务:

  1,确定本地媒体条件,如分辨率,编解码能力,这些需要在offer和answer中用到.

  2,取到应用程序所在机器的网络地址,即称作candidates.

一旦上面这些东西确定了,他们将通过信令机制和远端进行交换.

想象一下Alice呼叫Eve的过程( Alice is trying to call Eve.),下面就是完整offer/answer机制的细节:

1,Alice创建一个 RTCPeerConnection对象.

2,Alice创建一个offer(即SDP会话描述)通过RTCPeerConnection createOffer()方法.

3,Alice调用setLocalDescription()方法用他的offer.

4,Alice通过信令机制将他的offer发给Eve.

5,Eve调用setRemoteDescription()方式设置Alice的offer,因此他的RTCPeerConnection知道了Alice的设置.

6,Eve调用方法createAnswer(),然后会触发一个callback,这个callback里面可以去到自己的answer.

7,Eve设置他自己的anser通过调用方法setLocalDescription().

8,Eve通过信令机制将他的anser发给Alice.

9,Alice设置Eve的anser通过方法setRemoteDescription().

 

另外Alice和Eve也需要交换网络信息(即candidates),发现candidates参考了ICE framework.

1,Alice创建RTCPeerConnection对象时设置了onicecandidate handler.

2,hander被调用当candidates找到了的时候.

3,当Eve收到来自Alice的candidate消息的时候,他调用方法addIceCandidate(),添加candidate到远端描述里面.

JSEP支持ICE Candidate Trickling,他允许呼叫方在offer初始化结束后提供candidates给被叫方.而被叫方开始建立呼叫和连接而不需要等到所有candidate到达.

怎么发现客户端

这里有一种很简单的表述方式---我怎么找到别人视频?

打电话的时候我们有电话号码和电话本,知道打给谁,QQ聊天的时候,我们可以通过通讯录找到要聊天的人,webrtc也一样,他的客户端需要通过一种方式找到要聊天的人或要加入的会议.

webrtc没有定义这样一个发现过程,这个其实很简单,可以参考 talky.iotawk.com and browsermeeting.com,另外Chris Ball创建了serverless-webrtc,他可以通过Emai,IM来参与视频.

 

怎么创建信令服务?

再次重申:webrtc没有定义信令机制,因此无论你选择什么机制你都的需要一台中间服务端,用来在客户端之间交换数据,你总不可能直接说:"跟我朋友视频?",

由于信令消息很小,大多数交互都是在开始通话之前,可以参考 apprtc.appspot.com and samdutton-nodertc.jit.su, 测试发现:一个视频通话过程大概有35~40消息,数据量在10K左右,

所以相对来说信令服务器不怎么占带宽,也不需要消耗多大的CPU和内存.

从服务端推送消息给客户端

信令服务器推送消息需要时双向的,即客户端能发消息给服务器,服务器也能发消息给服务端,这种双向机制就将Http给排除了(当然可以使用长连接,而且很多人都是这么做的,只不过比较占资源).

说到这里很多人会想到WebSocket,没错,这是一种很好的解决方案,而且后台实现框架也很多,如PHP,Python,Ruby.

大约3/4的浏览器支持webSocekt,更重要的是支持WEBRTC的浏览器都支持WebSocket,包括PC和手机, TLS应该被使用为了所有连接,他能确保为被加密的消息不被截获,同时也能减少使用代理带来的问题(reduce problems with proxy traversal),更多这方面的知识请参考 WebRTC chapterWebSocket Cheat Sheet .

apprtc.appspot.com中的视频通讯使用的信令是 Google App Engine Channel API,他采用的是 Comet技术, HTML5 Rocks WebRTC article有详细的介绍(detailed code walkthrough)

当然你也可以通过Ajax来实现这样一个长连接,不过这样会产生很多重复的网络请求,而且应用在移动端会有很多问题.

扩展信令的实现

尽管信令服务占用的CPU和带宽资源都比较少,但实际应用中如果要考虑到高并发,信令服务还是有很大负载的.这些我们不深入讨论了,下面有一些不错的选择供参考:

1,eXtensible Messaging and Presence Protocol(XMPP):主要是用来给即时通讯用的,开源服务端包括ejabberd and Openfire. 客户端包括 Strophe.js use BOSH(但因为 various reasons,BOSH没有WebSocket高效),补充说明:Jingle是XMPP的扩展,支持音视频,webrtc项目里面的network和transort组件就是来自 libjingle库.

2,开源库如 ZeroMQOpenMQ

Developer Phil Leggetter's Real-Time Web Technologies Guide 提供了一个消息服务和库的综合清单.


使用RTCDataChannel控制信令

一旦信令服务建立好了,两个客户端之间建立了连接,理论上他们就可以使用RTCDataChannel进行点对点通讯了,这样可以减轻信令服务的压力和消息传递的延迟,这部分没有提供Demo.

使用已有信令服务

如果您不想自己动手,这里还有提供几个webrtc信令服务器,与上述代码类似他们使用socket.io. 与webrtc客户端的javascript集成到一起了.

webRTC.io:webrtc的第一个抽想库.

easyRTC:一个完整的webrtc库.

Signalmaster:信令服务器,和 SimpleWebRTC作为客户端脚本库配套使用.

如果您不想写任何代码的花,可以直接使用现有商业产品:vLineOpenTok and Asterisk.

如果您想实现录制功能,可参考 signaling server using PHP on Apache,虽然已经过时了,但代码可供参考.

信令安全性问题

因为信令使我们自己定义的,所以安全性问题跟webrtc无关,需要自己处理.一旦黑客掌握了你的信令,那他就是控制会话的开始,结束,重定向等等.

最重要的因素在信令安全中还是要靠使用安全协议,如HTTPS,WSS(如TLS),他们能确保未加密的消息不能被截取.

为确保信令安全,强烈推荐使用TLS.

使用ICE处理NATs和防火墙

元数据是通过信令服务器中转发给另一个客户端,但是对于流媒体数据,一旦会话建立,RTCPeerConnection将首先尝试使用点对点连接.

简单一点说就是:每个客户端都有一个唯一的地址,他能用来和其他客户端进行通讯和数据交换.

现实生活中客户端都位于一个或多个NAT之后,或者一些杀毒软件还阻止了某些端口和协议,或者在公司还有防火墙或代理,等等,防火墙和NAT或许是同一个设备,如我们家里用的路由器.

webrtc就是通过 ICE这套框架来处理复杂的网络环境的,如果想启用这个功能,你必须让你得应用程序传ice服务器的URL给RTCPeerConnection,描述如下:

ICE试着找最好的路径来让客户端建立连接,他会尝试所有可能的选项,然后选择最合适的方案,ICE首先尝试P2P连接,如果失败就会通过Turn服务器进行转接.

换一个说法就是:

1,STUN服务器是用来取外网地址的.

2,TURN服务器是在P2P失败时进行转发的.

每个TURN服务器都支持STUN,ICE处理复杂的NAT设置,同时NAT打洞要求不止一个公网IP和端口.