Windows版本的peerconnection_client demo是一个win32程序,入口函数为main.cc里面的wWinMain,程序整体流程就从这个入口函数下手开始分析。

1.peerconnection_client demo中主要的类的关系

整个demo中有3个主要的类分别是窗口类MainWnd,它的主要功能是实现了一个窗体程序,然后是PeerConnectionClient类,他的作用是与信令服务器来进行TCP通信,最后是联系MainWnd和PeerConnectionClient的类Conductor,Conductor实现了MainWndCallback和PeerConnectionClientObserver接口,当PeerConnectionClient和MainWnd完成某个事件时,会通过调用相应的接口来通知Conductor。
然后从入口函数wWinMain开始来分析一下demo的函数调用流程

2.入口函数wWinMain分析

在函数的一开始,初始化了Windows socket,以及webRTC消息循环。
  rtc::EnsureWinsockInit();
rtc::Win32Thread w32_thread;
rtc::ThreadManager::Instance()->SetCurrentThread(&w32_thread);
然后去处理启动程序的时候传入的命令行参数,感觉不太重要,这里略过。
处理完命令行参数之后,调用了MainWnd的Create函数创建了窗体
 //创建窗体
MainWnd wnd(FLAG_server, FLAG_port, FLAG_autoconnect, FLAG_autocall);
if (!wnd.Create()) {
RTC_NOTREACHED();
return -;
}

紧接着就是初始化SSL以及创建PeerConnectionClient和Conductor

  //创建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));

完成了上面的操作之后就进入了窗体消息循环,等待窗体上的操作

//窗体消息循环
// Main loop.
MSG msg;
BOOL gm;
//GetMessage函数只有在接收到WM_QUIT消息时才返回0
while ((gm = ::GetMessage(&msg, NULL, , )) != && gm != -) {
if (!wnd.PreTranslateMessage(&msg)) {
//将虚拟键消息转换为字符消息
::TranslateMessage(&msg);
//分派一个消息到窗口进程由窗口进程对消息进行处理
::DispatchMessage(&msg);
}
} //上面的消息循环退出后,如果仍然链接着,继续处理消息
if (conductor->connection_active() || client.is_connected()) {
while ((conductor->connection_active() || client.is_connected()) &&
(gm = ::GetMessage(&msg, NULL, , )) != && gm != -) {
if (!wnd.PreTranslateMessage(&msg)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}

再以后是关闭SSL

rtc::CleanupSSL();

3.窗体消息分析

窗体的消息是在MainWnd的OnMessage函数中进行处理的。
当点击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()) : ;
//登陆服务器
callback_->StartLogin(server, port);
//点击peer名
} else if (ui_ == LIST_PEERS) {
LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, , );
if (sel != LB_ERR) {
LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, );
if (peer_id != - && 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[];
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函数

void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket)
{
....
//peer连接成功
callback_->OnPeerConnected(id, name);
....
//登录服务器成功之后,切换到显示已登录用户列表UI
callback_->OnSignedIn();
}

这时就到了显示peer名的界面了,当点击peer名时会通过消息循环调用上面的OnDefaultAction函数

void Conductor::ConnectToPeer(int peer_id) {
RTC_DCHECK(peer_id_ == -);
RTC_DCHECK(peer_id != -); 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的主要程序流程图如下图所示

6.后续计划

因为对整个p2p连接的建立和音视频流的传输等过程还不是很熟悉,所以这篇文章只是浅显的描述了一下demo的函数调用流程,后面会把整个过程的细节理一下,然后自己实现一个peerconnection_client。
 
参考文章:
http://blog.csdn.net/qq_24283329/article/category/6915582

peerconnection_client分析笔记的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. 4.View绘制分析笔记之onDraw

    上一篇文章我们了解了View的onLayout,那么今天我们来学习Android View绘制三部曲的最后一步,onDraw,绘制. ViewRootImpl#performDraw private ...

  3. 2.View绘制分析笔记之onMeasure

    今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被V ...

  4. 1.Android 视图及View绘制分析笔记之setContentView

    自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外.对于所有Android Developer来说,我们接触最多的控件就是View.通常 ...

  5. zeromq源码分析笔记之线程间收发命令(2)

    在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socke ...

  6. glusterfs 4.0.1 api 分析笔记1

    一般来说,我们写个客户端程序大概的样子是这样的: /* glfs_example.c */ // gcc -o glfs_example glfs_example.c -L /usr/lib64/ - ...

  7. SEH分析笔记(X64篇)

    SEH分析笔记(X64篇) v1.0.0 boxcounter 历史: v1.0.0, 2011-11-4:最初版本. [不介意转载,但请注明出处 www.boxcounter.com  附件里有本文 ...

  8. 【转载】Instagram架构分析笔记

    原文地址:http://chengxu.org/p/401.html Instagram 架构分析笔记 全部 技术博客 Instagram团队上个月才迎来第 7 名员工,是的,7个人的团队.作为 iP ...

  9. CentOS下使用Iptraf进行网络流量的分析笔记

    CentOS下使用Iptraf进行网络流量的分析笔记 一.概述 Iptraf是一款linux环境下,监控网络流量的一款绝佳的免费小软件. 本博客其他随笔参考: Centos安装流量监控工具iftop笔 ...

随机推荐

  1. axios 讲解 和vue搭建使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 条款5.了解c++默默编写并且调用了哪些函数。

    如果想在一个内含reference成员的class内支持赋值操作,必须自己定义copy assignment操作符.而且面对“内含有const成员的”class,编译器的反应也是相同的,由于更改con ...

  3. UI-自定义TabBar

    MyCustomTabBar.h文件 #import <UIKit/UIKit.h> @interface MyCustomTabBar : UITabBarController @end ...

  4. 剑指offer--35.数组中只出现一次的数字

    时间限制:1秒 空间限制:32768K 热度指数:198150 本题知识点: 数组 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. class ...

  5. 【python】《利用python进行数据分析》笔记

    [第三章]ipython C-a 到行首 C-e 到行尾 %timeit 测量语句时间,%time是一次,%timeit是多次. %pdb是自动调试的开关. %debug中,可以用b 12在第12行设 ...

  6. let防止变量声明提前

    let可以解决原来js中,一个函数中变量混乱的问题,因为以前var 定义的变量时, {}是不能限制变量作用域的. "use strict"; +function(){ var t= ...

  7. 解决pip安装太慢的问题

    经常在使用Python的时候需要安装各种模块,而pip是很强大的模块安装工具,但是由于国外官方pypi经常被墙,导致不可用,所以我们最好是将自己使用的pip源更换一下,这样就能解决被墙导致的装不上库的 ...

  8. Golang 编译成 DLL 文件

    golang 编译 dll 过程中需要用到 gcc,所以先安装 MinGW. windows 64 位系统应下载 MinGW 的 64 位版本: https://sourceforge.net/pro ...

  9. 类里边的构造函数可以被private修饰,在类的内部创建对象。利用这种特性创建单类模式

  10. 【Python系统学习】基础篇

    这次真的是最后一次了!第三次滚Python的基础.走了太多弯路.认真一点!菜鸟! 教程 转义字符 \ 可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\ ...