编译运行
在成功编译WebRTC源码之后,没有编译成功的可以直接下载我编译好的。
https://github.com/hujianhua888/webrtc_vs2015
<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;
热门工具 换一换