webrtc c++ (四)peerConnection creatrOffer分析

基于通过webrtc自带的peerConnectionClient例子来分析,首先连接服务器,当有其他客户端上线后会接收到通知,界面增加对方主机的名称,双击后再主界面消息响应层会进入ConnectToPeer函数

//peerconnectionClient的消息分发函数
bool MainWnd::PreTranslateMessage(MSG* msg) {
  bool ret = false;
  if (msg->message == WM_CHAR) {
    if (msg->wParam == VK_TAB) {
      HandleTabbing();
      ret = true;
    } else if (msg->wParam == VK_RETURN) {
      //选中回车会进入该函数
      OnDefaultAction();
      ret = true;
    } else if (msg->wParam == VK_ESCAPE) {
      if (callback_) {
        if (ui_ == STREAMING) {
          callback_->DisconnectFromCurrentPeer();
        } else {
          callback_->DisconnectFromServer();
        }
      }
    }
  } else if (msg->hwnd == NULL && msg->message == UI_THREAD_CALLBACK) {
    callback_->UIThreadCallback(static_cast<int>(msg->wParam),
                                reinterpret_cast<void*>(msg->lParam));
    ret = true;
  }
  return ret;
}

//connect to peer 开始连接对方

void Conductor::ConnectToPeer(int peer_id)  
{
    //初始化peerConnection
     if (InitializePeerConnection())
     {
        peer_id_ = peer_id;
        //成功就创建offer
        peer_connection_->CreateOffer(this, NULL);
      }
}

通过上面代码可以看去,首先初始化peerconnection对象,然后调用peerconnection的CreatorOffer函数

先来分析InitializePeerConnection函数
/***********InitializePeerConnection分析************************
************ 包括本地peerconnection创键 ************************
************增加本地stream,增加音视频信息**********************/

bool Conductor::InitializePeerConnection()
{

      //创建peer_connection_factory_工厂,然后通过工厂创建peerconnection
      peer_connection_factory_  = webrtc::CreatePeerConnectionFactory();
      
      //然后通过peer_connection_factory_工厂来创建peerconnection
       if (!CreatePeerConnection(DTLS_ON))
       {
         main_wnd_->MessageBox("Error",
        "CreatePeerConnection failed", true);
        DeletePeerConnection();
        }
        //创建成功,添加本地streams
        AddStreams();
}

InitializePeerConnection 其实也做了两步事情,首先创建peer_connection_factory_ ,通过peer_connection_factory_ 来创建peerconnection对象,创建成功后,再给peerConnection添加本地流(包括视频轨与音频轨信息)

创建 peerconnection对象
 

//peerconnection通过factory来创建
bool Conductor::CreatePeerConnection(bool dtls)
{
  //ice相关
  webrtc::PeerConnectionInterface::RTCConfiguration config;
  webrtc::PeerConnectionInterface::IceServer server;
  server.uri = GetPeerConnectionString();
  config.servers.push_back(server);

  //是否dtls传输,配置项
  webrtc::FakeConstraints constraints;
  if (dtls) {
    constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
                            "true");
  } else {
    constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
                            "false");
  }
 
  //创建peerconnection
  peer_connection_ = peer_connection_factory_->CreatePeerConnection(
      config, &constraints, NULL, NULL, this);
  return peer_connection_.get() != NULL;
}

peerconnection创建完成后,所有的音视频通话相关都是基于peerconnection来完成了,所以要想与对方进行通话,首先要把自己的信息准备好,在InitializePeerConnection中做了添加自己的本地流信息这一步,即先把自己的音频轨与视频轨准备好

//addStreams
void Conductor::AddStreams()
{
  //创建音频轨
  rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
      peer_connection_factory_->CreateAudioTrack(
          kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
  //创建视频轨
  rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
      peer_connection_factory_->CreateVideoTrack(
          kVideoLabel,
          peer_connection_factory_->CreateVideoSource(OpenVideoCaptureDevice(), NULL)));
 
  //视频轨增加本地渲染 ,因为本地实时视频要查看                                                  
  main_wnd_->StartLocalRenderer(video_track);

  //创建stream
  rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
      peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);

  //stream中添加创建的音频轨与视频轨信息
  stream->AddTrack(audio_track);
  stream->AddTrack(video_track);
 
  //peerconnection添加本地stream
  if (!peer_connection_->AddStream(stream)) {
    LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
  }
  typedef std::pair<std::string,
                    rtc::scoped_refptr<webrtc::MediaStreamInterface> >
      MediaStreamPair;
  active_streams_.insert(MediaStreamPair(stream->label(), stream));
  //切换到stream流展示界面
  main_wnd_->SwitchToStreamingUI();
}

