git-svn-id: http://webrtc.googlecode.com/svn/trunk@5 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
77ae29bc81
commit
278733b2d9
321
peerconnection/samples/client/conductor.cc
Normal file
321
peerconnection/samples/client/conductor.cc
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "peerconnection/samples/client/conductor.h"
|
||||
|
||||
#include "peerconnection/samples/client/defaults.h"
|
||||
#include "talk/base/logging.h"
|
||||
|
||||
Conductor::Conductor(PeerConnectionClient* client, MainWnd* main_wnd)
|
||||
: handshake_(NONE),
|
||||
waiting_for_audio_(false),
|
||||
waiting_for_video_(false),
|
||||
peer_id_(-1),
|
||||
video_channel_(-1),
|
||||
audio_channel_(-1),
|
||||
client_(client),
|
||||
main_wnd_(main_wnd) {
|
||||
// Create a window for posting notifications back to from other threads.
|
||||
bool ok = Create(HWND_MESSAGE, L"Conductor", 0, 0, 0, 0, 0, 0);
|
||||
ASSERT(ok);
|
||||
client_->RegisterObserver(this);
|
||||
main_wnd->RegisterObserver(this);
|
||||
}
|
||||
|
||||
Conductor::~Conductor() {
|
||||
ASSERT(peer_connection_.get() == NULL);
|
||||
Destroy();
|
||||
DeletePeerConnection();
|
||||
}
|
||||
|
||||
bool Conductor::has_video() const {
|
||||
return video_channel_ != -1;
|
||||
}
|
||||
|
||||
bool Conductor::has_audio() const {
|
||||
return audio_channel_ != -1;
|
||||
}
|
||||
|
||||
bool Conductor::connection_active() const {
|
||||
return peer_connection_.get() != NULL;
|
||||
}
|
||||
|
||||
void Conductor::Close() {
|
||||
if (peer_connection_.get()) {
|
||||
peer_connection_->Close();
|
||||
} else {
|
||||
client_->SignOut();
|
||||
}
|
||||
}
|
||||
|
||||
bool Conductor::InitializePeerConnection() {
|
||||
ASSERT(peer_connection_.get() == NULL);
|
||||
peer_connection_.reset(new webrtc::PeerConnection(GetPeerConnectionString()));
|
||||
peer_connection_->RegisterObserver(this);
|
||||
if (!peer_connection_->Init()) {
|
||||
DeletePeerConnection();
|
||||
} else {
|
||||
bool audio = peer_connection_->SetAudioDevice("", "", 0);
|
||||
LOG(INFO) << "SetAudioDevice " << (audio ? "succeeded." : "failed.");
|
||||
}
|
||||
return peer_connection_.get() != NULL;
|
||||
}
|
||||
|
||||
void Conductor::DeletePeerConnection() {
|
||||
peer_connection_.reset();
|
||||
handshake_ = NONE;
|
||||
}
|
||||
|
||||
void Conductor::StartCaptureDevice() {
|
||||
ASSERT(peer_connection_.get());
|
||||
if (main_wnd_->IsWindow()) {
|
||||
main_wnd_->SwitchToStreamingUI();
|
||||
|
||||
if (peer_connection_->SetVideoCapture("")) {
|
||||
peer_connection_->SetVideoRenderer(-1, main_wnd_->handle(), 0,
|
||||
0.7f, 0.7f, 0.95f, 0.95f);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PeerConnectionObserver implementation.
|
||||
//
|
||||
|
||||
void Conductor::OnError() {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
void Conductor::OnSignalingMessage(const std::string& msg) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
|
||||
bool shutting_down = (video_channel_ == -1 && audio_channel_ == -1);
|
||||
|
||||
if (handshake_ == OFFER_RECEIVED && !shutting_down)
|
||||
StartCaptureDevice();
|
||||
|
||||
// Send our answer/offer/shutting down message.
|
||||
// If we're the initiator, this will be our offer. If we just received
|
||||
// an offer, this will be an answer. If PeerConnection::Close has been
|
||||
// called, then this is our signal to the other end that we're shutting
|
||||
// down.
|
||||
if (handshake_ != QUIT_SENT) {
|
||||
SendMessage(handle(), SEND_MESSAGE_TO_PEER, 0,
|
||||
reinterpret_cast<LPARAM>(&msg));
|
||||
}
|
||||
|
||||
if (shutting_down) {
|
||||
handshake_ = QUIT_SENT;
|
||||
PostMessage(handle(), PEER_CONNECTION_CLOSED, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a remote stream is added
|
||||
void Conductor::OnAddStream(const std::string& stream_id, int channel_id,
|
||||
bool video) {
|
||||
LOG(INFO) << __FUNCTION__ << " " << stream_id;
|
||||
bool send_notification = (waiting_for_video_ || waiting_for_audio_);
|
||||
if (video) {
|
||||
ASSERT(video_channel_ == -1);
|
||||
video_channel_ = channel_id;
|
||||
waiting_for_video_ = false;
|
||||
LOG(INFO) << "Setting video renderer for channel: " << channel_id;
|
||||
bool ok = peer_connection_->SetVideoRenderer(channel_id,
|
||||
main_wnd_->handle(), 1, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ASSERT(ok);
|
||||
} else {
|
||||
ASSERT(audio_channel_ == -1);
|
||||
audio_channel_ = channel_id;
|
||||
waiting_for_audio_ = false;
|
||||
}
|
||||
|
||||
if (send_notification && !waiting_for_audio_ && !waiting_for_video_)
|
||||
PostMessage(handle(), MEDIA_CHANNELS_INITIALIZED, 0, 0);
|
||||
}
|
||||
|
||||
void Conductor::OnRemoveStream(const std::string& stream_id, int channel_id,
|
||||
bool video) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
if (video) {
|
||||
ASSERT(channel_id == video_channel_);
|
||||
video_channel_ = -1;
|
||||
} else {
|
||||
ASSERT(channel_id == audio_channel_);
|
||||
audio_channel_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PeerConnectionClientObserver implementation.
|
||||
//
|
||||
|
||||
void Conductor::OnSignedIn() {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
main_wnd_->SwitchToPeerList(client_->peers());
|
||||
}
|
||||
|
||||
void Conductor::OnDisconnected() {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
if (peer_connection_.get()) {
|
||||
peer_connection_->Close();
|
||||
} else if (main_wnd_->IsWindow()) {
|
||||
main_wnd_->SwitchToConnectUI();
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::OnPeerConnected(int id, const std::string& name) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
// Refresh the list if we're showing it.
|
||||
if (main_wnd_->current_ui() == MainWnd::LIST_PEERS)
|
||||
main_wnd_->SwitchToPeerList(client_->peers());
|
||||
}
|
||||
|
||||
void Conductor::OnPeerDisconnected(int id, const std::string& name) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
if (id == peer_id_) {
|
||||
LOG(INFO) << "Our peer disconnected";
|
||||
peer_id_ = -1;
|
||||
if (peer_connection_.get())
|
||||
peer_connection_->Close();
|
||||
}
|
||||
|
||||
// Refresh the list if we're showing it.
|
||||
if (main_wnd_->current_ui() == MainWnd::LIST_PEERS)
|
||||
main_wnd_->SwitchToPeerList(client_->peers());
|
||||
}
|
||||
|
||||
void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) {
|
||||
ASSERT(peer_id_ == peer_id || peer_id_ == -1);
|
||||
|
||||
if (handshake_ == NONE) {
|
||||
handshake_ = OFFER_RECEIVED;
|
||||
peer_id_ = peer_id;
|
||||
if (!peer_connection_.get()) {
|
||||
// Got an offer. Give it to the PeerConnection instance.
|
||||
// Once processed, we will get a callback to OnSignalingMessage with
|
||||
// our 'answer' which we'll send to the peer.
|
||||
LOG(INFO) << "Got an offer from our peer: " << peer_id;
|
||||
if (!InitializePeerConnection()) {
|
||||
LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance";
|
||||
client_->SignOut();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (handshake_ == INITIATOR) {
|
||||
LOG(INFO) << "Remote peer sent us an answer";
|
||||
handshake_ = ANSWER_RECEIVED;
|
||||
} else {
|
||||
LOG(INFO) << "Remote peer is disconnecting";
|
||||
handshake_ = QUIT_SENT;
|
||||
}
|
||||
|
||||
peer_connection_->SignalingMessage(message);
|
||||
|
||||
if (handshake_ == QUIT_SENT) {
|
||||
DisconnectFromCurrentPeer();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MainWndCallback implementation.
|
||||
//
|
||||
|
||||
void Conductor::StartLogin(const std::string& server, int port) {
|
||||
ASSERT(!client_->is_connected());
|
||||
if (!client_->Connect(server, port, GetPeerName())) {
|
||||
MessageBoxA(main_wnd_->handle(),
|
||||
("Failed to connect to " + server).c_str(),
|
||||
"Error", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::DisconnectFromServer() {
|
||||
if (!client_->is_connected())
|
||||
return;
|
||||
client_->SignOut();
|
||||
}
|
||||
|
||||
void Conductor::ConnectToPeer(int peer_id) {
|
||||
ASSERT(peer_id_ == -1);
|
||||
ASSERT(peer_id != -1);
|
||||
ASSERT(handshake_ == NONE);
|
||||
|
||||
if (handshake_ != NONE)
|
||||
return;
|
||||
|
||||
if (InitializePeerConnection()) {
|
||||
peer_id_ = peer_id;
|
||||
waiting_for_video_ = peer_connection_->AddStream(kVideoLabel, true);
|
||||
waiting_for_audio_ = peer_connection_->AddStream(kAudioLabel, false);
|
||||
if (waiting_for_video_ || waiting_for_audio_)
|
||||
handshake_ = INITIATOR;
|
||||
ASSERT(waiting_for_video_ || waiting_for_audio_);
|
||||
}
|
||||
|
||||
if (handshake_ == NONE) {
|
||||
::MessageBoxA(main_wnd_->handle(), "Failed to initialize PeerConnection",
|
||||
"Error", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::DisconnectFromCurrentPeer() {
|
||||
if (peer_connection_.get())
|
||||
peer_connection_->Close();
|
||||
}
|
||||
|
||||
//
|
||||
// Win32Window implementation.
|
||||
//
|
||||
|
||||
bool Conductor::OnMessage(UINT msg, WPARAM wp, LPARAM lp,
|
||||
LRESULT& result) { // NOLINT
|
||||
bool ret = true;
|
||||
if (msg == MEDIA_CHANNELS_INITIALIZED) {
|
||||
ASSERT(handshake_ == INITIATOR);
|
||||
bool ok = peer_connection_->Connect();
|
||||
ASSERT(ok);
|
||||
StartCaptureDevice();
|
||||
// When we get an OnSignalingMessage notification, we'll send our
|
||||
// json encoded signaling message to the peer, which is the first step
|
||||
// of establishing a connection.
|
||||
} else if (msg == PEER_CONNECTION_CLOSED) {
|
||||
LOG(INFO) << "PEER_CONNECTION_CLOSED";
|
||||
DeletePeerConnection();
|
||||
::InvalidateRect(main_wnd_->handle(), NULL, TRUE);
|
||||
waiting_for_audio_ = false;
|
||||
waiting_for_video_ = false;
|
||||
peer_id_ = -1;
|
||||
ASSERT(video_channel_ == -1);
|
||||
ASSERT(audio_channel_ == -1);
|
||||
if (main_wnd_->IsWindow()) {
|
||||
if (client_->is_connected()) {
|
||||
main_wnd_->SwitchToPeerList(client_->peers());
|
||||
} else {
|
||||
main_wnd_->SwitchToConnectUI();
|
||||
}
|
||||
} else {
|
||||
DisconnectFromServer();
|
||||
}
|
||||
} else if (msg == SEND_MESSAGE_TO_PEER) {
|
||||
bool ok = client_->SendToPeer(peer_id_,
|
||||
*reinterpret_cast<std::string*>(lp));
|
||||
if (!ok) {
|
||||
LOG(LS_ERROR) << "SendToPeer failed";
|
||||
DisconnectFromServer();
|
||||
}
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
117
peerconnection/samples/client/conductor.h
Normal file
117
peerconnection/samples/client/conductor.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
|
||||
#define PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "peerconnection/samples/client/main_wnd.h"
|
||||
#include "peerconnection/samples/client/peer_connection_client.h"
|
||||
#include "talk/app/peerconnection.h"
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
|
||||
|
||||
class Conductor
|
||||
: public webrtc::PeerConnectionObserver,
|
||||
public PeerConnectionClientObserver,
|
||||
public MainWndCallback,
|
||||
public talk_base::Win32Window {
|
||||
public:
|
||||
enum WindowMessages {
|
||||
MEDIA_CHANNELS_INITIALIZED = WM_APP + 1,
|
||||
PEER_CONNECTION_CLOSED,
|
||||
SEND_MESSAGE_TO_PEER,
|
||||
};
|
||||
|
||||
enum HandshakeState {
|
||||
NONE,
|
||||
INITIATOR,
|
||||
ANSWER_RECEIVED,
|
||||
OFFER_RECEIVED,
|
||||
QUIT_SENT,
|
||||
};
|
||||
|
||||
Conductor(PeerConnectionClient* client, MainWnd* main_wnd);
|
||||
~Conductor();
|
||||
|
||||
bool has_video() const;
|
||||
bool has_audio() const;
|
||||
bool connection_active() const;
|
||||
|
||||
void Close();
|
||||
|
||||
protected:
|
||||
bool InitializePeerConnection();
|
||||
void DeletePeerConnection();
|
||||
void StartCaptureDevice();
|
||||
|
||||
//
|
||||
// PeerConnectionObserver implementation.
|
||||
//
|
||||
|
||||
virtual void OnError();
|
||||
virtual void OnSignalingMessage(const std::string& msg);
|
||||
|
||||
// Called when a remote stream is added
|
||||
virtual void OnAddStream(const std::string& stream_id, int channel_id,
|
||||
bool video);
|
||||
|
||||
virtual void OnRemoveStream(const std::string& stream_id,
|
||||
int channel_id,
|
||||
bool video);
|
||||
|
||||
//
|
||||
// PeerConnectionClientObserver implementation.
|
||||
//
|
||||
|
||||
virtual void OnSignedIn();
|
||||
|
||||
virtual void OnDisconnected();
|
||||
|
||||
virtual void OnPeerConnected(int id, const std::string& name);
|
||||
|
||||
virtual void OnPeerDisconnected(int id, const std::string& name);
|
||||
|
||||
virtual void OnMessageFromPeer(int peer_id, const std::string& message);
|
||||
|
||||
//
|
||||
// MainWndCallback implementation.
|
||||
//
|
||||
|
||||
virtual void StartLogin(const std::string& server, int port);
|
||||
|
||||
virtual void DisconnectFromServer();
|
||||
|
||||
virtual void ConnectToPeer(int peer_id);
|
||||
|
||||
virtual void DisconnectFromCurrentPeer();
|
||||
|
||||
//
|
||||
// Win32Window implementation.
|
||||
//
|
||||
|
||||
virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp,
|
||||
LRESULT& result); // NOLINT
|
||||
|
||||
protected:
|
||||
HandshakeState handshake_;
|
||||
bool waiting_for_audio_;
|
||||
bool waiting_for_video_;
|
||||
int peer_id_;
|
||||
talk_base::scoped_ptr<webrtc::PeerConnection> peer_connection_;
|
||||
PeerConnectionClient* client_;
|
||||
MainWnd* main_wnd_;
|
||||
int video_channel_;
|
||||
int audio_channel_;
|
||||
};
|
||||
|
||||
#endif // PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
|
48
peerconnection/samples/client/defaults.cc
Normal file
48
peerconnection/samples/client/defaults.cc
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "peerconnection/samples/client/defaults.h"
|
||||
|
||||
const char kAudioLabel[] = "audio_label";
|
||||
const char kVideoLabel[] = "video_label";
|
||||
const uint16 kDefaultServerPort = 8888;
|
||||
|
||||
std::string GetEnvVarOrDefault(const char* env_var_name,
|
||||
const char* default_value) {
|
||||
std::string value;
|
||||
const char* env_var = getenv(env_var_name);
|
||||
if (env_var)
|
||||
value = env_var;
|
||||
|
||||
if (value.empty())
|
||||
value = default_value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string GetPeerConnectionString() {
|
||||
return GetEnvVarOrDefault("WEBRTC_CONNECT", "STUN stun.l.google.com:19302");
|
||||
}
|
||||
|
||||
std::string GetDefaultServerName() {
|
||||
return GetEnvVarOrDefault("WEBRTC_SERVER", "localhost");
|
||||
}
|
||||
|
||||
std::string GetPeerName() {
|
||||
char computer_name[MAX_PATH] = {0}, user_name[MAX_PATH] = {0};
|
||||
DWORD size = ARRAYSIZE(computer_name);
|
||||
::GetComputerNameA(computer_name, &size);
|
||||
size = ARRAYSIZE(user_name);
|
||||
::GetUserNameA(user_name, &size);
|
||||
std::string ret(user_name);
|
||||
ret += '@';
|
||||
ret += computer_name;
|
||||
return ret;
|
||||
}
|
30
peerconnection/samples/client/defaults.h
Normal file
30
peerconnection/samples/client/defaults.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef PEERCONNECTION_SAMPLES_CLIENT_DEFAULTS_H_
|
||||
#define PEERCONNECTION_SAMPLES_CLIENT_DEFAULTS_H_
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
#include "talk/base/basictypes.h"
|
||||
|
||||
extern const char kAudioLabel[];
|
||||
extern const char kVideoLabel[];
|
||||
extern const uint16 kDefaultServerPort;
|
||||
|
||||
std::string GetEnvVarOrDefault(const char* env_var_name,
|
||||
const char* default_value);
|
||||
std::string GetPeerConnectionString();
|
||||
std::string GetDefaultServerName();
|
||||
std::string GetPeerName();
|
||||
|
||||
#endif // PEERCONNECTION_SAMPLES_CLIENT_DEFAULTS_H_
|
57
peerconnection/samples/client/main.cc
Normal file
57
peerconnection/samples/client/main.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "peerconnection/samples/client/conductor.h"
|
||||
#include "peerconnection/samples/client/main_wnd.h"
|
||||
#include "peerconnection/samples/client/peer_connection_client.h"
|
||||
#include "system_wrappers/source/trace_impl.h"
|
||||
#include "talk/base/win32socketinit.h"
|
||||
|
||||
|
||||
int PASCAL wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
|
||||
wchar_t* cmd_line, int cmd_show) {
|
||||
talk_base::EnsureWinsockInit();
|
||||
|
||||
webrtc::Trace::CreateTrace();
|
||||
webrtc::Trace::SetTraceFile("peerconnection_client.log");
|
||||
webrtc::Trace::SetLevelFilter(webrtc::kTraceWarning);
|
||||
|
||||
MainWnd wnd;
|
||||
if (!wnd.Create()) {
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PeerConnectionClient client;
|
||||
Conductor conductor(&client, &wnd);
|
||||
|
||||
// Main loop.
|
||||
MSG msg;
|
||||
BOOL gm;
|
||||
while ((gm = ::GetMessage(&msg, NULL, 0, 0)) && gm != -1) {
|
||||
if (!wnd.PreTranslateMessage(&msg)) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (conductor.connection_active() || client.is_connected()) {
|
||||
conductor.Close();
|
||||
while ((conductor.connection_active() || client.is_connected()) &&
|
||||
(gm = ::GetMessage(&msg, NULL, 0, 0)) && gm != -1) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
396
peerconnection/samples/client/main_wnd.cc
Normal file
396
peerconnection/samples/client/main_wnd.cc
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "peerconnection/samples/client/main_wnd.h"
|
||||
|
||||
#include "talk/base/common.h"
|
||||
#include "talk/base/logging.h"
|
||||
|
||||
ATOM MainWnd::wnd_class_ = 0;
|
||||
const wchar_t MainWnd::kClassName[] = L"WebRTC_MainWnd";
|
||||
|
||||
// TODO(tommi): declare in header:
|
||||
std::string GetDefaultServerName();
|
||||
|
||||
namespace {
|
||||
void CalculateWindowSizeForText(HWND wnd, const wchar_t* text,
|
||||
size_t* width, size_t* height) {
|
||||
HDC dc = ::GetDC(wnd);
|
||||
RECT text_rc = {0};
|
||||
::DrawText(dc, text, -1, &text_rc, DT_CALCRECT | DT_SINGLELINE);
|
||||
::ReleaseDC(wnd, dc);
|
||||
RECT client, window;
|
||||
::GetClientRect(wnd, &client);
|
||||
::GetWindowRect(wnd, &window);
|
||||
|
||||
*width = text_rc.right - text_rc.left;
|
||||
*width += (window.right - window.left) -
|
||||
(client.right - client.left);
|
||||
*height = text_rc.bottom - text_rc.top;
|
||||
*height += (window.bottom - window.top) -
|
||||
(client.bottom - client.top);
|
||||
}
|
||||
|
||||
HFONT GetDefaultFont() {
|
||||
static HFONT font = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
|
||||
return font;
|
||||
}
|
||||
|
||||
std::string GetWindowText(HWND wnd) {
|
||||
char text[MAX_PATH] = {0};
|
||||
::GetWindowTextA(wnd, &text[0], ARRAYSIZE(text));
|
||||
return text;
|
||||
}
|
||||
|
||||
void AddListBoxItem(HWND listbox, const std::string& str, LPARAM item_data) {
|
||||
LRESULT index = ::SendMessageA(listbox, LB_ADDSTRING, 0,
|
||||
reinterpret_cast<LPARAM>(str.c_str()));
|
||||
::SendMessageA(listbox, LB_SETITEMDATA, index, item_data);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MainWnd::MainWnd()
|
||||
: ui_(CONNECT_TO_SERVER), wnd_(NULL), edit1_(NULL), edit2_(NULL),
|
||||
label1_(NULL), label2_(NULL), button_(NULL), listbox_(NULL),
|
||||
destroyed_(false), callback_(NULL), nested_msg_(NULL) {
|
||||
}
|
||||
|
||||
MainWnd::~MainWnd() {
|
||||
ASSERT(!IsWindow());
|
||||
}
|
||||
|
||||
bool MainWnd::Create() {
|
||||
ASSERT(wnd_ == NULL);
|
||||
if (!RegisterWindowClass())
|
||||
return false;
|
||||
|
||||
wnd_ = ::CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, kClassName, L"WebRTC",
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
NULL, NULL, GetModuleHandle(NULL), this);
|
||||
|
||||
::SendMessage(wnd_, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
|
||||
TRUE);
|
||||
|
||||
CreateChildWindows();
|
||||
SwitchToConnectUI();
|
||||
|
||||
return wnd_ != NULL;
|
||||
}
|
||||
|
||||
bool MainWnd::Destroy() {
|
||||
BOOL ret = FALSE;
|
||||
if (IsWindow()) {
|
||||
ret = ::DestroyWindow(wnd_);
|
||||
}
|
||||
|
||||
return ret != FALSE;
|
||||
}
|
||||
|
||||
void MainWnd::RegisterObserver(MainWndCallback* callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
bool MainWnd::IsWindow() const {
|
||||
return wnd_ && ::IsWindow(wnd_) != FALSE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MainWnd::SwitchToConnectUI() {
|
||||
ASSERT(IsWindow());
|
||||
LayoutPeerListUI(false);
|
||||
ui_ = CONNECT_TO_SERVER;
|
||||
LayoutConnectUI(true);
|
||||
::SetFocus(edit1_);
|
||||
}
|
||||
|
||||
void MainWnd::SwitchToPeerList(const Peers& peers) {
|
||||
LayoutConnectUI(false);
|
||||
|
||||
::SendMessage(listbox_, LB_RESETCONTENT, 0, 0);
|
||||
|
||||
AddListBoxItem(listbox_, "List of currently connected peers:", -1);
|
||||
Peers::const_iterator i = peers.begin();
|
||||
for (; i != peers.end(); ++i)
|
||||
AddListBoxItem(listbox_, i->second.c_str(), i->first);
|
||||
|
||||
ui_ = LIST_PEERS;
|
||||
LayoutPeerListUI(true);
|
||||
}
|
||||
|
||||
void MainWnd::SwitchToStreamingUI() {
|
||||
LayoutConnectUI(false);
|
||||
LayoutPeerListUI(false);
|
||||
ui_ = STREAMING;
|
||||
}
|
||||
|
||||
void MainWnd::OnPaint() {
|
||||
PAINTSTRUCT ps;
|
||||
::BeginPaint(handle(), &ps);
|
||||
|
||||
RECT rc;
|
||||
::GetClientRect(handle(), &rc);
|
||||
HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
|
||||
::FillRect(ps.hdc, &rc, brush);
|
||||
::DeleteObject(brush);
|
||||
|
||||
::EndPaint(handle(), &ps);
|
||||
}
|
||||
|
||||
void MainWnd::OnDestroyed() {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
|
||||
void MainWnd::OnDefaultAction() {
|
||||
if (!callback_)
|
||||
return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
MainWnd* me = reinterpret_cast<MainWnd*>(
|
||||
::GetWindowLongPtr(hwnd, GWL_USERDATA));
|
||||
if (!me && WM_CREATE == msg) {
|
||||
CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lp);
|
||||
me = reinterpret_cast<MainWnd*>(cs->lpCreateParams);
|
||||
me->wnd_ = hwnd;
|
||||
::SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(me));
|
||||
}
|
||||
|
||||
LRESULT result = 0;
|
||||
if (me) {
|
||||
void* prev_nested_msg = me->nested_msg_;
|
||||
me->nested_msg_ = &msg;
|
||||
|
||||
bool handled = me->OnMessage(msg, wp, lp, &result);
|
||||
if (WM_NCDESTROY == msg) {
|
||||
me->destroyed_ = true;
|
||||
} else if (!handled) {
|
||||
result = ::DefWindowProc(hwnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
if (me->destroyed_ && prev_nested_msg == NULL) {
|
||||
me->OnDestroyed();
|
||||
me->wnd_ = NULL;
|
||||
me->destroyed_ = false;
|
||||
}
|
||||
|
||||
me->nested_msg_ = prev_nested_msg;
|
||||
} else {
|
||||
result = ::DefWindowProc(hwnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
bool MainWnd::RegisterWindowClass() {
|
||||
if (wnd_class_)
|
||||
return true;
|
||||
|
||||
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
|
||||
wcex.style = CS_DBLCLKS;
|
||||
wcex.hInstance = GetModuleHandle(NULL);
|
||||
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
|
||||
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
|
||||
wcex.lpfnWndProc = &WndProc;
|
||||
wcex.lpszClassName = kClassName;
|
||||
wnd_class_ = ::RegisterClassEx(&wcex);
|
||||
ASSERT(wnd_class_);
|
||||
return wnd_class_ != 0;
|
||||
}
|
||||
|
||||
void MainWnd::CreateChildWindow(HWND* wnd, MainWnd::ChildWindowID id,
|
||||
const wchar_t* class_name, DWORD control_style,
|
||||
DWORD ex_style) {
|
||||
if (::IsWindow(*wnd))
|
||||
return;
|
||||
|
||||
// Child windows are invisible at first, and shown after being resized.
|
||||
DWORD style = WS_CHILD | control_style;
|
||||
*wnd = ::CreateWindowEx(ex_style, class_name, L"", style,
|
||||
100, 100, 100, 100, wnd_,
|
||||
reinterpret_cast<HMENU>(id),
|
||||
GetModuleHandle(NULL), NULL);
|
||||
ASSERT(::IsWindow(*wnd));
|
||||
::SendMessage(*wnd, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
|
||||
TRUE);
|
||||
}
|
||||
|
||||
void MainWnd::CreateChildWindows() {
|
||||
// Create the child windows in tab order.
|
||||
CreateChildWindow(&label1_, LABEL1_ID, L"Static", ES_CENTER | ES_READONLY, 0);
|
||||
CreateChildWindow(&edit1_, EDIT_ID, L"Edit",
|
||||
ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
|
||||
CreateChildWindow(&label2_, LABEL2_ID, L"Static", ES_CENTER | ES_READONLY, 0);
|
||||
CreateChildWindow(&edit2_, EDIT_ID, L"Edit",
|
||||
ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
|
||||
CreateChildWindow(&button_, BUTTON_ID, L"Button", BS_CENTER | WS_TABSTOP, 0);
|
||||
|
||||
CreateChildWindow(&listbox_, LISTBOX_ID, L"ListBox",
|
||||
LBS_HASSTRINGS | LBS_NOTIFY, WS_EX_CLIENTEDGE);
|
||||
|
||||
::SetWindowTextA(edit1_, GetDefaultServerName().c_str());
|
||||
::SetWindowTextA(edit2_, "8888");
|
||||
}
|
||||
|
||||
void MainWnd::LayoutConnectUI(bool show) {
|
||||
struct Windows {
|
||||
HWND wnd;
|
||||
const wchar_t* text;
|
||||
size_t width;
|
||||
size_t height;
|
||||
} windows[] = {
|
||||
{ label1_, L"Server" },
|
||||
{ edit1_, L"XXXyyyYYYgggXXXyyyYYYggg" },
|
||||
{ label2_, L":" },
|
||||
{ edit2_, L"XyXyX" },
|
||||
{ button_, L"Connect" },
|
||||
};
|
||||
|
||||
if (show) {
|
||||
const size_t kSeparator = 5;
|
||||
size_t total_width = (ARRAYSIZE(windows) - 1) * kSeparator;
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
|
||||
CalculateWindowSizeForText(windows[i].wnd, windows[i].text,
|
||||
&windows[i].width, &windows[i].height);
|
||||
total_width += windows[i].width;
|
||||
}
|
||||
|
||||
RECT rc;
|
||||
::GetClientRect(wnd_, &rc);
|
||||
size_t x = (rc.right / 2) - (total_width / 2);
|
||||
size_t y = rc.bottom / 2;
|
||||
for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
|
||||
size_t top = y - (windows[i].height / 2);
|
||||
::MoveWindow(windows[i].wnd, x, top, windows[i].width, windows[i].height,
|
||||
TRUE);
|
||||
x += kSeparator + windows[i].width;
|
||||
if (windows[i].text[0] != 'X')
|
||||
::SetWindowText(windows[i].wnd, windows[i].text);
|
||||
::ShowWindow(windows[i].wnd, SW_SHOWNA);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
|
||||
::ShowWindow(windows[i].wnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWnd::LayoutPeerListUI(bool show) {
|
||||
if (show) {
|
||||
RECT rc;
|
||||
::GetClientRect(wnd_, &rc);
|
||||
::MoveWindow(listbox_, 0, 0, rc.right, rc.bottom, TRUE);
|
||||
::ShowWindow(listbox_, SW_SHOWNA);
|
||||
} else {
|
||||
::ShowWindow(listbox_, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWnd::HandleTabbing() {
|
||||
bool shift = ((::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
|
||||
UINT next_cmd = shift ? GW_HWNDPREV : GW_HWNDNEXT;
|
||||
UINT loop_around_cmd = shift ? GW_HWNDLAST : GW_HWNDFIRST;
|
||||
HWND focus = GetFocus(), next;
|
||||
do {
|
||||
next = ::GetWindow(focus, next_cmd);
|
||||
if (IsWindowVisible(next) &&
|
||||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!next) {
|
||||
next = ::GetWindow(focus, loop_around_cmd);
|
||||
if (IsWindowVisible(next) &&
|
||||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
focus = next;
|
||||
} while (true);
|
||||
::SetFocus(next);
|
||||
}
|
102
peerconnection/samples/client/main_wnd.h
Normal file
102
peerconnection/samples/client/main_wnd.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef PEERCONNECTION_SAMPLES_CLIENT_MAIN_WND_H_
|
||||
#define PEERCONNECTION_SAMPLES_CLIENT_MAIN_WND_H_
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "peerconnection/samples/client/peer_connection_client.h"
|
||||
#include "talk/base/win32.h"
|
||||
|
||||
class MainWndCallback {
|
||||
public:
|
||||
virtual void StartLogin(const std::string& server, int port) = 0;
|
||||
virtual void DisconnectFromServer() = 0;
|
||||
virtual void ConnectToPeer(int peer_id) = 0;
|
||||
virtual void DisconnectFromCurrentPeer() = 0;
|
||||
protected:
|
||||
virtual ~MainWndCallback() {}
|
||||
};
|
||||
|
||||
class MainWnd {
|
||||
public:
|
||||
static const wchar_t kClassName[];
|
||||
|
||||
enum UI {
|
||||
CONNECT_TO_SERVER,
|
||||
LIST_PEERS,
|
||||
STREAMING,
|
||||
};
|
||||
|
||||
MainWnd();
|
||||
~MainWnd();
|
||||
|
||||
bool Create();
|
||||
bool Destroy();
|
||||
bool IsWindow() const;
|
||||
|
||||
void RegisterObserver(MainWndCallback* callback);
|
||||
|
||||
bool PreTranslateMessage(MSG* msg);
|
||||
|
||||
void SwitchToConnectUI();
|
||||
void SwitchToPeerList(const Peers& peers);
|
||||
void SwitchToStreamingUI();
|
||||
|
||||
HWND handle() const { return wnd_; }
|
||||
UI current_ui() const { return ui_; }
|
||||
|
||||
protected:
|
||||
enum ChildWindowID {
|
||||
EDIT_ID = 1,
|
||||
BUTTON_ID,
|
||||
LABEL1_ID,
|
||||
LABEL2_ID,
|
||||
LISTBOX_ID,
|
||||
};
|
||||
|
||||
void OnPaint();
|
||||
void OnDestroyed();
|
||||
|
||||
void OnDefaultAction();
|
||||
|
||||
bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result);
|
||||
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||
static bool RegisterWindowClass();
|
||||
|
||||
void CreateChildWindow(HWND* wnd, ChildWindowID id, const wchar_t* class_name,
|
||||
DWORD control_style, DWORD ex_style);
|
||||
void CreateChildWindows();
|
||||
|
||||
void LayoutConnectUI(bool show);
|
||||
void LayoutPeerListUI(bool show);
|
||||
|
||||
void HandleTabbing();
|
||||
|
||||
private:
|
||||
UI ui_;
|
||||
HWND wnd_;
|
||||
HWND edit1_;
|
||||
HWND edit2_;
|
||||
HWND label1_;
|
||||
HWND label2_;
|
||||
HWND button_;
|
||||
HWND listbox_;
|
||||
bool destroyed_;
|
||||
void* nested_msg_;
|
||||
MainWndCallback* callback_;
|
||||
static ATOM wnd_class_;
|
||||
};
|
||||
|
||||
#endif // PEERCONNECTION_SAMPLES_CLIENT_MAIN_WND_H_
|
406
peerconnection/samples/client/peer_connection_client.cc
Normal file
406
peerconnection/samples/client/peer_connection_client.cc
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "peerconnection/samples/client/peer_connection_client.h"
|
||||
|
||||
#include "peerconnection/samples/client/defaults.h"
|
||||
#include "talk/base/logging.h"
|
||||
|
||||
PeerConnectionClient::PeerConnectionClient()
|
||||
: callback_(NULL), my_id_(-1), state_(NOT_CONNECTED) {
|
||||
control_socket_.SignalCloseEvent.connect(this,
|
||||
&PeerConnectionClient::OnClose);
|
||||
hanging_get_.SignalCloseEvent.connect(this,
|
||||
&PeerConnectionClient::OnClose);
|
||||
control_socket_.SignalConnectEvent.connect(this,
|
||||
&PeerConnectionClient::OnConnect);
|
||||
hanging_get_.SignalConnectEvent.connect(this,
|
||||
&PeerConnectionClient::OnHangingGetConnect);
|
||||
control_socket_.SignalReadEvent.connect(this,
|
||||
&PeerConnectionClient::OnRead);
|
||||
hanging_get_.SignalReadEvent.connect(this,
|
||||
&PeerConnectionClient::OnHangingGetRead);
|
||||
}
|
||||
|
||||
PeerConnectionClient::~PeerConnectionClient() {
|
||||
}
|
||||
|
||||
int PeerConnectionClient::id() const {
|
||||
return my_id_;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::is_connected() const {
|
||||
return my_id_ != -1;
|
||||
}
|
||||
|
||||
const Peers& PeerConnectionClient::peers() const {
|
||||
return peers_;
|
||||
}
|
||||
|
||||
void PeerConnectionClient::RegisterObserver(
|
||||
PeerConnectionClientObserver* callback) {
|
||||
ASSERT(!callback_);
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::Connect(const std::string& server, int port,
|
||||
const std::string& client_name) {
|
||||
ASSERT(!server.empty());
|
||||
ASSERT(!client_name.empty());
|
||||
ASSERT(state_ == NOT_CONNECTED);
|
||||
|
||||
if (server.empty() || client_name.empty())
|
||||
return false;
|
||||
|
||||
if (port <= 0)
|
||||
port = kDefaultServerPort;
|
||||
|
||||
server_address_.SetIP(server);
|
||||
server_address_.SetPort(port);
|
||||
|
||||
if (server_address_.IsUnresolved()) {
|
||||
hostent* h = gethostbyname(server_address_.IPAsString().c_str());
|
||||
if (!h) {
|
||||
LOG(LS_ERROR) << "Failed to resolve host name: "
|
||||
<< server_address_.IPAsString();
|
||||
return false;
|
||||
} else {
|
||||
server_address_.SetResolvedIP(
|
||||
ntohl(*reinterpret_cast<uint32*>(h->h_addr_list[0])));
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
wsprintfA(buffer, "GET /sign_in?%s HTTP/1.0\r\n\r\n", client_name.c_str());
|
||||
onconnect_data_ = buffer;
|
||||
|
||||
bool ret = ConnectControlSocket();
|
||||
if (ret)
|
||||
state_ = SIGNING_IN;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::SendToPeer(int peer_id, const std::string& message) {
|
||||
if (state_ != CONNECTED)
|
||||
return false;
|
||||
|
||||
ASSERT(is_connected());
|
||||
ASSERT(control_socket_.GetState() == talk_base::Socket::CS_CLOSED);
|
||||
if (!is_connected() || peer_id == -1)
|
||||
return false;
|
||||
|
||||
char headers[1024];
|
||||
wsprintfA(headers, "POST /message?peer_id=%i&to=%i HTTP/1.0\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n",
|
||||
my_id_, peer_id, message.length());
|
||||
onconnect_data_ = headers;
|
||||
onconnect_data_ += message;
|
||||
return ConnectControlSocket();
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::SignOut() {
|
||||
if (state_ == NOT_CONNECTED || state_ == SIGNING_OUT)
|
||||
return true;
|
||||
|
||||
if (hanging_get_.GetState() != talk_base::Socket::CS_CLOSED)
|
||||
hanging_get_.Close();
|
||||
|
||||
if (control_socket_.GetState() == talk_base::Socket::CS_CLOSED) {
|
||||
ASSERT(my_id_ != -1);
|
||||
state_ = SIGNING_OUT;
|
||||
|
||||
char buffer[1024];
|
||||
wsprintfA(buffer, "GET /sign_out?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
|
||||
onconnect_data_ = buffer;
|
||||
return ConnectControlSocket();
|
||||
} else {
|
||||
state_ = SIGNING_OUT_WAITING;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnectionClient::Close() {
|
||||
control_socket_.Close();
|
||||
hanging_get_.Close();
|
||||
onconnect_data_.clear();
|
||||
peers_.clear();
|
||||
my_id_ = -1;
|
||||
state_ = NOT_CONNECTED;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::ConnectControlSocket() {
|
||||
ASSERT(control_socket_.GetState() == talk_base::Socket::CS_CLOSED);
|
||||
int err = control_socket_.Connect(server_address_);
|
||||
if (err == SOCKET_ERROR) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnectionClient::OnConnect(talk_base::AsyncSocket* socket) {
|
||||
ASSERT(!onconnect_data_.empty());
|
||||
int sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());
|
||||
ASSERT(sent == onconnect_data_.length());
|
||||
onconnect_data_.clear();
|
||||
}
|
||||
|
||||
void PeerConnectionClient::OnHangingGetConnect(talk_base::AsyncSocket* socket) {
|
||||
char buffer[1024];
|
||||
wsprintfA(buffer, "GET /wait?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
|
||||
int len = lstrlenA(buffer);
|
||||
int sent = socket->Send(buffer, len);
|
||||
ASSERT(sent == len);
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::GetHeaderValue(const std::string& data,
|
||||
size_t eoh,
|
||||
const char* header_pattern,
|
||||
size_t* value) {
|
||||
ASSERT(value);
|
||||
size_t found = data.find(header_pattern);
|
||||
if (found != std::string::npos && found < eoh) {
|
||||
*value = atoi(&data[found + lstrlenA(header_pattern)]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::GetHeaderValue(const std::string& data, size_t eoh,
|
||||
const char* header_pattern,
|
||||
std::string* value) {
|
||||
ASSERT(value);
|
||||
size_t found = data.find(header_pattern);
|
||||
if (found != std::string::npos && found < eoh) {
|
||||
size_t begin = found + lstrlenA(header_pattern);
|
||||
size_t end = data.find("\r\n", begin);
|
||||
if (end == std::string::npos)
|
||||
end = eoh;
|
||||
value->assign(data.substr(begin, end - begin));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::ReadIntoBuffer(talk_base::AsyncSocket* socket,
|
||||
std::string* data,
|
||||
size_t* content_length) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
|
||||
char buffer[0xffff];
|
||||
do {
|
||||
int bytes = socket->Recv(buffer, sizeof(buffer));
|
||||
if (bytes <= 0)
|
||||
break;
|
||||
data->append(buffer, bytes);
|
||||
} while (true);
|
||||
|
||||
bool ret = false;
|
||||
size_t i = data->find("\r\n\r\n");
|
||||
if (i != std::string::npos) {
|
||||
LOG(INFO) << "Headers received";
|
||||
const char kContentLengthHeader[] = "\r\nContent-Length: ";
|
||||
if (GetHeaderValue(*data, i, "\r\nContent-Length: ", content_length)) {
|
||||
LOG(INFO) << "Expecting " << *content_length << " bytes.";
|
||||
size_t total_response_size = (i + 4) + *content_length;
|
||||
if (data->length() >= total_response_size) {
|
||||
ret = true;
|
||||
std::string should_close;
|
||||
const char kConnection[] = "\r\nConnection: ";
|
||||
if (GetHeaderValue(*data, i, kConnection, &should_close) &&
|
||||
should_close.compare("close") == 0) {
|
||||
socket->Close();
|
||||
}
|
||||
} else {
|
||||
// We haven't received everything. Just continue to accept data.
|
||||
}
|
||||
} else {
|
||||
LOG(LS_ERROR) << "No content length field specified by the server.";
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PeerConnectionClient::OnRead(talk_base::AsyncSocket* socket) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
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.
|
||||
ASSERT(state_ == SIGNING_IN);
|
||||
my_id_ = peer_id;
|
||||
ASSERT(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;
|
||||
}
|
||||
}
|
||||
ASSERT(is_connected());
|
||||
callback_->OnSignedIn();
|
||||
} else if (state_ == SIGNING_OUT) {
|
||||
Close();
|
||||
callback_->OnDisconnected();
|
||||
} else if (state_ == SIGNING_OUT_WAITING) {
|
||||
SignOut();
|
||||
}
|
||||
}
|
||||
|
||||
control_data_.clear();
|
||||
|
||||
if (state_ == SIGNING_IN) {
|
||||
ASSERT(hanging_get_.GetState() == talk_base::Socket::CS_CLOSED);
|
||||
state_ = CONNECTED;
|
||||
hanging_get_.Connect(server_address_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnectionClient::OnHangingGetRead(talk_base::AsyncSocket* socket) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
size_t content_length = 0;
|
||||
if (ReadIntoBuffer(socket, ¬ification_data_, &content_length)) {
|
||||
size_t peer_id = 0, eoh = 0;
|
||||
bool ok = ParseServerResponse(notification_data_, content_length,
|
||||
&peer_id, &eoh);
|
||||
|
||||
if (ok) {
|
||||
// Store the position where the body begins.
|
||||
size_t pos = eoh + 4;
|
||||
|
||||
if (my_id_ == peer_id) {
|
||||
// A notification about a new member or a member that just
|
||||
// disconnected.
|
||||
int id = 0;
|
||||
std::string name;
|
||||
bool connected = false;
|
||||
if (ParseEntry(notification_data_.substr(pos), &name, &id,
|
||||
&connected)) {
|
||||
if (connected) {
|
||||
peers_[id] = name;
|
||||
callback_->OnPeerConnected(id, name);
|
||||
} else {
|
||||
peers_.erase(id);
|
||||
callback_->OnPeerDisconnected(id, name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback_->OnMessageFromPeer(peer_id,
|
||||
notification_data_.substr(pos));
|
||||
}
|
||||
}
|
||||
|
||||
notification_data_.clear();
|
||||
}
|
||||
|
||||
if (hanging_get_.GetState() == talk_base::Socket::CS_CLOSED &&
|
||||
state_ == CONNECTED) {
|
||||
hanging_get_.Connect(server_address_);
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::ParseEntry(const std::string& entry,
|
||||
std::string* name,
|
||||
int* id,
|
||||
bool* connected) {
|
||||
ASSERT(name);
|
||||
ASSERT(id);
|
||||
ASSERT(connected);
|
||||
ASSERT(entry.length());
|
||||
|
||||
*connected = false;
|
||||
size_t separator = entry.find(',');
|
||||
if (separator != std::string::npos) {
|
||||
*id = atoi(&entry[separator + 1]);
|
||||
name->assign(entry.substr(0, separator));
|
||||
separator = entry.find(',', separator + 1);
|
||||
if (separator != std::string::npos) {
|
||||
*connected = atoi(&entry[separator + 1]) ? true : false;
|
||||
}
|
||||
}
|
||||
return !name->empty();
|
||||
}
|
||||
|
||||
int PeerConnectionClient::GetResponseStatus(const std::string& response) {
|
||||
int status = -1;
|
||||
size_t pos = response.find(' ');
|
||||
if (pos != std::string::npos)
|
||||
status = atoi(&response[pos + 1]);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool PeerConnectionClient::ParseServerResponse(const std::string& response,
|
||||
size_t content_length,
|
||||
size_t* peer_id,
|
||||
size_t* eoh) {
|
||||
LOG(INFO) << response;
|
||||
|
||||
int status = GetResponseStatus(response.c_str());
|
||||
if (status != 200) {
|
||||
LOG(LS_ERROR) << "Received error from server";
|
||||
Close();
|
||||
callback_->OnDisconnected();
|
||||
return false;
|
||||
}
|
||||
|
||||
*eoh = response.find("\r\n\r\n");
|
||||
ASSERT(*eoh != std::string::npos);
|
||||
if (*eoh == std::string::npos)
|
||||
return false;
|
||||
|
||||
*peer_id = -1;
|
||||
|
||||
// See comment in peer_channel.cc for why we use the Pragma header and
|
||||
// not e.g. "X-Peer-Id".
|
||||
GetHeaderValue(response, *eoh, "\r\nPragma: ", peer_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnectionClient::OnClose(talk_base::AsyncSocket* socket, int err) {
|
||||
LOG(INFO) << __FUNCTION__;
|
||||
|
||||
socket->Close();
|
||||
|
||||
if (err != WSAECONNREFUSED) {
|
||||
if (socket == &hanging_get_) {
|
||||
if (state_ == CONNECTED) {
|
||||
LOG(INFO) << "Issuing a new hanging get";
|
||||
hanging_get_.Close();
|
||||
hanging_get_.Connect(server_address_);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Failed to connect to the server.
|
||||
Close();
|
||||
callback_->OnDisconnected();
|
||||
}
|
||||
}
|
103
peerconnection/samples/client/peer_connection_client.h
Normal file
103
peerconnection/samples/client/peer_connection_client.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef PEERCONNECTION_SAMPLES_CLIENT_PEER_CONNECTION_CLIENT_H_
|
||||
#define PEERCONNECTION_SAMPLES_CLIENT_PEER_CONNECTION_CLIENT_H_
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "talk/base/sigslot.h"
|
||||
#include "talk/base/win32socketserver.h"
|
||||
|
||||
typedef std::map<int, std::string> Peers;
|
||||
|
||||
struct PeerConnectionClientObserver {
|
||||
virtual void OnSignedIn() = 0; // Called when we're logged on.
|
||||
virtual void OnDisconnected() = 0;
|
||||
virtual void OnPeerConnected(int id, const std::string& name) = 0;
|
||||
virtual void OnPeerDisconnected(int id, const std::string& name) = 0;
|
||||
virtual void OnMessageFromPeer(int peer_id, const std::string& message) = 0;
|
||||
protected:
|
||||
virtual ~PeerConnectionClientObserver() {}
|
||||
};
|
||||
|
||||
class PeerConnectionClient : public sigslot::has_slots<> {
|
||||
public:
|
||||
enum State {
|
||||
NOT_CONNECTED,
|
||||
SIGNING_IN,
|
||||
CONNECTED,
|
||||
SIGNING_OUT_WAITING,
|
||||
SIGNING_OUT,
|
||||
};
|
||||
|
||||
PeerConnectionClient();
|
||||
~PeerConnectionClient();
|
||||
|
||||
int id() const;
|
||||
bool is_connected() const;
|
||||
const Peers& peers() const;
|
||||
|
||||
void RegisterObserver(PeerConnectionClientObserver* callback);
|
||||
|
||||
bool Connect(const std::string& server, int port,
|
||||
const std::string& client_name);
|
||||
|
||||
bool SendToPeer(int peer_id, const std::string& message);
|
||||
|
||||
bool SignOut();
|
||||
|
||||
protected:
|
||||
void Close();
|
||||
bool ConnectControlSocket();
|
||||
void OnConnect(talk_base::AsyncSocket* socket);
|
||||
void OnHangingGetConnect(talk_base::AsyncSocket* socket);
|
||||
|
||||
// Quick and dirty support for parsing HTTP header values.
|
||||
bool GetHeaderValue(const std::string& data, size_t eoh,
|
||||
const char* header_pattern, size_t* value);
|
||||
|
||||
bool GetHeaderValue(const std::string& data, size_t eoh,
|
||||
const char* header_pattern, std::string* value);
|
||||
|
||||
// Returns true if the whole response has been read.
|
||||
bool ReadIntoBuffer(talk_base::AsyncSocket* socket, std::string* data,
|
||||
size_t* content_length);
|
||||
|
||||
void OnRead(talk_base::AsyncSocket* socket);
|
||||
|
||||
void OnHangingGetRead(talk_base::AsyncSocket* socket);
|
||||
|
||||
// Parses a single line entry in the form "<name>,<id>,<connected>"
|
||||
bool ParseEntry(const std::string& entry, std::string* name, int* id,
|
||||
bool* connected);
|
||||
|
||||
int GetResponseStatus(const std::string& response);
|
||||
|
||||
bool ParseServerResponse(const std::string& response, size_t content_length,
|
||||
size_t* peer_id, size_t* eoh);
|
||||
|
||||
void OnClose(talk_base::AsyncSocket* socket, int err);
|
||||
|
||||
PeerConnectionClientObserver* callback_;
|
||||
talk_base::SocketAddress server_address_;
|
||||
talk_base::Win32Socket control_socket_;
|
||||
talk_base::Win32Socket hanging_get_;
|
||||
std::string onconnect_data_;
|
||||
std::string control_data_;
|
||||
std::string notification_data_;
|
||||
Peers peers_;
|
||||
State state_;
|
||||
int my_id_;
|
||||
};
|
||||
|
||||
#endif // PEERCONNECTION_SAMPLES_CLIENT_PEER_CONNECTION_CLIENT_H_
|
Loading…
x
Reference in New Issue
Block a user