webRTC学习三(代码分析)

项目来源:

https://github.com/pchab/AndroidRTC

      AndroidRTC是ProjectRTC的android客户端,下载后直接AndroidStudio打开。AndroidRTC中包含两个moudle,app是主界面,webrtc-client是工具类moudle 。

     本文为自己学习过程中查询的资料和代码的分析,纯属个人观点,有不对之处还望提出,多多交流和提高,谢谢。

     Web API——第三方开发人员用来开发基于Web的应用,如视频聊天。
    WebRTC Native C++ API——浏览器厂商用于实现Web API的函数集。
    Session Management——抽象session层,支持调用构建和管理层,由应用开发者来决定如何实现协议。
    VoiceEngine——音频媒体链的框架,从声卡到网络。
    iSAC——一种用于VoIP和流音频的宽带和超宽带音频编解码器,iSAC采用16 kHz或32 kHz的采样频率和12—52 kbps的可变比特率。
    iLBC——用于VoIP和流音频的窄带语音编解码器,使用8 kHZ的采样频率,20毫秒帧比特率为15.2 kbps,30毫米帧的比特率为13.33 kbps,标准由 IETF RFC 3951和3952定义。
     NetEQ for Voice——动态抖动缓存和错误隐藏算法,用于缓解网络抖动和丢包引起的负面影响。在保持高音频质量的同时尽可能降低延迟。
     VideoEngine——视频媒体链的框架,从相机像头到网络,从网络到屏幕。
     VP8——来自于WebM项目的视频编解码器,非常适合RTC,因为它是为低延迟而设计开发的。
     Image enhancements——消除通过摄像头获取的图片的视频噪声等。

     PeerConnection位于WebRTC Native C++ API的最上层,它的代码实现来源于libjingle(一款p2p开发工具包),目前被应用于WebRTC中。

     

  • 可以看得出来PeerConnectionFactory的重要性,` Factory是工厂,工厂可以生产很多很多的PeerConnection
  • 摄像头、麦克风这些设备只能是进程独占方式的,所有只有一个,然后多个PeerConnection共享使用LocalMediaStream 
看一下demo的代码结构

webRTC学习三(代码分析)_第1张图片

下面是爱立信公司的openWebRTCDemo

webRTC学习三(代码分析)_第2张图片

最开始以为OpenWebrtc是对Webrtc的修改版本,但是实际上不是这样。

Openwebrtc是爱立信实验室开发的,Ericsson和Google都是webrtc标准的主要制定者,两家公司分别实现了一套webrtc ,即Ericsson Openwebrtc和我们熟知的Google Webrtc。两者更是一种竞争关系。

Google Webrtc基于GIPS,而Ericsson Openwebrtc基于GStreamer。


WebRTC Android端的大体实现过程如下:(在不考虑播放本地视频的情况下)

  • 连接服务器,并通过服务器打通两个客户端的网络通道。
  • 从摄像头和麦克风获取媒体流 。
  • 将本地媒体流通过网络通道传送给对方的客户端 。
  • 渲染播放接收到的媒体流 。

关键技术

核心类PeerConnectionFactory

首先需要初始化PeerConnectionFactory,这是WebRTC的核心工具类,初始化方法如下:

PeerConnectionFactory.initializeAndroidGlobals(
    context,//上下文,可自定义监听
    initializeAudio,//是否初始化音频,布尔值
    initializeVideo,//是否初始化视频,布尔值
    videoCodecHwAcceleration,//是否支持硬件加速,布尔值
    renderEGLContext);//是否支持硬件渲染,布尔值
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后就可以获得对象:PeerConnectionFactory factory= new PeerConnectionFactory();

获取媒体流

第一步:获取视频源videoSource

String frontCameraName = VideoCapturerAndroid.getNameOfFrontFacingDevice();
VideoCapturer videoCapturer = VideoCapturerAndroid.create(frontCameraName);
VideoSource videoSource = factory.createVideoSource(videoCapturer,videoConstraints);
  • 1
  • 2
  • 3

其中videoConstraints是对视频流的一些限制,按如下方法创建。

MediaConstraints videoConstraints = new MediaConstraints();
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", Integer.toString(pcParams.videoHeight)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", Integer.toString(pcParams.videoWidth)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxFrameRate", Integer.toString(pcParams.videoFps)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minFrameRate", Integer.toString(pcParams.videoFps)));
  • 1
  • 2
  • 3
  • 4
  • 5

第二步:获取音频源audioSource

音频源的获取简单许多:

AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
  • 1

第三步:获得封装VideoTrack/AudioTrack

VideoTrack/AudioTrack 是 VideoSource/AudioSource 的封装,方便他们的播放和传输:

VideoTrack videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
AudioTrack audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
  • 1
  • 2

第四步:获取媒体流localMS

其实 VideoTrack/AudioTrack 已经可以播放了,不过我们先不考虑本地播放。那么如果要把他们发送到对方客户端,我们需要把他们添加到媒体流中:

MediaStream localMS=factory.createLocalMediaStream("ARDAMS");
localMS.addTrack(videoTrack);
localMS.addTrack(audeoTrack);
  • 1
  • 2
  • 3

然后,如果有建立好的连接通道,我们就可以把 localMS 发送出去了。

建立连接通道

WebRTC是基于P2P的,但是在连接通道建立好之前,我们仍然需要服务器帮助传递信令,而且需要服务器帮助进行网络穿透。大体需要如下几个步骤。

第一步:创建PeerConnection的对象。

PeerConnection pc = factory.createPeerConnection(
    iceServers,//ICE服务器列表
    pcConstraints,//MediaConstraints
    context);//上下文,可做监听
  • 1
  • 2
  • 3
  • 4

iceServers 我们下面再说。 
pcConstraints是媒体限制,可以添加如下约束:

pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
  • 1
  • 2
  • 3

监听器建议同时实现SdpObserver、PeerConnection.Observer两个接口。

第二步:信令交换

建立连接通道时我们需要在WebRTC两个客户端之间进行一些信令交换,我们以A作为发起端,B作为响应端(A call B,假设服务器和A、B已经连接好,并且只提供转发功能,PeerConnection对象为pc ):

  • A向B发出一个“init”请求(我觉得这步没有也行)。
  • B收到后“init”请求后,调用pc.createOffer()方法创建一个包含SDP描述符(包含媒体信息,如分辨率、编解码能力等)的offer信令。
  • offer信令创建成功后会调用SdpObserver监听中的onCreateSuccess()响应函数,在这里B会通过pc.setLocalDescription将offer信令(SDP描述符)赋给自己的PC对象,同时将offer信令发送给A 。
  • A收到B的offer信令后,利用pc.setRemoteDescription()方法将B的SDP描述赋给A的PC对象。
  • A在onCreateSuccess()监听响应函数中调用pc.setLocalDescription将answer信令(SDP描述符)赋给自己的PC对象,同时将answer信令发送给B 。
  • B收到A的answer信令后,利用pc.setRemoteDescription()方法将A的SDP描述赋给B的PC对象。

这样,A、B之间就完成里了信令交换。

第三步:通过ICE框架穿透NAT/防火墙

如果在局域网内,信令交换后就已经可以传递媒体流了,但如果双方不在同一个局域网,就需要进行NAT/防火墙穿透(我是在局域网下测试的,没有穿透,但还是把这方面内容介绍下)。

WebRTC使用ICE框架来保证穿透。ICE全名叫交互式连接建立(Interactive Connectivity Establishment),一种综合性的NAT/FW穿越技术,它是一种框架,可以整合各种NAT/FW穿越技术如STUN、TURN(Traversal Using Relay NAT 中继NAT实现的穿透)。ICE会先使用STUN,尝试建立一个基于UDP的连接,如果失败了,就会去TCP(先尝试HTTP,然后尝试HTTPS),如果依旧失败ICE就会使用一个中继的TURN服务器。使用STUN服务器穿透的结构如下: 
这里写图片描述 
我们可以使用Google的stun服务器:stun:stun.l.google.com:19302(Google嘛,你懂得,当然如果有精力可以自己搭建一个stun服务器),那么我们怎么把这个地址告诉WebRTC呢,还记得之前的iceServers吗,就是在创建PeerConnection对象的时候需要的参数,iceServers里面存放的就是进行穿透地址变换的服务器地址,添加方法如下(保险起见可以多添加几个服务器地址,如果有的话):

iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
  • 1

然后这个stun服务器地址也需要通过信令交换,同样以A、B客户端为例过程如下:

  • A、B分别创建PC实例pc(配置了穿透服务器地址) 。
  • 当网络候选可用时,PeerConnection.Observer监听会调用onIceCandidate()响应函数并提供IceCandidate(里面包含穿透所需的信息)的对象。在这里,我们可以让A、B将IceCandidate对象的内容发送给对方。
  • A、B收到对方发来的candidate信令后,利用pc.addIceCandidate()方法将穿透信息赋给各自的PeerConnection对象。

至此,连接通道完全打通,然后我们只需要将之前获取的媒体流localMS赋给pc即可:

pc.addStream(localMS);//也可以先添加,连接通道打通后一样会触发监听响应。
  • 1

在连接通道正常的情况下,对方的PeerConnection.Observer监听就会调用onAddStream()响应函数并提供接收到的媒体流。



你可能感兴趣的:(即时通讯)