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

转载,并稍微做了修改,应该是版本不同,导致的差异。比如 AddStreams, 已经换成了AddTracks()。 仅作为自己的学习笔记,如有错误,欢迎指出。

整个demo中有3个主要的类分别是:

  1. 窗口类MainWnd,它的主要功能是实现了一个窗体程序。MainWnd 是封装好的创建窗口的类,方便winMain函数进行调用。
  2. PeerConnectionClient类,他的作用是与信令服务器(server)来进行TCP通信,
  3. 将MainWnd类和PeerConnectionClient类 连接在一起的Conductor类,Conductor实现了MainWndCallback和PeerConnectionClientObserver接口,当PeerConnectionClient和MainWnd完成某个事件时,会通过调用相应的接口来通知Conductor。
  4. main_wnd.h 文件中,定义了很多纯虚函数,这些纯虚函数,在conductor 中被重新定义内容,即有 override 标志的函数。

上一篇《webRTC示例分析(三)——peerConnection-client》主要分析了窗口的创建,这一篇分析一下,点击button之后,client端的消息响应过程。

窗体的消息是在MainWnd的 OnMessage() 函数中进行处理的。

当点击connect按钮时,OnMessage() 接收消息响应

main_wnd  类进行响应

bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
  switch (msg) {
   ......
    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;

    case WM_CLOSE:
      if (callback_)
        callback_->Close();
      break;
  }
  return false;
}

点击connect按钮和连接服务器成功之后,会进入OnDefaultAction函数。MainWnd 类中。

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);    // 登录端口和IP
  } // 点击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);
       // 连接到peer
      if (peer_id != -1 && callback_) {
        callback_->ConnectToPeer(peer_id);
      }
    }
  } else {
    ::MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
  }
}

首先看一下怎么连接服务器的:

PeerConnectionClient类

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];
  snprintf(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的时候触发相应的事件,从而调用和信号绑定的槽函数。

PeerConnectionClient类

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函数, 

屡一下继承关系:

class AsyncSocket : public Socket

class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<>

socket->Send(), // 调用Socket类中的Send()函数。

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) {
  size_t content_length = 0;
  if (ReadIntoBuffer(socket, &control_data_, &content_length)) {
    size_t peer_id = 0, eoh = 0;
    bool ok =
        ParseServerResponse(control_data_, content_length, &peer_id, &eoh);
    if (ok) {
      if (my_id_ == -1) {
        // First response.  Let's store our server assigned ID.
        RTC_DCHECK(state_ == SIGNING_IN);
        my_id_ = static_cast<int>(peer_id);
        RTC_DCHECK(my_id_ != -1);

        // The body of the response will be a list of already connected peers.
        if (content_length) {
          size_t pos = eoh + 4;
          while (pos < control_data_.size()) {
            size_t eol = control_data_.find('\n', pos);
            if (eol == std::string::npos)
              break;
            int id = 0;
            std::string name;
            bool connected;
            if (ParseEntry(control_data_.substr(pos, eol - pos), &name, &id,
                           &connected) &&
                id != my_id_) {
              peers_[id] = name;
              callback_->OnPeerConnected(id, name);// 连接成功
            }
            pos = eol + 1;
          }
        }
        RTC_DCHECK(is_connected());
        callback_->OnSignedIn();    //登录服务器成功之后,切换到显示已登录用户列表UI
      } else if (state_ == SIGNING_OUT) {
        Close();
        callback_->OnDisconnected();
      } else if (state_ == SIGNING_OUT_WAITING) {
        SignOut();
      }
    }

    control_data_.clear();

    if (state_ == SIGNING_IN) {
      RTC_DCHECK(hanging_get_->GetState() == rtc::Socket::CS_CLOSED);
      state_ = CONNECTED;
      hanging_get_->Connect(server_address_);
    }
  }
}

这时就到了显示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, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());    
  } else {
    main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
  }
}
bool Conductor::InitializePeerConnection() {
  RTC_DCHECK(!peer_connection_factory_);
  RTC_DCHECK(!peer_connection_);

    //创建 PeerConnectionFactory
  peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
      nullptr /* network_thread */, nullptr /* worker_thread */,
      nullptr /* signaling_thread */, nullptr /* default_adm */,
      webrtc::CreateBuiltinAudioEncoderFactory(),
      webrtc::CreateBuiltinAudioDecoderFactory(),
      webrtc::CreateBuiltinVideoEncoderFactory(),
      webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
      nullptr /* audio_processing */);

  if (!peer_connection_factory_) {
    main_wnd_->MessageBox("Error", "Failed to initialize PeerConnectionFactory",
                          true);
    DeletePeerConnection();
    return false;
  }

  if (!CreatePeerConnection(/*dtls=*/true)) {
    main_wnd_->MessageBox("Error", "CreatePeerConnection failed", true);
    DeletePeerConnection();
  }

  AddTracks();    // 添加轨(addStreams())

  return peer_connection_ != nullptr;
}

然后就开始进行通信了,添加轨,也就是视频轨、音频轨等。对应旧版本的AddStream().

void Conductor::AddTracks() {
  if (!peer_connection_->GetSenders().empty()) {
    return;  // Already added tracks.
  }
    // 创建音频轨
  rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
      peer_connection_factory_->CreateAudioTrack(
          kAudioLabel, peer_connection_factory_->CreateAudioSource(
                           cricket::AudioOptions())));
  auto result_or_error = peer_connection_->AddTrack(audio_track, {kStreamId});
  if (!result_or_error.ok()) {
    RTC_LOG(LS_ERROR) << "Failed to add audio track to PeerConnection: "
                      << result_or_error.error().message();
  }

    //创建视频轨
  rtc::scoped_refptr<CapturerTrackSource> video_device =
      CapturerTrackSource::Create();
  if (video_device) {
    rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track_(
        peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device));
    main_wnd_->StartLocalRenderer(video_track_);

    result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId});
    if (!result_or_error.ok()) {
      RTC_LOG(LS_ERROR) << "Failed to add video track to PeerConnection: "
                        << result_or_error.error().message();
    }
  } else {
    RTC_LOG(LS_ERROR) << "OpenVideoCaptureDevice failed";
  }

  main_wnd_->SwitchToStreamingUI();
}

根据获取的server ip 和port ,登录server

void Conductor::StartLogin(const std::string& server, int port) {
  if (client_->is_connected())
    return;
  server_ = server;
  client_->Connect(server, port, GetPeerName());
}

猜你喜欢

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