至此InitializePeerConnection分析就结束了,首先创建peerconnection对象,创建成功后,所有的音视频通话相关操作都由这个peerconnection来完成;既然 要音视频通话,就需要先把自己的音视频信息准备好,所以第二部就是把自己的本地流数据准备好(本地的音频流与视频流),加入到成员变量strean对象中
/***********InitializePeerConnection分析结束************************/
/*******************************************************************/
/*******************************************************************/

 

webrtc建立会话的核心就是信令交互,信令交互的核心则是交换sdp信息,即媒协商,简单说来,就是A与B进行音视频通话,首先要知道对方支持的编解码格式,然后自己支持的编解码格式,webrtc 中还加入了ice 候选者信息 然后大家交换一下,达成一个大家都有的音视频编解码信息,与ice相关信息;ice相关信息用来p2p使用, 这部完成后才能进行音视频通话

简单来说就是即先获取自己本地的描述信息(为媒体协商做准备),然后与对方进行媒体协商,协商成功后进行音视频会话

creatoteOffer就是获取自己本地的媒体描述信息


//peerconnection本地初始化完成后,发起者就需要createOffer,俩邀请远端的客户端来就行通话,这里createOffer
//是个宏观概念,就是创建本地的媒体描述信息,创建成功后,通过虚函数回调OnSucces

/************************peer_connection CreateOffer 分析 ****************************************/
/************************peer_connection 发起者 调用CreateOffer **********************************/
/*************************************************************************************************/

// peerconnection.cc中的createOffer实现
//第一个参数,webrtc::CreateSessionDescriptionObserver *指针成功后的回调接口类,即conductor类的this指针,因为conductor继承了这个observer类
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,const MediaConstraintsInterface* constraints)
{

  //offer 选项
  RTCOfferAnswerOptions options;

  bool value;
  size_t mandatory_constraints = 0;

  if (FindConstraint(constraints,
                     MediaConstraintsInterface::kOfferToReceiveAudio,
                     &value,
                     &mandatory_constraints)) {
    options.offer_to_receive_audio =
        value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
  }

  if (FindConstraint(constraints,
                     MediaConstraintsInterface::kOfferToReceiveVideo,
                     &value,
                     &mandatory_constraints)) {
    options.offer_to_receive_video =
        value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
  }

  if (FindConstraint(constraints,
                     MediaConstraintsInterface::kVoiceActivityDetection,
                     &value,
                     &mandatory_constraints)) {
    options.voice_activity_detection = value;
  }

  if (FindConstraint(constraints,
                     MediaConstraintsInterface::kIceRestart,
                     &value,
                     &mandatory_constraints)) {
    options.ice_restart = value;
  }

  if (FindConstraint(constraints,
                     MediaConstraintsInterface::kUseRtpMux,
                     &value,
                     &mandatory_constraints)) {
    options.use_rtp_mux = value;
  }

  //获取option后,与过本地的sdp描述结合,调用webrtcSession的createoffer    
  CreateOffer(observer, options);
}

//调用了重载的CreatorCoffer函数
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
                                 const RTCOfferAnswerOptions& options) {
  TRACE_EVENT0("webrtc", "PeerConnection::CreateOffer");
  if (!VERIFY(observer != nullptr)) {
    LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
    return;
  }

  cricket::MediaSessionOptions session_options;
  //获取本地sdp描述信息
  if (!GetOptionsForOffer(options, &session_options)) {
    std::string error = "CreateOffer called with invalid options.";
    LOG(LS_ERROR) << error;
    PostCreateSessionDescriptionFailure(observer, error);
    return;
  }
  调用webrtcSession的createoffer    
  session_->CreateOffer(observer, options, session_options);
}

可以看到上面最终调用了webrtcSession的createOffer 函数,peerConnection与对方通话的核心类是webrtcSession类,一个连接相当于一个session,

/*调用webrtc_session_desc_factory_的createOffer*/
void WebRtcSession::CreateOffer(
    CreateSessionDescriptionObserver* observer,
    const PeerConnectionInterface::RTCOfferAnswerOptions& options,
    const cricket::MediaSessionOptions& session_options) {
  webrtc_session_desc_factory_->CreateOffer(observer, options, session_options);
}

