在学习webRTC源码,下面分析peerConnection-client,仅作为自己的学习笔记,如有错误,欢迎指出。
示例中的client,是基于win32控制台写的单文档应用程序,所以其入口函数是winMain 下面先介绍一下Windows程序内部运行原理,以便更好理解这个程序。如果读者已经熟悉,可以跳过这部分。
鼠标点击、键盘按下、麦克风发声、打开摄像头等,都属于【输入输出设备】,①②,直接跟操作系统打交道,【操作系统】为【应用程序】留出了API,可以调用。【消息队列】存储待处理的消息。
举个例子:用户点击播放器,要播放声音。这个消息被放入【消息队列】,然后【应用程序】拿到这个消息(进行捕获),然后处理(多大声音播放,用什么解码器播放),然后将参数通过API传给【操作系统】,【操作系统】根据播放的要求,再调用【输出设备】,进行播放。
以下是winMain() 函数的结构:
创建一个完整的窗口,需要以下几步(显示和更新,可以算为1步):
窗口过程函数,也叫回调函数。回调函数原理:当应用程序收到给某一个窗口的消息时,就调用某一函数来处理这条消息。【调用】是由操作系统来完成的,当【如何处理】需要开发者来完成。
---------------------------------华丽分割线,上面↑ 是基础知识回顾,下面↓开始分析client 的winMain---------------------------------
首先,去 main.cc 文件中,找到程序入口。我们发现了wWinMain(). 这个函数中:
有registerWindowClass(), 这个全局静态函数是设计并注册窗口,这里有回调函数:static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
::CreateWindowExW() ;// 系统API函数,创建主窗口。
创建窗口(可以理解为框架窗口,也就是主窗口,最大最小化、菜单、标题、鼠标等的设置),
创建子窗口(也就是视类窗口)CreateChildWindows(); 并设计文本框、静态文本、按钮等 SwitchToConnectUI();
// main.cc ,程序入口
int PASCAL wWinMain(HINSTANCE instance,
HINSTANCE prev_instance,
wchar_t* cmd_line,
int cmd_show)
{
// 初始化套接字状态,和多线程状态
rtc::WinsockInitializer winsock_init;
rtc::Win32SocketServer w32_ss;
rtc::Win32Thread w32_thread(&w32_ss);
rtc::ThreadManager::Instance()->SetCurrentThread(&w32_thread);
// 传入参数,argc=1, argv[0]=client.exe的路径
rtc::WindowsCommandLineArguments win_args;
int argc = win_args.argc();
char** argv = win_args.argv();
rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
if (FLAG_help) {
rtc::FlagList::Print(NULL, false);
return 0;
}
webrtc::test::ValidateFieldTrialsStringOrDie(FLAG_force_fieldtrials);
// InitFieldTrialsFromString stores the char*, so the char array must outlive
// the application.
webrtc::field_trial::InitFieldTrialsFromString(FLAG_force_fieldtrials);
// Abort if the user specifies a port that is outside the allowed
// range [1, 65535].
if ((FLAG_port < 1) || (FLAG_port > 65535)) {
printf("Error: %i is not a valid port.\n", FLAG_port);
return -1;
}
// 创建窗口,其中窗口的设计和注册是在create()函数中完成的
MainWnd wnd(FLAG_server, FLAG_port, FLAG_autoconnect, FLAG_autocall);
if (!wnd.Create()) {
RTC_NOTREACHED();
return -1;
}
rtc::InitializeSSL();
//创建PeerConnectionClient
//PeerConnectionClient主要用来处理与信令服务器的tcp通讯
//它有两个Win32Socket:control_socket_和hanging_get_,
//在PeerConnectionClient::DoConnect()中创建,并在PeerConnectionClient::InitSocketSignals()中连接好socket的信号。
PeerConnectionClient client;
//scoped_refptr 是一个智能指针
//RefCountedObject实现了一个线程安全的引用计数功能
//代码的作用是创建了一个Conductor对象并用conductor指向它
rtc::scoped_refptr conductor(
new rtc::RefCountedObject(&client, &wnd));
// 开始消息循环
// Main loop.
MSG msg;
BOOL gm;
while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
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, 0, 0)) != 0 && gm != -1) {
if (!wnd.PreTranslateMessage(&msg)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}
rtc::CleanupSSL();
return 0;
}
main_win.cc, 注册窗口、创建窗口
bool MainWnd::Create() {
RTC_DCHECK(wnd_ == NULL);
if (!RegisterWindowClass()) // 注册窗口
return false;
ui_thread_id_ = ::GetCurrentThreadId();
// 创建窗口,可以认为是mainFrame窗口
wnd_ =
::CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, kClassName, L"WebRTC",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
::SendMessage(wnd_, WM_SETFONT, reinterpret_cast(GetDefaultFont()),
TRUE);
// 这里可以理解为view类窗口
CreateChildWindows();
SwitchToConnectUI();
return wnd_ != NULL;
}
main_win.cc 设计与注册窗口
// 设计并注册窗口
// static
bool MainWnd::RegisterWindowClass() {
if (wnd_class_)
return true;
WNDCLASSEXW wcex = {sizeof(WNDCLASSEX)};
wcex.style = CS_DBLCLKS;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1);
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.lpfnWndProc = &WndProc; // 回调函数
wcex.lpszClassName = kClassName;
wnd_class_ = ::RegisterClassExW(&wcex); // 注册窗口
RTC_DCHECK(wnd_class_ != 0);
return wnd_class_ != 0;
}
在上面设计注册行数中,回调函数WndProc(), 也就是窗口过程函数:
LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
下图为跟踪的过程,其中2/3/4步骤,其实是给了用户再次修改窗口格式的机会。
---------------------- 华丽分割线,上面↑ 是分析client 的winMain,下面↓开始分析client的运行过程 ------------------------------------------
整个demo中有3个主要的类分别是:
参考链接:https://blog.csdn.net/xipiaoyouzi/article/details/90712909
参考链接:https://blog.csdn.net/qq_24283329/article/details/71791463
参考链接:https://blog.csdn.net/qq_24283329/article/details/71885121