webrtc源码分析

编译运行

在成功编译WebRTC源码之后,没有编译成功的可以直接下载我编译好的。https://github.com/hujianhua888/webrtc_vs2015
成功编译后,可以运行WebRTC自带的例子体验一对一音视频通信效果。使用src/out/Debug 目录下的peerconnection_client.exe 和 peerconnection_server.exe两个文件,最终运行的架构图如下图所示:
这里写图片描述
效果如下:
这里写图片描述
局域网运行PeerConnection 例子需要用到两台电脑,并要求两台电脑都配置有摄像头和麦克风(没有的话,使用虚拟摄像头,看我之前的帧子)。测试步骤如下:
1. 电脑A运行peerconnection_server.exe。
2. 电脑A运行peerconnection_client.exe, Server一栏输入 localhost,点击Connect。
3. 电脑B运行peerconnection_client.exe,Server一栏输入电脑A的局域网ip地址,点击Connect。
4. 电脑A或电脑B双击列表框出现的第一个选项, 建立音视频通信。

代码解析

peerconnection_client 客户端代码框图如下:
这里写图片描述

界面消息分发处理:
下面函数,主要是私有消息,当我们通过程序处理得到sdp等信息时,通过调用
void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) 调用sdp信息写
jmessage[kSessionDescriptionSdpName] = sdp;
SendMessage(writer.write(jmessage));
通过main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg)放入队列中,然后
UIThreadCallback回调,进行消息的发送。

另外还有void Conductor::OnAddStream以及void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) 使用同样的方法进行消息发送。

bool MainWnd::PreTranslateMessage(MSG* msg) {
  bool ret = false;
  if (msg->message == WM_CHAR) {
    if (msg->wParam == VK_TAB) {
      HandleTabbing();
      ret = true;
    } else if (msg->wParam == VK_RETURN) {
      OnDefaultAction();
      ret = true;
    } else if (msg->wParam == VK_ESCAPE) {
      if (callback_) {
        if (ui_ == STREAMING) {
          callback_->DisconnectFromCurrentPeer();
        } else {
          callback_->DisconnectFromServer();
        }
      }
    }
  } else if (msg->hwnd == NULL && msg->message == UI_THREAD_CALLBACK) {
  //通过此函数把SDP,ICE等信息发送到服务器
    callback_->UIThreadCallback(static_cast<int>(msg->wParam),
                                reinterpret_cast<void*>(msg->lParam));

    ret = true;
  }
  return ret;
}

Ui界面点击通过回调函数如下。
以及socket收到消息时,通过下面回调进行处理。

LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
   OnMessage//点击UI界面分发的消息主要通过此函数处理。
   //bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result),实现包括连接peer功能。
}


LRESULT Win32Window::WndProc(HWND hwnd, UINT uMsg,
                             WPARAM wParam, LPARAM lParam) {
     OnMessage//收到服务器的消息通过此函数处理,并调用OnSocketNotify处理消息。
  }

OnMessage创建时用来处理界面的消息程序,OnPaint()里面包括对视频图像的显示功能。

  if (ui_ == STREAMING && remote_renderer && local_renderer) {
    AutoLock<VideoRenderer> local_lock(local_renderer);
    AutoLock<VideoRenderer> remote_lock(remote_renderer);
    ......
    }

ui_值为CONNECT_TO_SERVER,LIST_PEERS,首先是为CONNECT_TO_SERVER显示连接到服务器的界面,当连接完成后,并且读到其他peer名字后,会置为LIST_PEERS,显示列表的界面。当点击连接与其他peer连接后,会进入到显示视频的界面。

bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
  switch (msg) {
    case WM_ERASEBKGND:
      *result = TRUE;
      return true;

    case WM_PAINT:
      OnPaint();
      return true;

    case WM_SETFOCUS:
      if (ui_ == CONNECT_TO_SERVER) {
        SetFocus(edit1_);
      } else if (ui_ == LIST_PEERS) {
        SetFocus(listbox_);
      }
      return true;

    case WM_SIZE:
      if (ui_ == CONNECT_TO_SERVER) {
        LayoutConnectUI(true);
      } else if (ui_ == LIST_PEERS) {
        LayoutPeerListUI(true);
      }
      break;

    case WM_CTLCOLORSTATIC:
      *result = reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_WINDOW));
      return true;

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

Ui界面点击处理

OnDefaultAction 表示连接到服务器按下处理。
(1)如果点击connect,那么执行StartLogin表示连接到服务器。那么将准备建立连接到服务器。那么会执行Connect等函数。其中DoConnect表示建立连接,OnConnect 表示发送消息,并读取peer列表,并且显示。详见下面的描述