/*
WebRtcSessionDescriptionFactory 的creatorOffer
*/
void WebRtcSessionDescriptionFactory::CreateOffer(
    CreateSessionDescriptionObserver* observer,
    const PeerConnectionInterface::RTCOfferAnswerOptions& options,
    const cricket::MediaSessionOptions& session_options)
{
    /*... 最终调用了InternalCreateOffer(request);*/
    
    InternalCreateOffer(request);
}

/*InternalCreateOffer */
void WebRtcSessionDescriptionFactory::InternalCreateOffer(CreateSessionDescriptionRequest request)
{
  cricket::SessionDescription* desc(session_desc_factory_.CreateOffer(
      request.options, session_->local_description()
                           ? session_->local_description()->description()
                           : nullptr));

  JsepSessionDescription* offer(new JsepSessionDescription(
      JsepSessionDescription::kOffer));
  if (!offer->Initialize(desc, session_id_,
                         rtc::ToString(session_version_++))) {
    delete offer;
    PostCreateSessionDescriptionFailed(request.observer,
                                       "Failed to initialize the offer.");
    return;
  }
  if (session_->local_description())
  {
    for (const cricket::ContentInfo& content :session_->local_description()->description()->contents())
    {
      // Include all local ICE candidates in the SessionDescription unless
      // the remote peer has requested an ICE restart.
      if (!request.options.transport_options[content.name].ice_restart)
      {
        //将candidate加入进offer
        CopyCandidatesFromSessionDescription(session_->local_description(),content.name, offer);
      }
    }
  }
  //发送创建成功消息,然后消息处理调用之前observer的OnSuccess函数,
  PostCreateSessionDescriptionSucceeded(request.observer, offer);
}

将所有需要的sdp信息加入到session中后,至此这一步就算完成,然后发送MSG_CREATE_SESSIONDESCRIPTION_SUCCESS消息,在跨线程消息处理函数中最终调用observer中的OnSuccess函数,即conductor的OnSuccess函数
 

/*
PostCreateSessionDescriptionSucceeded
*/
void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
    CreateSessionDescriptionObserver* observer,
    SessionDescriptionInterface* description) {
  CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
  msg->description.reset(description);
  //发送MSG_CREATE_SESSIONDESCRIPTION_SUCCESS成功消息,将observer带进去
  signaling_thread_->Post(RTC_FROM_HERE, this,
                          MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
}

/*处理MSG_CREATE_SESSIONDESCRIPTION_SUCCESS完成消息*/
void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg)
 {
  switch (msg->message_id)
  {
    case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS:
    {
      CreateSessionDescriptionMsg* param =
          static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
      //回调OnSuccess函数
      param->observer->OnSuccess(param->description.release());
      delete param;
      break;
      //...
    }
 }
}

总结:虽然creatorOffer函数很复杂,其实这个操作的核心思想就是创建自己的本地媒体描述信息并保存,即创建本地sdp保存,
成功后会通过虚函数回调初始调用类的OnSuccess函数

/************************peer_connection CreateOffer分析析结束*************************************/
/*************************************************************************************************/
/*************************************************************************************************/

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebRTC PeerConnectionWebRTC技术框架中重要的一环,它提供了实时音视频通信的能力。如果你想在你的应用程序中使用PeerConnection,你需要经过以下步骤将它引入进来: 1. 首先,你需要在你的应用程序中引入WebRTC的JavaScript库。你可以通过在你的HTML文件中引入以下代码来实现: ```<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>``` 2. 接下来,你需要在你的应用程序中创建PeerConnection实例。创建实例时,你需要指定ICE服务器的URL。此外,你还可以将本地音视频流添加到PeerConnection中。 ``` var pc = new RTCPeerConnection({ 'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }] }); navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(stream) { var videoTrack = stream.getVideoTracks()[0]; var audioTrack = stream.getAudioTracks()[0]; pc.addTrack(videoTrack, stream); pc.addTrack(audioTrack, stream); }); ``` 3. 现在,你已经成功地创建了PeerConnection实例,并添加了本地音视频流。下一步是启动ICE候选项协商,以建立远程音视频流。这可以通过创建一个SDP交换会话,以与远程对等方交换SDP来实现。 ``` pc.createOffer().then(function(offer) { return pc.setLocalDescription(offer); }).then(function() { // send offer to remote peer }); ``` 以上就是引入WebRTC PeerConnection的方法。当然,以上代码只是基础代码,并不能实现完整的音视频通信。在实际应用中,还需要处理候选项协商、SDP交换、ICE连接状态等。但不管怎样,PeerConnection作为WebRTC技术的核心,对于实现实时音视频通信至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值