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. UIView常用属性与方法/UIKit继承结构

    UIView常用属性与方法 @interface UIView : UIResponder<NSCoding, UIAppearance, UIAppearanceContainer, UIDy ...

  2. $fn、$extends $fn.extends的用法,jquery的插件开发

    原文链接:http://caibaojian.com/jquery-extend-and-jquery-fn-extend.html Query.fn.extend(); jQuery.extend( ...

  3. 剑指offer--25.二叉树的镜像

    时间限制:1秒 空间限制:32768K 热度指数:238655 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 ...

  4. Report: Disappearing Wetlands Put Planet Life at Risk

    A new report warns that wetlands are disappearing three times faster than the world’s forests, with ...

  5. react 学习文章

    生命周期 学习笔记 一些坑 项目完成后总结 理解Immutable 是否要同构如何同构 react组件最佳实践 redux集合所有的state props来源, 页面所有状态 数据的唯一来源 reac ...

  6. pg_rewind 源端时间线发生改变 同步失败

    master-standby情况下,发生如下行为: 1.master停掉后,standby做为新的master(可能存在部分事物没有同步到standby中). 2.新master运行过程中出错,进行恢 ...

  7. jdk1.8 HashMap 实现 数组+链表/红黑树

    转载至 http://www.cnblogs.com/leesf456/p/5242233.html 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Ja ...

  8. Arcgis for Js之featurelayer实现空间查询和属性查询

    空间查询和属性查询是常用的两种对数据的检索与查询方式,在本节,将讲述Arcgis for Js下如何实现featurelayer的这两种查询方式,先贴图给大家看看: 实现界面 属性查询 空间查询 看完 ...

  9. linux rsync-文件同步和数据传输工具

    一.rsync的概述 rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync.rsync是Linux系统下的文件同步和数据传输工具,它采用“rsync” ...

  10. linux 系统监控某目录下文件及文件夹的变化

    inotifywait 是一个可以实时监控文件变动的工具,它利用linux内核中的inotify机制实现监控功能. 查看内核版本 [root@Oracle ~]# uname -r 2.6.32-22 ...