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