webRTC示例分析(三)——peerConnection-client

在学习webRTC源码,下面分析peerConnection-client,仅作为自己的学习笔记,如有错误,欢迎指出。

示例中的client,是基于win32控制台写的单文档应用程序,所以其入口函数是winMain 下面先介绍一下Windows程序内部运行原理,以便更好理解这个程序。如果读者已经熟悉,可以跳过这部分。

鼠标点击、键盘按下、麦克风发声、打开摄像头等,都属于【输入输出设备】,①②,直接跟操作系统打交道,【操作系统】为【应用程序】留出了API,可以调用。【消息队列】存储待处理的消息。

举个例子:用户点击播放器,要播放声音。这个消息被放入【消息队列】,然后【应用程序】拿到这个消息(进行捕获),然后处理(多大声音播放,用什么解码器播放),然后将参数通过API传给【操作系统】,【操作系统】根据播放的要求,再调用【输出设备】,进行播放。

以下是winMain() 函数的结构:

创建一个完整的窗口,需要以下几步(显示和更新,可以算为1步):

  1. 设计窗口
  2. 注册窗口
  3. 创建窗口
  4. 显示窗口
  5. 更新窗口
  6. 消息循环(消息循环不属于创建窗口)

窗口过程函数,也叫回调函数。回调函数原理:当应用程序收到给某一个窗口的消息时,就调用某一函数来处理这条消息。【调用】是由操作系统来完成的,当【如何处理】需要开发者来完成。

---------------------------------华丽分割线,上面↑ 是基础知识回顾,下面↓开始分析client  的winMain---------------------------------

首先,去 main.cc 文件中,找到程序入口。我们发现了wWinMain(). 这个函数中:

  1. 先初始化套接字状态,和多线程状态。并实例化获得当前线程。
  2. 再往下,发现了MainWnd  wnd, 并初始化,创建。在这个MainWnd上右键,转到声明,可以看到main_win.h  中,class MainWnd : public MainWindow {}。 MainWnd 继承MainWindow,并构造了一个MainWnd的对象。 用这wnd 调用create() 函数,进行创建窗口。这里是将窗口创建的过程,分装成了一个类
  3. 在main_win.h文件中的create()函数中:
  • 有registerWindowClass(), 这个全局静态函数是设计并注册窗口,这里有回调函数:static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)

  • ::CreateWindowExW()  ;// 系统API函数,创建主窗口。

  • 创建窗口(可以理解为框架窗口,也就是主窗口,最大最小化、菜单、标题、鼠标等的设置),

  • 创建子窗口(也就是视类窗口)CreateChildWindows(); 并设计文本框、静态文本、按钮等 SwitchToConnectUI();

  1. 上面准备工作做好以后,系统通知conductor。
  2. 消息循环。这里需要注意,为啥有两个消息循环?大胆猜测:一个用来和server交换数据,一个用来处理本应用程序消息。(跟踪调试后,发现:当没有连接server的时候,程序仅进入第一个while循环,第二个循环是关闭时,用来关闭各种函数,释放空间的。)
// 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> conductor(
      new rtc::RefCountedObject<Conductor>(&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<WPARAM>(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<HBRUSH>(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个主要的类分别是:

  1. 窗口类MainWnd,它的主要功能是实现了一个窗体程序,也就是把窗口的创建过程封装成类,方便主函数调用。
  2. PeerConnectionClient类,他的作用是与信令服务器(server)来进行TCP通信,
  3. 将MainWnd类和PeerConnectionClient类 连接在一起的Conductor类,Conductor实现了MainWndCallback和PeerConnectionClientObserver接口,当PeerConnectionClient和MainWnd完成某个事件时,会通过调用相应的接口来通知Conductor。

参考链接: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

猜你喜欢

转载自blog.csdn.net/qq_34732729/article/details/105798775