天天看點

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中。

webRTC學習三(代碼分析) 關鍵技術
  • 可以看得出來PeerConnectionFactory的重要性,` Factory是工廠,工廠可以生産很多很多的PeerConnection
  • 攝像頭、麥克風這些裝置隻能是程序獨占方式的,所有隻有一個,然後多個PeerConnection共享使用LocalMediaStream 

看一下demo的代碼結構

webRTC學習三(代碼分析) 關鍵技術

下面是愛立信公司的openWebRTCDemo

webRTC學習三(代碼分析) 關鍵技術

最開始以為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伺服器穿透的結構如下: 

webRTC學習三(代碼分析) 關鍵技術

我們可以使用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()

響應函數并提供接收到的媒體流。

繼續閱讀