webrtc-官方demo客户端分析
peerconnection_client分析笔记
Windows版本的peerconnection_client demo是一个win32程序,入口函数为main.cc里面的wWinMain,程序整体流程就从这个入口函数下手开始分析。
1.demo中主要的类的关系
整个demo中有3个主要的类分别是窗口类MainWnd,PeerConnectionClient和Conductor。MainWnd主要功能是显示界面并处理界面消息,PeerConnectionClient类,他的作用是与信令服务器来进行TCP通信。最后是联系MainWnd和PeerConnectionClient的类Conductor,Conductor实现了MainWnd的控制接口MainWndCallback和PeerConnectionClient的消息回调接口PeerConnectionClientObserver,同时它还实现与服务器进行媒体通信调用。他们之间的简单的关系如下:
从图中可以看出,Conductor是PeerConnectionClient的“观察者”,当PeerConnectionClient收到网络消息后,会通过调用相应的接口来通知Conductor,然后界面显示;同时Conductor实现MainWndCallback接口,界面按钮消息通过他传递给PeerConnectionClient。
2.入口函数wWinMain分析
入口wWinMain完成了三个主要类的创建和直接关系的建立,然后进入消息循环等到用户操作。
//创建PeerConnectionClient
//PeerConnectionClient主要用来处理与信令服务器的tcp通讯
//它有两个Win32Socket:control_socket_和hanging_get_,
//在PeerConnectionClient::DoConnect()中创建,并在PeerConnectionClient::InitSocketSignals()中连接好socket的信号。
PeerConnectionClient client;
//scoped_refptr 是一个智能指针
//RefCountedObject实现了一个线程安全的引用计数功能
//代码的作用是创建了一个Conductor对象并用conductor指向它
rtc::scoped_refptr<Conductor> conductor(
new rtc::RefCountedObject<Conductor>(&client, &wnd));
3.窗体消息分析
窗体的消息是在MainWnd的OnMessage函数中进行处理的。
3.1 链接服务器
当点击connect按钮时
//connect按钮按下
case WM_COMMAND:
if (button_ == reinterpret_cast<HWND>(lp)) {
if (BN_CLICKED == HIWORD(wp))
OnDefaultAction();
} else if (listbox_ == reinterpret_cast<HWND>(lp)) {
if (LBN_DBLCLK == HIWORD(wp)) {
OnDefaultAction();
}
}
return true;
点击connect按钮和连接服务器成功之后点击peer名都会进入OnDefaultAction函数
void MainWnd::OnDefaultAction() {
if (!callback_)
return;
//点击connect按钮
if (ui_ == CONNECT_TO_SERVER) {
std::string server(GetWindowText(edit1_));
std::string port_str(GetWindowText(edit2_));
int port = port_str.length() ? atoi(port_str.c_str()) : 0;
//登陆服务器
callback_->StartLogin(server, port);
//点击peer名
} else if (ui_ == LIST_PEERS) {
LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0);
if (sel != LB_ERR) {
LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0);
if (peer_id != -1 && callback_) {
//连接到peer
callback_->ConnectToPeer(peer_id);
}
}
} else {
MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
}
}
首先看一下怎么连接服务器的
void PeerConnectionClient::DoConnect() {
//创建control_socket和hanging_get_两个AsyncSocket,等待socket事件
//control_socket_和hanging_get_是两个指向AsyncSocket的智能指针
control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));
//连接socket信号和槽
InitSocketSignals();
char buffer[1024];
sprintfn(buffer, sizeof(buffer),
"GET /sign_in?%s HTTP/1.0\r\n\r\n", client_name_.c_str());
onconnect_data_ = buffer;
//control_socket_连接服务器,等待连接成功信号,调用OnConnect槽函数
bool ret = ConnectControlSocket();
if (ret)
state_ = SIGNING_IN;
if (!ret) {
callback_->OnServerConnectionFailure();
}
}
这里因为是异步的socket,通过注册socket信号的槽函数,会在socket连接成功和读socket的时候触发相应的事件,从而调用和信号绑定的槽函数
void PeerConnectionClient::InitSocketSignals() {
RTC_DCHECK(control_socket_.get() != NULL);
RTC_DCHECK(hanging_get_.get() != NULL);
// control_socket_关闭信号连接OnClose槽函数
control_socket_->SignalCloseEvent.connect(this,
&PeerConnectionClient::OnClose);
//hanging_get_关闭信号连接OnClose槽函数
hanging_get_->SignalCloseEvent.connect(this,
&PeerConnectionClient::OnClose);
//control_socket_连接信号连接OnConnect槽函数
control_socket_->SignalConnectEvent.connect(this,
&PeerConnectionClient::OnConnect);
//hanging_get_连接信号连接OnHangingGetConnect槽函数
hanging_get_->SignalConnectEvent.connect(this,
&PeerConnectionClient::OnHangingGetConnect);
//control_socket_读信号连接了OnRead槽函数
control_socket_->SignalReadEvent.connect(this,
&PeerConnectionClient::OnRead);
//hanging_get_读信号连接了OnHangingGetRead槽函数
hanging_get_->SignalReadEvent.connect(this,
&PeerConnectionClient::OnHangingGetRead);
}
所以直接去看PeerConnectionClient的OnConnect函数
void PeerConnectionClient::OnConnect(rtc::AsyncSocket* socket) {
RTC_DCHECK(!onconnect_data_.empty());
//control_socket_连接服务器成功就发送 "GET /sign_in?%s HTTP/1.0\r\n\r\n"
//成功后,服务器会返回当前 channel连接的其他peer ,"200 Added"
size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());
RTC_DCHECK(sent == onconnect_data_.length());
onconnect_data_.clear();
}
连接服务器成功之后会向服务器发送登录请求,成功之后,服务器返回当前channel连接的其他peer名,接着就去看一下PeerConnectionClient的OnRead函数。
OnRead函数会读取服务器返回的客户端列表,然后调用OnPeerConnected通知界面显示用户列表并显示。当点击peer名时会通过消息循环调用上面的OnDefaultAction函数,然后调用ConnectToPeer函数链接服务器。
void Conductor::ConnectToPeer(int peer_id) {
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;
}
//初始化PeerConnection
if (InitializePeerConnection()) {
peer_id_ = peer_id;
//创建一个offer!!!!
peer_connection_->CreateOffer(this, NULL);
} else {
main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
}
}
初始化链接工厂,包括音视频编解码参数。
bool Conductor::InitializePeerConnection() {
RTC_DCHECK(peer_connection_factory_.get() == NULL);
RTC_DCHECK(peer_connection_.get() == NULL);
//创建PeerConnectionFactory
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory();
....
//添加stream,切换到stream UI
AddStreams();
....
}
添加本地音视频流信息。
void Conductor::AddStreams() {
if (active_streams_.find(kStreamLabel) != active_streams_.end())
return; // Already added.
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);
//创建MediaStream采集并传送本地音视频
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);
stream->AddTrack(audio_track);
stream->AddTrack(video_track);
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));
//切换到StreamingUI
main_wnd_->SwitchToStreamingUI();
}
5.程序主要流程图
demo的主要程序流程图如下图所示
上一篇: triangle(三角形最短路径)