概述
基于通过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分析析结束*************************************/
/*************************************************************************************************/
/*************************************************************************************************/
最后
以上就是清秀康乃馨为你收集整理的webrtc c++ (四)peerConnection creatrOffer分析的全部内容,希望文章能够帮你解决webrtc c++ (四)peerConnection creatrOffer分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复