(2)当得到peer列表时,点击相对应的peer,那么执行ConnectToPeer,表示创建p2p连接。那么会调用CreatePeerConnection等函数,创建本地信息。详见下面的描述

  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);    
  } 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_) {
        callback_->ConnectToPeer(peer_id);
      }  
    }
  } else {
    MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
  }

连接到服务器
执行StartLogin连接到服务器,当建立连接后,然后会收到服务器发过来的信息,包括peer name以及id,下面过程是读取其他客户端peer的名字。并进行显示。主要是socket接口回调PeerConnectionClient::OnRead来得到消息。
void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) ->
callback_->OnSignedIn()->SwitchToPeerList->LayoutPeerListUI

 if (ParseEntry(control_data_.substr(pos, eol - pos), &name, &id,
                &connected) && id != my_id_) {
   peers_[id] = name;
   callback_->OnPeerConnected(id, name);
   }

当得到其他peer的名字,会把界面转为peer 列表。会调用PeerConnectionClient里面的callback_->OnSignedIn();
即void Conductor::OnSignedIn() ->SwitchToPeerList进行转化。
连接到其他peer
当点击其他peer名字,执行:
MainWnd::WndProc->OnDefaultAction->ConnectToPeer->CreatePeerConnection->CreatePeerConnection
MainWnd::WndProc->OnDefaultAction->ConnectToPeer->CreatePeerConnection-> AddStream
ConnectToPeer当第二次调用OnDefaultAction,当前客户端连接到另一个客户端,即建立p2p连接。那么其中会调用AddStreams(), creatoffer等函数。
其中在AddStreams()中调用:
StartLocalRenderer 显示本地流。
AddTrack 把流加载,用于进行传输。

本地消息发送函数

通过creatoffer得到本地的SDP信息后,然后需要把SDP信息发送到服务器。主要是通信把消息放在队列中,然后通过函数void Conductor::UIThreadCallback(int msg_id, void* data)实现发送。

void Conductor::UIThreadCallback(int msg_id, void* data)
{
 case SEND_MESSAGE_TO_PEER: {//SEND_MESSAGE_TO_PEER 表示把当前的信息发送给服务器
      LOG(INFO) << "SEND_MESSAGE_TO_PEER";
      std::string* msg = reinterpret_cast<std::string*>(data);//SDP信息
        if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) {
          LOG(LS_ERROR) << "SendToPeer failed";
          DisconnectFromServer();
        }
}
 case NEW_STREAM_ADDED: {//当建立p2p连接时,收到远程流,并且用于显示
   webrtc::MediaStreamInterface* stream =
       reinterpret_cast<webrtc::MediaStreamInterface*>(
       data);
   webrtc::VideoTrackVector tracks = stream->GetVideoTracks();
   // Only render the first track.
   if (!tracks.empty()) {
     webrtc::VideoTrackInterface* track = tracks[0];
     main_wnd_->StartRemoteRenderer(track);
   }
   stream->Release();
   break;
 }
 }

收到消息回调函数

OnMessage当建立socket连接时,用来接受服务器发送消息。其中调用OnSocketNotify

void Win32Socket::OnSocketNotify(SOCKET socket, int event, int error) {
    case FD_READ:
      if (error != ERROR_SUCCESS) {
        ReportWSAError("WSAAsync:read notify", error, addr_);
      } else {
        SignalReadEvent(this);
      }
      break;

    case FD_WRITE:
      if (error != ERROR_SUCCESS) {
        ReportWSAError("WSAAsync:write notify", error, addr_);
      } else {
        SignalWriteEvent(this);
      }
      break;
}

通过Win32Window::WndProc->OnSocketNotify->OnMessageFromPeer 表示收到远程的信息,其中会调用下面的OnAddStream等函数,被OnSocketNotify读函数调用,收到远程SDP等信息。

Win32Window::WndProc->OnSocketNotify->OnMessageFromPeer->SetRemoteDescription->void Conductor::OnAddStream 远程流调用函数,通过此方法接受远程信息,与socket读数据有点区别。

Win32Window::WndPro->void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate)->SendMessage(writer.write(jmessage))->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg)
收到ice信息。然后回调发送下一ice消息给远程。

如果收到SDP信息,在OnMessageFromPeer调用下面函数进行远程SDP设置。
SetRemoteDescription->OnAddStream->main_wnd_->QueueUIThreadCallback(NEW_STREAM_ADDED, stream.release());通过此函数回调准备发送下一次的ICE信息。然后通过UIThreadCallback主动发送信息。

通过Win32Window::WndProc->OnSocketNotify-> OnIceCandidate 收到ice消息

void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) 读服务器的数据。

void MainWnd::VideoRenderer::OnFrame 表示接受视频数据。

重新VideoRenderer:此函数,这样能被调用。
void OnFrame(const webrtc::VideoFrame& frame) override;

猜你喜欢

转载自blog.csdn.net/bvngh3247/article/details/81298131