转载,并稍微做了修改,应该是版本不同,导致的差异。比如 AddStreams, 已经换成了AddTracks()。 仅作为自己的学习笔记,如有错误,欢迎指出。
整个demo中有3个主要的类分别是:
上一篇《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(lp)) {
if (BN_CLICKED == HIWORD(wp))
OnDefaultAction();
} else if (listbox_ == reinterpret_cast(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(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 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 video_device =
CapturerTrackSource::Create();
if (video_device) {
rtc::scoped_refptr 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());
}