webrtc c++ (四)peerConnection creatrOffer分析

本文详细解析了WebRTC音视频通话的核心流程,从PeerConnection的创建到媒体描述信息的生成,再到信令交互与媒体协商过程。重点介绍了如何初始化PeerConnection,创建本地媒体流,以及创建Offer进行媒体描述。

基于通过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分析析结束*************************************/
/*************************************************************************************************/
/*************************************************************************************************/

 

内容概要:本文档围绕“基于序阻抗建模的虚拟同步发电机(VSG)并网逆变器仿真模型”展开,旨在复现高水平期刊论文中的关键研究成果。资源包含完整的Simulink仿真模型与配套MATLAB代码,重点聚焦于VSG在弱电网条件下的序阻抗建模方法、正负序阻抗特性分析、扫频法仿真验证及系统稳定性判据应用。内容深入探讨了VSG并网逆变器的小信号动态响应、阻抗交互机理及其在复杂电网环境中的稳定运行问题,同时关联多项前沿研究主题,如构网型变流器、光伏逆变器阻抗建模、低电压穿越控制等,体现了较强的学术复现与工程仿真价值。; 适合人群:面向具备电力系统、电力电子或自动化等相关专业背景的研究生、科研人员及工程技术人员,特别适用于从事新能源并网、逆变器控制策略设计、电网阻抗建模与稳定性分析等方向的研究者,以及需要复现顶刊论文仿真实验的技术人员。; 使用场景及目标:① 掌握VSG并网系统的序阻抗建模流程,理解正负序阻抗对弱电网稳定性的影响机制;② 学习并实现基于Simulink的扫频法仿真技术,完成阻抗特性辨识与奈奎斯特稳定性判断;③ 借助所提供的模型与代码快速搭建仿真平台,支撑科研论文撰写、课题申报、项目开发与学术复现实验。; 阅读建议:建议结合自身研究方向选择核心模块进行精读与调试,优先运行扫频与阻抗建模部分,重点关注模型参数设置、仿真步长选取与结果物理意义的解读;推荐参考文中提及的博士/硕士论文复现案例,深化理论与实践结合,提升对复杂电力电子系统稳定性问题的理解与建模能力。
内容概要:本文系统研究了光伏并网逆变器与虚拟同步发电机(VSG)在弱电网环境下的正负序阻抗建模方法,并基于Simulink平台构建了两者的精细化阻抗模型,实现了扫频仿真与稳定性对比分析。研究聚焦于不对称电网条件下系统的动态响应特性,通过分序阻抗建模揭示其在扰动下的交互机理,采用扫频法验证模型准确性,并结合奈奎斯特稳定性判据对两类逆变器的并网稳定性进行深入评估。内容涵盖从理论建模、仿真实现到稳定性判据应用的完整技术链条,尤其强调对VSG惯性与阻尼特性的模拟及其对系统稳定裕度的改善作用,为高比例新能源接入引发的弱电网稳定问题提供了有效的分析工具与解决方案,具备较高的学术研究价值与工程复现意义。; 适合人群:电力电子、电力系统自动化、新能源并网技术及相关专业的硕士/博士研究生、科研人员以及从事并网逆变器控制、电网稳定性分析的工程师。; 使用场景及目标:①掌握光伏并网逆变器与虚拟同步发电机的正负序阻抗建模核心技术;②熟练运用Simulink进行阻抗扫描(sweeping)与时域/频域联合仿真;③对比分析跟网型与构网型逆变器在弱电网中的稳定性能差异,为新型电力系统中构网型控制策略的设计与优化提供理论依据和技术支撑。; 阅读建议:建议结合文中提及的“博士论文复现”“期刊复现”等实例,下载配套的Simulink仿真模型与相关代码资源,动手实践阻抗建模与扫频全过程,深入理解锁相环、电流环等控制环节对序阻抗特性的影响,并可进一步拓展至多机并网、宽频振荡等复杂场景的稳定性研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值