欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

peerconnect-client与server信令获取和交互流程

程序员文章站 2022-07-01 15:34:54
...

原因:上个文章只是介绍了一下client和server端的简单协议交互,并没有涉及到webRTC的信令交互.即offer,answer,candidate等。

概述:webRTC的通信为点对点,则每个点都会创建自己的offer和candidate发送给对端,对端收到后创建自己的answer和candidate进行回复。然后webRTC内部进行candidate连通测试。下面利用代码进行简单描述。

接上文获取在线列表成员,然后点击成员进行连接调用代码如下,通过代码可以看出首先进行创建PeerConnection对象,然后创建本地的audio_track,video_track及准备需要的Mediastream.然后初始化成功后调用CreateOffer方法.

void Conductor::ConnectToPeer(int peer_id) {
	LOG(INFO) << __FUNCTION__;

  RTC_DCHECK(peer_id_ == -1);
  RTC_DCHECK(peer_id != -1);

  if (peer_connection_.get()) {
    main_wnd_->MessageBox("Error",
        "We only support connecting to one peer at a time", true);
    return;
  }

  if (InitializePeerConnection()) {
    peer_id_ = peer_id;
    peer_connection_->CreateOffer(this, NULL);
  } else {
    main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
  }
}

当CreateOffer成功后则内部会进行事件回调,事件主要定义在CreateSessionDescriptionObserver中.通过定义可以看出主要实现回调为CreateOffer和CreateAnswer.

// CreateOffer and CreateAnswer callback interface.
class CreateSessionDescriptionObserver : public rtc::RefCountInterface {
 public:
  // This callback transfers the ownership of the |desc|.
  // TODO(deadbeef): Make this take an std::unique_ptr<> to avoid confusion
  // around ownership.
  virtual void OnSuccess(SessionDescriptionInterface* desc) = 0;
  virtual void OnFailure(const std::string& error) = 0;

 protected:
  ~CreateSessionDescriptionObserver() {}
};

客户端实现OnSuccess方法后,将本机回调接收到Offer信息信息调用SetLocalDescription进行保存,随后发送给服务端.主要代码和数据信息如下

void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
	LOG(INFO) << __FUNCTION__;

  peer_connection_->SetLocalDescription(
      DummySetSessionDescriptionObserver::Create(), desc);

  std::string sdp;
  desc->ToString(&sdp);

  Json::StyledWriter writer;
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = desc->type();
  jmessage[kSessionDescriptionSdpName] = sdp;
  SendMessage(writer.write(jmessage));
  LOG(INFO) << jmessage;

}

 {
	"sdp" : "v=0\r\no=- 449280690799912824 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE audio video\r\na=msid-semantic: WMS stream_label\r\nm=audio 9 UDP/TLS/RTP/SAVPF 103 104 0 8 106 105 13 112 113 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:cF8F\r\na=ice-pwd:/mLl0OIRuzAUE0gXyCJ+Z+P4\r\na=fingerprint:sha-256 79:B2:E3:E0:8A:0C:CB:DA:D2:12:D6:50:FB:FD:B5:E8:18:CA:C6:F6:07:26:0E:F7:CF:53:7F:80:E4:76:73:3B\r\na=setup:actpass\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:112 telephone-event/32000\r\na=rtpmap:113 telephone-event/16000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:2929087609 cname:O025nGVLc6C+RI1k\r\na=ssrc:2929087609 msid:stream_label audio_label\r\na=ssrc:2929087609 mslabel:stream_label\r\na=ssrc:2929087609 label:audio_label\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:cF8F\r\na=ice-pwd:/mLl0OIRuzAUE0gXyCJ+Z+P4\r\na=fingerprint:sha-256 79:B2:E3:E0:8A:0C:CB:DA:D2:12:D6:50:FB:FD:B5:E8:18:CA:C6:F6:07:26:0E:F7:CF:53:7F:80:E4:76:73:3B\r\na=setup:actpass\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:4 urn:3gpp:video-orientation\r\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtpmap:100 H264/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 nack pli\r\na=rtcp-fb:100 goog-remb\r\na=rtcp-fb:100 transport-cc\r\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:102 red/90000\r\na=rtpmap:127 ulpfec/90000\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\na=rtpmap:125 rtx/90000\r\na=fmtp:125 apt=102\r\na=ssrc-group:FID 4198811712 3825804760\r\na=ssrc:4198811712 cname:O025nGVLc6C+RI1k\r\na=ssrc:4198811712 msid:stream_label video_label\r\na=ssrc:4198811712 mslabel:stream_label\r\na=ssrc:4198811712 label:video_label\r\na=ssrc:3825804760 cname:O025nGVLc6C+RI1k\r\na=ssrc:3825804760 msid:stream_label video_label\r\na=ssrc:3825804760 mslabel:stream_label\r\na=ssrc:3825804760 label:video_label\r\n",
	"type" : "offer"
}

而Candidate的回调主要实现是通过OnIceCandidate事件,该事件定义在PeerConnectionObserver中.当收集到一个candidate后就会进行回调,然后将Candidate数据发送给服务端进行转发,可以看出candidate的回调为多次。

// PeerConnection callback interface, used for RTCPeerConnection events.
// Application should implement these methods.
class PeerConnectionObserver {
 public:

  // A new ICE candidate has been gathered.
  virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0;

std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {}

 protected:
  // Dtor protected as objects shouldn't be deleted via this interface.
  ~PeerConnectionObserver() {}
};

{
   "candidate" : "candidate:1347174600 1 udp 2122260223 192.168.27.196 50715 typ host generation 0 ufrag oBB3 network-id 3 network-cost 50",
   "sdpMLineIndex" : 0,
   "sdpMid" : "audio"
}

当服务器接收到数据信息后进行转发,这时客户端的hanging_get对象的OnHangingRead将被触发.然后将数据通过OnMessageFromPeer进行解析.通过如下代码可以看出获取的数据有两种类型一种是有type类型,一种是没有type类型。而有type类型的数据就是对端的offer或者answer.则接收后则会调用SetRemoteDescription进行保存。如果是没有type类型则是candidate信息,则调用AddIceCandidate进行保存.然后webrtc内部就会进行ice连接测试,然后开始点对点通信。

void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) {


  rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);
  if (!type.empty()) {

   
    LOG(INFO) << " Received session description :" << message;
    peer_connection_->SetRemoteDescription(
        DummySetSessionDescriptionObserver::Create(), session_description);
    if (session_description->type() ==
        webrtc::SessionDescriptionInterface::kOffer) {
      peer_connection_->CreateAnswer(this, NULL);
    }
    return;

  } else {

    if (!peer_connection_->AddIceCandidate(candidate.get())) {
      LOG(WARNING) << "Failed to apply the received candidate";
      return;
    }
    LOG(INFO) << " Received candidate :" << message;
    return;
  }
}

总结:以上流程就是简单的信令协议交互,端点之间传递自己的Candidate和Offer或者Answer,然后实现点对点通信,webRtc暴露出的接口想对来说较为简单,下一步分析一下Candidate数据信息含义。

相关标签: webRTC