peerconnection_client分析筆記

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 -1;
  }

緊接着就是初始化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, 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);
      }
    }
  }

複製代碼

再以後是關閉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()) : 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函數

複製代碼

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_ == -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的主要程序流程圖如下圖所示

6.後續計劃

因爲對整個p2p連接的建立和音視頻流的傳輸等過程還不是很熟悉,所以這篇文章只是淺顯的描述了一下demo的函數調用流程,後面會把整個過程的細節理一下,然後自己實現一個peerconnection_client。

 

參考文章:

http://blog.csdn.net/qq_24283329/article/category/6915582

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章