aboutsummaryrefslogtreecommitdiff
path: root/third_party_mods
diff options
context:
space:
mode:
authorperkj@webrtc.org <perkj@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2011-10-03 15:59:40 +0000
committerperkj@webrtc.org <perkj@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2011-10-03 15:59:40 +0000
commit99239d5a41fa81bd5f8d5aa06ffe6c27da2d0243 (patch)
tree398845afc9613f43c152430b8b322ba522ec6939 /third_party_mods
parentf458916145e5c85f7321a82d60fed07da6f7af0a (diff)
downloadwebrtc-99239d5a41fa81bd5f8d5aa06ffe6c27da2d0243.tar.gz
First compiling version of peerconnection_client_dev using the new Peerconnection API.
Links but does not work since the new peerconnection is under development. I would like to commit a version with as few changes as possible to the old peerconnection_client but using the new PeerConnection API. BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/183003 git-svn-id: http://webrtc.googlecode.com/svn/trunk@677 4adac7df-926f-26a2-2b94-8c16560cd09d
Diffstat (limited to 'third_party_mods')
-rw-r--r--third_party_mods/libjingle/libjingle.gyp55
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.cc353
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.h109
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.cc58
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.h30
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main.cc85
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.cc458
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.h114
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/main.cc58
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.cc582
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.h190
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.cc479
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.h109
-rw-r--r--third_party_mods/libjingle/source/talk/examples/peerconnection_client/peerconnection_client.gyp55
14 files changed, 2697 insertions, 38 deletions
diff --git a/third_party_mods/libjingle/libjingle.gyp b/third_party_mods/libjingle/libjingle.gyp
index 84750e2199..f59b5a0605 100644
--- a/third_party_mods/libjingle/libjingle.gyp
+++ b/third_party_mods/libjingle/libjingle.gyp
@@ -713,40 +713,18 @@
], # conditions
},
{
- 'target_name': 'peerconnection_client_dev',
+ 'target_name': 'peerconnection_unittests',
'conditions': [
- ['peer_connection_dev==1 and OS=="linux"', {
- 'type': 'executable',
- 'sources': [
- '<(libjingle_mods)/source/talk/app/peer_connection_dev/peerconnection_client_dev.cc',
- ],
- 'libraries': [
- '-lXext',
- '-lX11',
- ],
- }, {
- 'type': 'none',
- } ], # peer_connection_dev
- ['inside_chromium_build==1', {
- 'dependencies': [
- 'libjingle_app',
- ],
- }, {
+ ['peer_connection_dev==1', {
'dependencies': [
'libjingle_app',
+ '../../testing/gtest.gyp:gtest',
+ '../../testing/gtest.gyp:gtest_main',
+ # TODO(perkj): Temporary build the client app here to make sure
+ # nothing is broken.
+ 'source/talk/examples/peerconnection_client/'
+ 'peerconnection_client.gyp:peerconnection_client_dev',
],
- } ], # inside_chromium_build
- ], # conditions
- },
- {
- 'target_name': 'peerconnection_unittests',
- 'dependencies': [
- 'libjingle_app',
- '../../testing/gtest.gyp:gtest',
- '../../testing/gtest.gyp:gtest_main',
- ],
- 'conditions': [
- ['peer_connection_dev==1', {
'type': 'executable',
'conditions': [
['inside_chromium_build==1', {
@@ -760,6 +738,12 @@
'../../src/system_wrappers/source/system_wrappers.gyp:system_wrappers',
],
}],
+ ['OS=="linux"', {
+ 'libraries': [
+ '-lXext',
+ '-lX11',
+ ],
+ }],
], #conditions
'sources': [
'<(libjingle_mods)/source/talk/app/webrtc_dev/mediastreamimpl_unittest.cc',
@@ -770,15 +754,10 @@
'<(libjingle_mods)/source/talk/app/webrtc_dev/peerconnectionsignaling_unittest.cc',
#'<(libjingle_mods)/source/talk/app/webrtc_dev/webrtcsession_unittest.cc',
],
- }, { # peer_connection_dev != 1
+ } , {
'type': 'none',
- } ], # peer_connection_dev
- ['peer_connection_dev==1 and OS=="linux"', {
- 'libraries': [
- '-lXext',
- '-lX11',
- ],
- } ],
+ }
+ ], # peer_connection_dev
], # conditions
},
],
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.cc
new file mode 100644
index 0000000000..34959c573b
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.cc
@@ -0,0 +1,353 @@
+/*
+ * 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 "talk/examples/peerconnection_client/conductor.h"
+
+#include <utility>
+
+#include "talk/examples/peerconnection_client/defaults.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/session/phone/videorendererfactory.h"
+
+Conductor::Conductor(PeerConnectionClient* client, MainWindow* main_wnd)
+ : peer_id_(-1),
+ client_(client),
+ main_wnd_(main_wnd) {
+ client_->RegisterObserver(this);
+ main_wnd->RegisterObserver(this);
+}
+
+Conductor::~Conductor() {
+ ASSERT(peer_connection_.get() == NULL);
+}
+
+bool Conductor::connection_active() const {
+ return peer_connection_.get() != NULL;
+}
+
+void Conductor::Close() {
+ client_->SignOut();
+ DeletePeerConnection();
+}
+
+bool Conductor::InitializePeerConnection() {
+ ASSERT(peer_connection_factory_.get() == NULL);
+ ASSERT(peer_connection_.get() == NULL);
+
+ peer_connection_factory_ = webrtc::PeerConnectionManager::Create();
+
+ if (!peer_connection_factory_.get()) {
+ main_wnd_->MessageBox("Error",
+ "Failed to initialize PeerConnectionFactory", true);
+ DeletePeerConnection();
+ return false;
+ }
+
+ peer_connection_ = peer_connection_factory_->CreatePeerConnection(
+ GetPeerConnectionString(), this);
+
+ if (!peer_connection_.get()) {
+ main_wnd_->MessageBox("Error",
+ "CreatePeerConnection failed", true);
+ DeletePeerConnection();
+ }
+ return peer_connection_.get() != NULL;
+}
+
+void Conductor::DeletePeerConnection() {
+ peer_connection_.release();
+ active_streams_.clear();
+ peer_connection_factory_.release();
+ peer_id_ = -1;
+}
+
+void Conductor::EnsureStreamingUI() {
+ ASSERT(peer_connection_.get() != NULL);
+ if (main_wnd_->IsWindow()) {
+ if (main_wnd_->current_ui() != MainWindow::STREAMING)
+ main_wnd_->SwitchToStreamingUI();
+ }
+}
+
+//
+// PeerConnectionObserver implementation.
+//
+
+void Conductor::OnError() {
+ LOG(LS_ERROR) << __FUNCTION__;
+ main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_ERROR, NULL);
+}
+
+void Conductor::OnSignalingMessage(const std::string& msg) {
+ LOG(INFO) << __FUNCTION__;
+
+ std::string* msg_copy = new std::string(msg);
+ main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg_copy);
+}
+
+// Called when a remote stream is added
+void Conductor::OnAddStream(webrtc::MediaStream* stream) {
+ LOG(INFO) << __FUNCTION__ << " " << stream->label();
+
+ stream->AddRef();
+ main_wnd_->QueueUIThreadCallback(NEW_STREAM_ADDED,
+ stream);
+}
+
+void Conductor::OnRemoveStream(webrtc::MediaStream* stream) {
+ LOG(INFO) << __FUNCTION__ << " " << stream->label();
+ stream->AddRef();
+ main_wnd_->QueueUIThreadCallback(STREAM_REMOVED,
+ stream);
+}
+
+//
+// PeerConnectionClientObserver implementation.
+//
+
+void Conductor::OnSignedIn() {
+ LOG(INFO) << __FUNCTION__;
+ main_wnd_->SwitchToPeerList(client_->peers());
+}
+
+void Conductor::OnDisconnected() {
+ LOG(INFO) << __FUNCTION__;
+
+ DeletePeerConnection();
+
+ 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() == MainWindow::LIST_PEERS)
+ main_wnd_->SwitchToPeerList(client_->peers());
+}
+
+void Conductor::OnPeerDisconnected(int id) {
+ LOG(INFO) << __FUNCTION__;
+ if (id == peer_id_) {
+ LOG(INFO) << "Our peer disconnected";
+ main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL);
+ } else {
+ // Refresh the list if we're showing it.
+ if (main_wnd_->current_ui() == MainWindow::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);
+ ASSERT(!message.empty());
+
+ if (!peer_connection_.get()) {
+ ASSERT(peer_id_ == -1);
+ peer_id_ = peer_id;
+
+ // 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 (peer_id != peer_id_) {
+ ASSERT(peer_id_ != -1);
+ LOG(WARNING) << "Received an offer from a peer while already in a "
+ "conversation with a different peer.";
+ return;
+ }
+
+ peer_connection_->ProcessSignalingMessage(message);
+}
+
+void Conductor::OnMessageSent(int err) {
+ // Process the next pending message if any.
+ main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, NULL);
+}
+
+//
+// MainWndCallback implementation.
+//
+
+bool Conductor::StartLogin(const std::string& server, int port) {
+ if (client_->is_connected())
+ return false;
+
+ if (!client_->Connect(server, port, GetPeerName())) {
+ main_wnd_->MessageBox("Error", ("Failed to connect to " + server).c_str(),
+ true);
+ return false;
+ }
+
+ return true;
+}
+
+void Conductor::DisconnectFromServer() {
+ if (client_->is_connected())
+ client_->SignOut();
+}
+
+void Conductor::ConnectToPeer(int peer_id) {
+ ASSERT(peer_id_ == -1);
+ ASSERT(peer_id != -1);
+
+ if (peer_connection_.get()) {
+ main_wnd_->MessageBox("Error",
+ "We only support connecting to one peer at a time", true);
+ return;
+ }
+
+ if (InitializePeerConnection()) {
+ peer_id_ = peer_id;
+ main_wnd_->SwitchToStreamingUI();
+ EnsureStreamingUI();
+ AddStreams();
+ } else {
+ main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
+ }
+}
+
+void Conductor::AddStreams() {
+ if (active_streams_.find(kStreamLabel) != active_streams_.end())
+ return; // Already added.
+
+ scoped_refptr<webrtc::LocalAudioTrack> audio_track(
+ webrtc::CreateLocalAudioTrack(kAudioLabel, NULL));
+
+ scoped_refptr<webrtc::LocalVideoTrack> video_track(
+ webrtc::CreateLocalVideoTrack(kVideoLabel, NULL));
+
+ scoped_refptr<webrtc::VideoRenderer> renderer(webrtc::CreateVideoRenderer(
+ main_wnd_->local_renderer()));
+ video_track->SetRenderer(renderer);
+
+ scoped_refptr<webrtc::LocalMediaStream> stream =
+ webrtc::CreateLocalMediaStream(kStreamLabel);
+
+ stream->AddTrack(audio_track);
+ stream->AddTrack(video_track);
+ peer_connection_->AddStream(stream);
+ peer_connection_->CommitStreamChanges();
+ typedef std::pair<std::string, scoped_refptr<webrtc::MediaStream> >
+ MediaStreamPair;
+ active_streams_.insert(MediaStreamPair(stream->label(), stream));
+}
+
+void Conductor::DisconnectFromCurrentPeer() {
+ LOG(INFO) << __FUNCTION__;
+ if (peer_connection_.get()) {
+ client_->SendHangUp(peer_id_);
+ DeletePeerConnection();
+ }
+
+ if (main_wnd_->IsWindow())
+ main_wnd_->SwitchToPeerList(client_->peers());
+}
+
+void Conductor::UIThreadCallback(int msg_id, void* data) {
+ switch (msg_id) {
+ case PEER_CONNECTION_CLOSED:
+ LOG(INFO) << "PEER_CONNECTION_CLOSED";
+ DeletePeerConnection();
+
+ ASSERT(active_streams_.empty());
+
+ if (main_wnd_->IsWindow()) {
+ if (client_->is_connected()) {
+ main_wnd_->SwitchToPeerList(client_->peers());
+ } else {
+ main_wnd_->SwitchToConnectUI();
+ }
+ } else {
+ DisconnectFromServer();
+ }
+ break;
+
+ case SEND_MESSAGE_TO_PEER: {
+ LOG(INFO) << "SEND_MESSAGE_TO_PEER";
+ std::string* msg = reinterpret_cast<std::string*>(data);
+ if (client_->IsSendingMessage()) {
+ ASSERT(msg != NULL);
+ pending_messages_.push_back(msg);
+ } else {
+ if (!msg && !pending_messages_.empty()) {
+ msg = pending_messages_.front();
+ pending_messages_.pop_front();
+ }
+ if (msg) {
+ bool ok = client_->SendToPeer(peer_id_, *msg);
+ if (!ok && peer_id_ != -1) {
+ LOG(LS_ERROR) << "SendToPeer failed";
+ DisconnectFromServer();
+ }
+ delete msg;
+ }
+
+ if (!peer_connection_.get())
+ peer_id_ = -1;
+ }
+ break;
+ }
+
+ case PEER_CONNECTION_ADDSTREAMS:
+ AddStreams();
+ break;
+
+ case PEER_CONNECTION_ERROR:
+ main_wnd_->MessageBox("Error", "an unknown error occurred", true);
+ break;
+
+ case NEW_STREAM_ADDED: {
+ webrtc::MediaStream* stream = reinterpret_cast<webrtc::MediaStream*>(
+ data);
+ scoped_refptr<webrtc::MediaStreamTrackList> tracks =
+ stream->tracks();
+ for (size_t i = 0; i < tracks->count(); ++i) {
+ if (tracks->at(i)->kind().compare(webrtc::kVideoTrackKind) == 0) {
+ webrtc::VideoTrack* track =
+ reinterpret_cast<webrtc::VideoTrack*>(tracks->at(i).get());
+ LOG(INFO) << "Setting video renderer for track: " << track->label();
+ scoped_refptr<webrtc::VideoRenderer> renderer(
+ webrtc::CreateVideoRenderer(main_wnd_->remote_renderer()));
+ track->SetRenderer(renderer);
+ }
+ }
+ // If we haven't shared any streams with this peer (we're the receiver)
+ // then do so now.
+ if (active_streams_.empty())
+ AddStreams();
+ stream->Release();
+ break;
+ }
+
+ case STREAM_REMOVED: {
+ webrtc::MediaStream* stream = reinterpret_cast<webrtc::MediaStream*>(
+ data);
+ active_streams_.erase(stream->label());
+ stream->Release();
+ if (active_streams_.empty()) {
+ LOG(INFO) << "All streams have been closed.";
+ main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL);
+ }
+ break;
+ }
+
+ default:
+ ASSERT(false);
+ break;
+ }
+}
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.h b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.h
new file mode 100644
index 0000000000..ed0fe2131d
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/conductor.h
@@ -0,0 +1,109 @@
+/*
+ * 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 <deque>
+#include <map>
+#include <set>
+#include <string>
+
+#include "talk/examples/peerconnection_client/main_wnd.h"
+#include "talk/examples/peerconnection_client/peer_connection_client.h"
+#include "talk/app/webrtc_dev/mediastream.h"
+#include "talk/app/webrtc_dev/peerconnection.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace cricket {
+class VideoRenderer;
+} // namespace cricket
+
+class Conductor
+ : public webrtc::PeerConnectionObserver,
+ public PeerConnectionClientObserver,
+ public MainWndCallback {
+ public:
+ enum CallbackID {
+ MEDIA_CHANNELS_INITIALIZED = 1,
+ PEER_CONNECTION_CLOSED,
+ SEND_MESSAGE_TO_PEER,
+ PEER_CONNECTION_ADDSTREAMS,
+ PEER_CONNECTION_ERROR,
+ NEW_STREAM_ADDED,
+ STREAM_REMOVED,
+ };
+
+ Conductor(PeerConnectionClient* client, MainWindow* main_wnd);
+ ~Conductor();
+
+ bool connection_active() const;
+
+ virtual void Close();
+
+ protected:
+ bool InitializePeerConnection();
+ void DeletePeerConnection();
+ void EnsureStreamingUI();
+ void AddStreams();
+
+ //
+ // PeerConnectionObserver implementation.
+ //
+ virtual void OnError();
+ virtual void OnMessage(const std::string& msg) {}
+ virtual void OnSignalingMessage(const std::string& msg);
+ virtual void OnStateChange(Readiness state) {}
+ virtual void OnAddStream(webrtc::MediaStream* stream);
+ virtual void OnRemoveStream(webrtc::MediaStream* stream);
+
+
+ //
+ // PeerConnectionClientObserver implementation.
+ //
+
+ virtual void OnSignedIn();
+
+ virtual void OnDisconnected();
+
+ virtual void OnPeerConnected(int id, const std::string& name);
+
+ virtual void OnPeerDisconnected(int id);
+
+ virtual void OnMessageFromPeer(int peer_id, const std::string& message);
+
+ virtual void OnMessageSent(int err);
+
+ //
+ // MainWndCallback implementation.
+ //
+
+ virtual bool StartLogin(const std::string& server, int port);
+
+ virtual void DisconnectFromServer();
+
+ virtual void ConnectToPeer(int peer_id);
+
+ virtual void DisconnectFromCurrentPeer();
+
+ virtual void UIThreadCallback(int msg_id, void* data);
+
+ protected:
+ int peer_id_;
+ scoped_refptr<webrtc::PeerConnection> peer_connection_;
+ scoped_refptr<webrtc::PeerConnectionManager> peer_connection_factory_;
+ PeerConnectionClient* client_;
+ MainWindow* main_wnd_;
+ std::deque<std::string*> pending_messages_;
+ std::map<std::string, scoped_refptr<webrtc::MediaStream> > active_streams_;
+};
+
+#endif // PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.cc
new file mode 100644
index 0000000000..38b6d9d4a1
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.cc
@@ -0,0 +1,58 @@
+/*
+ * 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 "talk/examples/peerconnection_client/defaults.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "talk/base/common.h"
+
+const char kAudioLabel[] = "audio_label";
+const char kVideoLabel[] = "video_label";
+const char kStreamLabel[] = "stream_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[256];
+ if (gethostname(computer_name, ARRAY_SIZE(computer_name)) != 0)
+ strcpy(computer_name, "host");
+ std::string ret(GetEnvVarOrDefault("USERNAME", "user"));
+ ret += '@';
+ ret += computer_name;
+ return ret;
+}
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.h b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.h
new file mode 100644
index 0000000000..bd05980c6b
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/defaults.h
@@ -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 <string>
+
+#include "talk/base/basictypes.h"
+
+extern const char kAudioLabel[];
+extern const char kVideoLabel[];
+extern const char kStreamLabel[];
+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_
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main.cc
new file mode 100644
index 0000000000..87590fbe5d
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main.cc
@@ -0,0 +1,85 @@
+/*
+ * 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 <gtk/gtk.h>
+
+#include "talk/examples/peerconnection_client/conductor.h"
+#include "talk/examples/peerconnection_client/linux/main_wnd.h"
+#include "talk/examples/peerconnection_client/peer_connection_client.h"
+
+#include "talk/base/thread.h"
+
+class CustomSocketServer : public talk_base::PhysicalSocketServer {
+ public:
+ CustomSocketServer(talk_base::Thread* thread, GtkMainWnd* wnd)
+ : thread_(thread), wnd_(wnd), conductor_(NULL), client_(NULL) {}
+ virtual ~CustomSocketServer() {}
+
+ void set_client(PeerConnectionClient* client) { client_ = client; }
+ void set_conductor(Conductor* conductor) { conductor_ = conductor; }
+
+ // Override so that we can also pump the GTK message loop.
+ virtual bool Wait(int cms, bool process_io) {
+ // Pump GTK events.
+ // TODO(tommi): We really should move either the socket server or UI to a
+ // different thread. Alternatively we could look at merging the two loops
+ // by implementing a dispatcher for the socket server and/or use
+ // g_main_context_set_poll_func.
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if (!wnd_->IsWindow() && !conductor_->connection_active() &&
+ client_ != NULL && !client_->is_connected()) {
+ thread_->Quit();
+ }
+ return talk_base::PhysicalSocketServer::Wait(0/*cms == -1 ? 1 : cms*/,
+ process_io);
+ }
+
+ protected:
+ talk_base::Thread* thread_;
+ GtkMainWnd* wnd_;
+ Conductor* conductor_;
+ PeerConnectionClient* client_;
+};
+
+int main(int argc, char* argv[]) {
+ gtk_init(&argc, &argv);
+ g_type_init();
+ g_thread_init(NULL);
+
+ GtkMainWnd wnd;
+ wnd.Create();
+
+ talk_base::AutoThread auto_thread;
+ talk_base::Thread* thread = talk_base::Thread::Current();
+ CustomSocketServer socket_server(thread, &wnd);
+ thread->set_socketserver(&socket_server);
+
+ // Must be constructed after we set the socketserver.
+ PeerConnectionClient client;
+ Conductor conductor(&client, &wnd);
+ socket_server.set_client(&client);
+ socket_server.set_conductor(&conductor);
+
+ thread->Run();
+
+ // gtk_main();
+ wnd.Destroy();
+
+ thread->set_socketserver(NULL);
+ // TODO(tommi): Run the Gtk main loop to tear down the connection.
+ //while (gtk_events_pending()) {
+ // gtk_main_iteration();
+ //}
+
+ return 0;
+}
+
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.cc
new file mode 100644
index 0000000000..9abc762be6
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.cc
@@ -0,0 +1,458 @@
+/*
+ * 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 "talk/examples/peerconnection_client/linux/main_wnd.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <stddef.h>
+
+#include "talk/examples/peerconnection_client/defaults.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+
+using talk_base::sprintfn;
+
+namespace {
+
+//
+// Simple static functions that simply forward the callback to the
+// GtkMainWnd instance.
+//
+
+gboolean OnDestroyedCallback(GtkWidget* widget, GdkEvent* event,
+ gpointer data) {
+ reinterpret_cast<GtkMainWnd*>(data)->OnDestroyed(widget, event);
+ return FALSE;
+}
+
+void OnClickedCallback(GtkWidget* widget, gpointer data) {
+ reinterpret_cast<GtkMainWnd*>(data)->OnClicked(widget);
+}
+
+gboolean OnKeyPressCallback(GtkWidget* widget, GdkEventKey* key,
+ gpointer data) {
+ reinterpret_cast<GtkMainWnd*>(data)->OnKeyPress(widget, key);
+ return false;
+}
+
+void OnRowActivatedCallback(GtkTreeView* tree_view, GtkTreePath* path,
+ GtkTreeViewColumn* column, gpointer data) {
+ reinterpret_cast<GtkMainWnd*>(data)->OnRowActivated(tree_view, path, column);
+}
+
+// Creates a tree view, that we use to display the list of peers.
+void InitializeList(GtkWidget* list) {
+ GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+ GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes(
+ "List Items", renderer, "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+ GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+}
+
+// Adds an entry to a tree view.
+void AddToList(GtkWidget* list, const gchar* str, int value) {
+ GtkListStore* store = GTK_LIST_STORE(
+ gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
+
+ GtkTreeIter iter;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, str, 1, value, -1);
+}
+
+struct UIThreadCallbackData {
+ explicit UIThreadCallbackData(MainWndCallback* cb, int id, void* d)
+ : callback(cb), msg_id(id), data(d) {}
+ MainWndCallback* callback;
+ int msg_id;
+ void* data;
+};
+
+gboolean HandleUIThreadCallback(gpointer data) {
+ UIThreadCallbackData* cb_data = reinterpret_cast<UIThreadCallbackData*>(data);
+ cb_data->callback->UIThreadCallback(cb_data->msg_id, cb_data->data);
+ delete cb_data;
+ return false;
+}
+
+gboolean Redraw(gpointer data) {
+ GtkMainWnd* wnd = reinterpret_cast<GtkMainWnd*>(data);
+ wnd->OnRedraw();
+ return false;
+}
+} // end anonymous
+
+//
+// GtkMainWnd implementation.
+//
+
+GtkMainWnd::GtkMainWnd()
+ : window_(NULL), draw_area_(NULL), vbox_(NULL), server_edit_(NULL),
+ port_edit_(NULL), peer_list_(NULL), callback_(NULL),
+ server_("localhost") {
+ char buffer[10];
+ sprintfn(buffer, sizeof(buffer), "%i", kDefaultServerPort);
+ port_ = buffer;
+}
+
+GtkMainWnd::~GtkMainWnd() {
+ ASSERT(!IsWindow());
+}
+
+void GtkMainWnd::RegisterObserver(MainWndCallback* callback) {
+ callback_ = callback;
+}
+
+bool GtkMainWnd::IsWindow() {
+ return window_ != NULL && GTK_IS_WINDOW(window_);
+}
+
+void GtkMainWnd::MessageBox(const char* caption, const char* text,
+ bool is_error) {
+ GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(window_),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ is_error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, "%s", text);
+ gtk_window_set_title(GTK_WINDOW(dialog), caption);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+MainWindow::UI GtkMainWnd::current_ui() {
+ if (vbox_)
+ return CONNECT_TO_SERVER;
+
+ if (peer_list_)
+ return LIST_PEERS;
+
+ return STREAMING;
+}
+
+cricket::VideoRenderer* GtkMainWnd::local_renderer() {
+ if (!local_renderer_.get())
+ local_renderer_.reset(new VideoRenderer(this));
+ return local_renderer_.get();
+}
+
+cricket::VideoRenderer* GtkMainWnd::remote_renderer() {
+ if (!remote_renderer_.get())
+ remote_renderer_.reset(new VideoRenderer(this));
+ return remote_renderer_.get();
+}
+
+void GtkMainWnd::QueueUIThreadCallback(int msg_id, void* data) {
+ g_idle_add(HandleUIThreadCallback,
+ new UIThreadCallbackData(callback_, msg_id, data));
+}
+
+bool GtkMainWnd::Create() {
+ ASSERT(window_ == NULL);
+
+ window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ if (window_) {
+ gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
+ gtk_window_set_default_size(GTK_WINDOW(window_), 640, 480);
+ gtk_window_set_title(GTK_WINDOW(window_), "PeerConnection client");
+ g_signal_connect(G_OBJECT(window_), "delete-event",
+ G_CALLBACK(&OnDestroyedCallback), this);
+ g_signal_connect(window_, "key-press-event", G_CALLBACK(OnKeyPressCallback),
+ this);
+
+ SwitchToConnectUI();
+ }
+
+ return window_ != NULL;
+}
+
+bool GtkMainWnd::Destroy() {
+ if (!IsWindow())
+ return false;
+
+ gtk_widget_destroy(window_);
+ window_ = NULL;
+
+ return true;
+}
+
+void GtkMainWnd::SwitchToConnectUI() {
+ LOG(INFO) << __FUNCTION__;
+
+ ASSERT(IsWindow());
+ ASSERT(vbox_ == NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window_), 10);
+
+ if (peer_list_) {
+ gtk_widget_destroy(peer_list_);
+ peer_list_ = NULL;
+ }
+
+ vbox_ = gtk_vbox_new(FALSE, 5);
+ GtkWidget* valign = gtk_alignment_new(0, 1, 0, 0);
+ gtk_container_add(GTK_CONTAINER(vbox_), valign);
+ gtk_container_add(GTK_CONTAINER(window_), vbox_);
+
+ GtkWidget* hbox = gtk_hbox_new(FALSE, 5);
+
+ GtkWidget* label = gtk_label_new("Server");
+ gtk_container_add(GTK_CONTAINER(hbox), label);
+
+ server_edit_ = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(server_edit_), server_.c_str());
+ gtk_widget_set_size_request(server_edit_, 400, 30);
+ gtk_container_add(GTK_CONTAINER(hbox), server_edit_);
+
+ port_edit_ = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(port_edit_), port_.c_str());
+ gtk_widget_set_size_request(port_edit_, 70, 30);
+ gtk_container_add(GTK_CONTAINER(hbox), port_edit_);
+
+ GtkWidget* button = gtk_button_new_with_label("Connect");
+ gtk_widget_set_size_request(button, 70, 30);
+ g_signal_connect(button, "clicked", G_CALLBACK(OnClickedCallback), this);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+
+ GtkWidget* halign = gtk_alignment_new(1, 0, 0, 0);
+ gtk_container_add(GTK_CONTAINER(halign), hbox);
+ gtk_box_pack_start(GTK_BOX(vbox_), halign, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(window_);
+}
+
+void GtkMainWnd::SwitchToPeerList(const Peers& peers) {
+ LOG(INFO) << __FUNCTION__;
+
+ // Clean up buffers from a potential previous session.
+ local_renderer_.reset();
+ remote_renderer_.reset();
+
+ if (!peer_list_) {
+ gtk_container_set_border_width(GTK_CONTAINER(window_), 0);
+ if (vbox_) {
+ gtk_widget_destroy(vbox_);
+ vbox_ = NULL;
+ server_edit_ = NULL;
+ port_edit_ = NULL;
+ } else if (draw_area_) {
+ gtk_widget_destroy(draw_area_);
+ draw_area_ = NULL;
+ draw_buffer_.reset();
+ }
+
+ peer_list_ = gtk_tree_view_new();
+ g_signal_connect(peer_list_, "row-activated",
+ G_CALLBACK(OnRowActivatedCallback), this);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(peer_list_), FALSE);
+ InitializeList(peer_list_);
+ gtk_container_add(GTK_CONTAINER(window_), peer_list_);
+ gtk_widget_show_all(window_);
+ } else {
+ GtkListStore* store =
+ GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(peer_list_)));
+ gtk_list_store_clear(store);
+ }
+
+ AddToList(peer_list_, "List of currently connected peers:", -1);
+ for (Peers::const_iterator i = peers.begin(); i != peers.end(); ++i)
+ AddToList(peer_list_, i->second.c_str(), i->first);
+}
+
+void GtkMainWnd::SwitchToStreamingUI() {
+ LOG(INFO) << __FUNCTION__;
+
+ ASSERT(draw_area_ == NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window_), 0);
+ if (peer_list_) {
+ gtk_widget_destroy(peer_list_);
+ peer_list_ = NULL;
+ }
+
+ draw_area_ = gtk_drawing_area_new();
+ gtk_container_add(GTK_CONTAINER(window_), draw_area_);
+
+ gtk_widget_show_all(window_);
+}
+
+void GtkMainWnd::OnDestroyed(GtkWidget* widget, GdkEvent* event) {
+ callback_->Close();
+ window_ = NULL;
+ draw_area_ = NULL;
+ vbox_ = NULL;
+ server_edit_ = NULL;
+ port_edit_ = NULL;
+ peer_list_ = NULL;
+}
+
+void GtkMainWnd::OnClicked(GtkWidget* widget) {
+ server_ = gtk_entry_get_text(GTK_ENTRY(server_edit_));
+ port_ = gtk_entry_get_text(GTK_ENTRY(port_edit_));
+ int port = port_.length() ? atoi(port_.c_str()) : 0;
+ callback_->StartLogin(server_, port);
+}
+
+void GtkMainWnd::OnKeyPress(GtkWidget* widget, GdkEventKey* key) {
+ if (key->type == GDK_KEY_PRESS) {
+ switch (key->keyval) {
+ case GDK_Escape:
+ if (draw_area_) {
+ callback_->DisconnectFromCurrentPeer();
+ } else if (peer_list_) {
+ callback_->DisconnectFromServer();
+ }
+ break;
+
+ case GDK_KP_Enter:
+ case GDK_Return:
+ if (vbox_) {
+ OnClicked(NULL);
+ } else if (peer_list_) {
+ // OnRowActivated will be called automatically when the user
+ // presses enter.
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void GtkMainWnd::OnRowActivated(GtkTreeView* tree_view, GtkTreePath* path,
+ GtkTreeViewColumn* column) {
+ ASSERT(peer_list_ != NULL);
+ GtkTreeIter iter;
+ GtkTreeModel* model;
+ GtkTreeSelection* selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ char* text;
+ int id = -1;
+ gtk_tree_model_get(model, &iter, 0, &text, 1, &id, -1);
+ if (id != -1)
+ callback_->ConnectToPeer(id);
+ g_free(text);
+ }
+}
+
+void GtkMainWnd::OnRedraw() {
+ gdk_threads_enter();
+
+ if (remote_renderer_.get() && remote_renderer_->image() != NULL &&
+ draw_area_ != NULL) {
+ int width = remote_renderer_->width();
+ int height = remote_renderer_->height();
+
+ if (!draw_buffer_.get()) {
+ draw_buffer_size_ = (width * height * 4) * 4;
+ draw_buffer_.reset(new uint8[draw_buffer_size_]);
+ gtk_widget_set_size_request(draw_area_, width * 2, height * 2);
+ }
+
+ const uint32* image = reinterpret_cast<const uint32*>(
+ remote_renderer_->image());
+ uint32* scaled = reinterpret_cast<uint32*>(draw_buffer_.get());
+ for (int r = 0; r < height; ++r) {
+ for (int c = 0; c < width; ++c) {
+ int x = c * 2;
+ scaled[x] = scaled[x + 1] = image[c];
+ }
+
+ uint32* prev_line = scaled;
+ scaled += width * 2;
+ memcpy(scaled, prev_line, (width * 2) * 4);
+
+ image += width;
+ scaled += width * 2;
+ }
+
+ if (local_renderer_.get() && local_renderer_->image()) {
+ image = reinterpret_cast<const uint32*>(local_renderer_->image());
+ scaled = reinterpret_cast<uint32*>(draw_buffer_.get());
+ // Position the local preview on the right side.
+ scaled += (width * 2) - (local_renderer_->width() / 2);
+ // right margin...
+ scaled -= 10;
+ // ... towards the bottom.
+ scaled += (height * width * 4) -
+ ((local_renderer_->height() / 2) *
+ (local_renderer_->width() / 2) * 4);
+ // bottom margin...
+ scaled -= (width * 2) * 5;
+ for (int r = 0; r < local_renderer_->height(); r += 2) {
+ for (int c = 0; c < local_renderer_->width(); c += 2) {
+ scaled[c / 2] = image[c + r * local_renderer_->width()];
+ }
+ scaled += width * 2;
+ }
+ }
+
+ gdk_draw_rgb_32_image(draw_area_->window,
+ draw_area_->style->fg_gc[GTK_STATE_NORMAL],
+ 0,
+ 0,
+ width * 2,
+ height * 2,
+ GDK_RGB_DITHER_MAX,
+ draw_buffer_.get(),
+ (width * 2) * 4);
+ }
+
+ gdk_threads_leave();
+}
+
+GtkMainWnd::VideoRenderer::VideoRenderer(GtkMainWnd* main_wnd)
+ : width_(0), height_(0), main_wnd_(main_wnd) {
+}
+
+GtkMainWnd::VideoRenderer::~VideoRenderer() {
+}
+
+bool GtkMainWnd::VideoRenderer::SetSize(int width, int height, int reserved) {
+ gdk_threads_enter();
+ width_ = width;
+ height_ = height;
+ image_.reset(new uint8[width * height * 4]);
+ gdk_threads_leave();
+ return true;
+}
+
+bool GtkMainWnd::VideoRenderer::RenderFrame(const cricket::VideoFrame* frame) {
+ gdk_threads_enter();
+
+ int size = width_ * height_ * 4;
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB,
+ image_.get(),
+ size,
+ width_ * 4);
+ // Convert the B,G,R,A frame to R,G,B,A, which is accepted by GTK.
+ // The 'A' is just padding for GTK, so we can use it as temp.
+ uint8* pix = image_.get();
+ uint8* end = image_.get() + size;
+ while (pix < end) {
+ pix[3] = pix[0]; // Save B to A.
+ pix[0] = pix[2]; // Set Red.
+ pix[2] = pix[3]; // Set Blue.
+ pix[3] = 0xFF; // Fixed Alpha.
+ pix += 4;
+ }
+
+ gdk_threads_leave();
+
+ g_idle_add(Redraw, main_wnd_);
+
+ return true;
+}
+
+
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.h b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.h
new file mode 100644
index 0000000000..64a3eb2b8d
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/linux/main_wnd.h
@@ -0,0 +1,114 @@
+/*
+ * 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_LINUX_MAIN_WND_H_
+#define PEERCONNECTION_SAMPLES_CLIENT_LINUX_MAIN_WND_H_
+
+#include "talk/examples/peerconnection_client/main_wnd.h"
+#include "talk/examples/peerconnection_client/peer_connection_client.h"
+
+// Forward declarations.
+typedef struct _GtkWidget GtkWidget;
+typedef union _GdkEvent GdkEvent;
+typedef struct _GdkEventKey GdkEventKey;
+typedef struct _GtkTreeView GtkTreeView;
+typedef struct _GtkTreePath GtkTreePath;
+typedef struct _GtkTreeViewColumn GtkTreeViewColumn;
+
+// Implements the main UI of the peer connection client.
+// This is functionally equivalent to the MainWnd class in the Windows
+// implementation.
+class GtkMainWnd : public MainWindow {
+ public:
+ GtkMainWnd();
+ ~GtkMainWnd();
+
+ virtual void RegisterObserver(MainWndCallback* callback);
+ virtual bool IsWindow();
+ virtual void SwitchToConnectUI();
+ virtual void SwitchToPeerList(const Peers& peers);
+ virtual void SwitchToStreamingUI();
+ virtual void MessageBox(const char* caption, const char* text,
+ bool is_error);
+ virtual MainWindow::UI current_ui();
+ virtual cricket::VideoRenderer* local_renderer();
+ virtual cricket::VideoRenderer* remote_renderer();
+ virtual void QueueUIThreadCallback(int msg_id, void* data);
+
+ // Creates and shows the main window with the |Connect UI| enabled.
+ bool Create();
+
+ // Destroys the window. When the window is destroyed, it ends the
+ // main message loop.
+ bool Destroy();
+
+ // Callback for when the main window is destroyed.
+ void OnDestroyed(GtkWidget* widget, GdkEvent* event);
+
+ // Callback for when the user clicks the "Connect" button.
+ void OnClicked(GtkWidget* widget);
+
+ // Callback for keystrokes. Used to capture Esc and Return.
+ void OnKeyPress(GtkWidget* widget, GdkEventKey* key);
+
+ // Callback when the user double clicks a peer in order to initiate a
+ // connection.
+ void OnRowActivated(GtkTreeView* tree_view, GtkTreePath* path,
+ GtkTreeViewColumn* column);
+
+ void OnRedraw();
+
+ protected:
+ class VideoRenderer : public cricket::VideoRenderer {
+ public:
+ VideoRenderer(GtkMainWnd* main_wnd);
+ virtual ~VideoRenderer();
+
+ virtual bool SetSize(int width, int height, int reserved);
+
+ virtual bool RenderFrame(const cricket::VideoFrame* frame);
+
+ const uint8* image() const {
+ return image_.get();
+ }
+
+ int width() const {
+ return width_;
+ }
+
+ int height() const {
+ return height_;
+ }
+
+ protected:
+ talk_base::scoped_array<uint8> image_;
+ int width_;
+ int height_;
+ GtkMainWnd* main_wnd_;
+ };
+
+ protected:
+ GtkWidget* window_; // Our main window.
+ GtkWidget* draw_area_; // The drawing surface for rendering video streams.
+ GtkWidget* vbox_; // Container for the Connect UI.
+ GtkWidget* server_edit_;
+ GtkWidget* port_edit_;
+ GtkWidget* peer_list_; // The list of peers.
+ MainWndCallback* callback_;
+ std::string server_;
+ std::string port_;
+ talk_base::scoped_ptr<VideoRenderer> local_renderer_;
+ talk_base::scoped_ptr<VideoRenderer> remote_renderer_;
+ talk_base::scoped_ptr<uint8> draw_buffer_;
+ int draw_buffer_size_;
+};
+
+#endif // PEERCONNECTION_SAMPLES_CLIENT_LINUX_MAIN_WND_H_
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main.cc
new file mode 100644
index 0000000000..27d81e0bf0
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main.cc
@@ -0,0 +1,58 @@
+/*
+ * 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 "talk/examples/peerconnection_client/conductor.h"
+#include "talk/examples/peerconnection_client/main_wnd.h"
+#include "talk/examples/peerconnection_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()) {
+ while ((conductor.connection_active() || client.is_connected()) &&
+ (gm = ::GetMessage(&msg, NULL, 0, 0)) && gm != -1) {
+ if (!wnd.PreTranslateMessage(&msg)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.cc
new file mode 100644
index 0000000000..c237331e2c
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.cc
@@ -0,0 +1,582 @@
+/*
+ * 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 "talk/examples/peerconnection_client/main_wnd.h"
+
+#include <math.h>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+ATOM MainWnd::wnd_class_ = 0;
+const wchar_t MainWnd::kClassName[] = L"WebRTC_MainWnd";
+
+namespace {
+
+const char kConnecting[] = "Connecting... ";
+const char kNoVideoStreams[] = "(no video streams either way)";
+const char kNoIncomingStream[] = "(no incoming video)";
+
+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;
+
+ ui_thread_id_ = ::GetCurrentThreadId();
+ 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() {
+ 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();
+ }
+ }
+ }
+ } else if (msg->hwnd == NULL && msg->message == UI_THREAD_CALLBACK) {
+ callback_->UIThreadCallback(static_cast<int>(msg->wParam),
+ reinterpret_cast<void*>(msg->lParam));
+ ret = true;
+ }
+ return ret;
+}
+
+void MainWnd::SwitchToConnectUI() {
+ ASSERT(IsWindow());
+ LayoutPeerListUI(false);
+ ui_ = CONNECT_TO_SERVER;
+ LayoutConnectUI(true);
+ ::SetFocus(edit1_);
+}
+
+void MainWnd::SwitchToPeerList(const Peers& peers) {
+ remote_video_.reset();
+ local_video_.reset();
+
+ 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);
+ ::SetFocus(listbox_);
+}
+
+void MainWnd::SwitchToStreamingUI() {
+ LayoutConnectUI(false);
+ LayoutPeerListUI(false);
+ ui_ = STREAMING;
+}
+
+void MainWnd::MessageBox(const char* caption, const char* text, bool is_error) {
+ DWORD flags = MB_OK;
+ if (is_error)
+ flags |= MB_ICONERROR;
+
+ ::MessageBoxA(handle(), text, caption, flags);
+}
+
+cricket::VideoRenderer* MainWnd::local_renderer() {
+ if (!local_video_.get())
+ local_video_.reset(new VideoRenderer(handle(), 1, 1));
+ return local_video_.get();
+}
+
+cricket::VideoRenderer* MainWnd::remote_renderer() {
+ if (!remote_video_.get())
+ remote_video_.reset(new VideoRenderer(handle(), 1, 1));
+ return remote_video_.get();
+}
+
+void MainWnd::QueueUIThreadCallback(int msg_id, void* data) {
+ ::PostThreadMessage(ui_thread_id_, UI_THREAD_CALLBACK,
+ static_cast<WPARAM>(msg_id), reinterpret_cast<LPARAM>(data));
+}
+
+void MainWnd::OnPaint() {
+ PAINTSTRUCT ps;
+ ::BeginPaint(handle(), &ps);
+
+ RECT rc;
+ ::GetClientRect(handle(), &rc);
+
+ if (ui_ == STREAMING && remote_video_.get() && local_video_.get()) {
+ AutoLock<VideoRenderer> local_lock(local_video_.get());
+ AutoLock<VideoRenderer> remote_lock(remote_video_.get());
+
+ const BITMAPINFO& bmi = remote_video_->bmi();
+ int height = abs(bmi.bmiHeader.biHeight);
+ int width = bmi.bmiHeader.biWidth;
+
+ const uint8* image = remote_video_->image();
+ if (image != NULL) {
+ HDC dc_mem = ::CreateCompatibleDC(ps.hdc);
+ ::SetStretchBltMode(dc_mem, HALFTONE);
+
+ // Set the map mode so that the ratio will be maintained for us.
+ HDC all_dc[] = { ps.hdc, dc_mem };
+ for (int i = 0; i < ARRAY_SIZE(all_dc); ++i) {
+ SetMapMode(all_dc[i], MM_ISOTROPIC);
+ SetWindowExtEx(all_dc[i], width, height, NULL);
+ SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL);
+ }
+
+ HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom);
+ HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem);
+
+ POINT logical_area = { rc.right, rc.bottom };
+ DPtoLP(ps.hdc, &logical_area, 1);
+
+ HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
+ RECT logical_rect = {0, 0, logical_area.x, logical_area.y };
+ ::FillRect(dc_mem, &logical_rect, brush);
+ ::DeleteObject(brush);
+
+ int max_unit = std::max(width, height);
+ int x = (logical_area.x / 2) - (width / 2);
+ int y = (logical_area.y / 2) - (height / 2);
+
+ StretchDIBits(dc_mem, x, y, width, height,
+ 0, 0, width, height, image, &bmi, DIB_RGB_COLORS, SRCCOPY);
+
+ if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) {
+ const BITMAPINFO& bmi = local_video_->bmi();
+ image = local_video_->image();
+ int thumb_width = bmi.bmiHeader.biWidth / 4;
+ int thumb_height = abs(bmi.bmiHeader.biHeight) / 4;
+ StretchDIBits(dc_mem,
+ logical_area.x - thumb_width - 10,
+ logical_area.y - thumb_height - 10,
+ thumb_width, thumb_height,
+ 0, 0, bmi.bmiHeader.biWidth, -bmi.bmiHeader.biHeight,
+ image, &bmi, DIB_RGB_COLORS, SRCCOPY);
+ }
+
+ BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y,
+ dc_mem, 0, 0, SRCCOPY);
+
+ // Cleanup.
+ ::SelectObject(dc_mem, bmp_old);
+ ::DeleteObject(bmp_mem);
+ ::DeleteDC(dc_mem);
+ } else {
+ // We're still waiting for the video stream to be initialized.
+ HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
+ ::FillRect(ps.hdc, &rc, brush);
+ ::DeleteObject(brush);
+
+ HGDIOBJ old_font = ::SelectObject(ps.hdc, GetDefaultFont());
+ ::SetTextColor(ps.hdc, RGB(0xff, 0xff, 0xff));
+ ::SetBkMode(ps.hdc, TRANSPARENT);
+
+ std::string text(kConnecting);
+ if (!local_video_->image()) {
+ text += kNoVideoStreams;
+ } else {
+ text += kNoIncomingStream;
+ }
+ ::DrawTextA(ps.hdc, text.c_str(), -1, &rc,
+ DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ ::SelectObject(ps.hdc, old_font);
+ }
+ } else {
+ 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_);
+ } 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;
+}
+
+// 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_ != 0);
+ 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) != FALSE);
+ ::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);
+ InvalidateRect(wnd_, NULL, TRUE);
+ }
+}
+
+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);
+}
+
+//
+// MainWnd::VideoRenderer
+//
+
+MainWnd::VideoRenderer::VideoRenderer(HWND wnd, int width, int height)
+ : wnd_(wnd) {
+ ::InitializeCriticalSection(&buffer_lock_);
+ ZeroMemory(&bmi_, sizeof(bmi_));
+ bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi_.bmiHeader.biPlanes = 1;
+ bmi_.bmiHeader.biBitCount = 32;
+ bmi_.bmiHeader.biCompression = BI_RGB;
+ bmi_.bmiHeader.biWidth = width;
+ bmi_.bmiHeader.biHeight = -height;
+ bmi_.bmiHeader.biSizeImage = width * height *
+ (bmi_.bmiHeader.biBitCount >> 3);
+}
+
+MainWnd::VideoRenderer::~VideoRenderer() {
+ ::DeleteCriticalSection(&buffer_lock_);
+}
+
+bool MainWnd::VideoRenderer::SetSize(int width, int height, int reserved) {
+ AutoLock<VideoRenderer> lock(this);
+
+ bmi_.bmiHeader.biWidth = width;
+ bmi_.bmiHeader.biHeight = -height;
+ bmi_.bmiHeader.biSizeImage = width * height *
+ (bmi_.bmiHeader.biBitCount >> 3);
+ image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]);
+
+ return true;
+}
+
+bool MainWnd::VideoRenderer::RenderFrame(const cricket::VideoFrame* frame) {
+ if (!frame)
+ return false;
+
+ {
+ AutoLock<VideoRenderer> lock(this);
+
+ ASSERT(image_.get() != NULL);
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(),
+ bmi_.bmiHeader.biSizeImage,
+ bmi_.bmiHeader.biWidth *
+ (bmi_.bmiHeader.biBitCount >> 3));
+ }
+
+ InvalidateRect(wnd_, NULL, TRUE);
+
+ return true;
+}
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.h b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.h
new file mode 100644
index 0000000000..02be371601
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/main_wnd.h
@@ -0,0 +1,190 @@
+/*
+ * 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 "talk/examples/peerconnection_client/peer_connection_client.h"
+#include "talk/base/win32.h"
+#include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/videocommon.h"
+#include "talk/session/phone/videoframe.h"
+#include "talk/session/phone/videorenderer.h"
+
+class MainWndCallback {
+ public:
+ virtual bool StartLogin(const std::string& server, int port) = 0;
+ virtual void DisconnectFromServer() = 0;
+ virtual void ConnectToPeer(int peer_id) = 0;
+ virtual void DisconnectFromCurrentPeer() = 0;
+ virtual void UIThreadCallback(int msg_id, void* data) = 0;
+ virtual void Close() = 0;
+ protected:
+ virtual ~MainWndCallback() {}
+};
+
+// Pure virtual interface for the main window.
+class MainWindow {
+ public:
+ virtual ~MainWindow() {}
+
+ enum UI {
+ CONNECT_TO_SERVER,
+ LIST_PEERS,
+ STREAMING,
+ };
+
+ virtual void RegisterObserver(MainWndCallback* callback) = 0;
+
+ virtual bool IsWindow() = 0;
+ virtual void MessageBox(const char* caption, const char* text,
+ bool is_error) = 0;
+
+ virtual UI current_ui() = 0;
+
+ virtual void SwitchToConnectUI() = 0;
+ virtual void SwitchToPeerList(const Peers& peers) = 0;
+ virtual void SwitchToStreamingUI() = 0;
+
+ virtual cricket::VideoRenderer* local_renderer() = 0;
+ virtual cricket::VideoRenderer* remote_renderer() = 0;
+
+ virtual void QueueUIThreadCallback(int msg_id, void* data) = 0;
+};
+
+#ifdef WIN32
+
+class MainWnd : public MainWindow {
+ public:
+ static const wchar_t kClassName[];
+
+ enum WindowMessages {
+ UI_THREAD_CALLBACK = WM_APP + 1,
+ };
+
+ MainWnd();
+ ~MainWnd();
+
+ bool Create();
+ bool Destroy();
+ bool PreTranslateMessage(MSG* msg);
+
+ virtual void RegisterObserver(MainWndCallback* callback);
+ virtual bool IsWindow();
+ virtual void SwitchToConnectUI();
+ virtual void SwitchToPeerList(const Peers& peers);
+ virtual void SwitchToStreamingUI();
+ virtual void MessageBox(const char* caption, const char* text,
+ bool is_error);
+ virtual UI current_ui() { return ui_; }
+
+ virtual cricket::VideoRenderer* local_renderer();
+ virtual cricket::VideoRenderer* remote_renderer();
+
+ virtual void QueueUIThreadCallback(int msg_id, void* data);
+
+ HWND handle() const { return wnd_; }
+
+ class VideoRenderer : public cricket::VideoRenderer {
+ public:
+ VideoRenderer(HWND wnd, int width, int height);
+ virtual ~VideoRenderer();
+
+ void Lock() {
+ ::EnterCriticalSection(&buffer_lock_);
+ }
+
+ void Unlock() {
+ ::LeaveCriticalSection(&buffer_lock_);
+ }
+
+ virtual bool SetSize(int width, int height, int reserved);
+
+ // Called when a new frame is available for display.
+ virtual bool RenderFrame(const cricket::VideoFrame* frame);
+
+ const BITMAPINFO& bmi() const { return bmi_; }
+ const uint8* image() const { return image_.get(); }
+
+ protected:
+ enum {
+ SET_SIZE,
+ RENDER_FRAME,
+ };
+
+ HWND wnd_;
+ BITMAPINFO bmi_;
+ talk_base::scoped_array<uint8> image_;
+ CRITICAL_SECTION buffer_lock_;
+ };
+
+ // A little helper class to make sure we always to proper locking and
+ // unlocking when working with VideoRenderer buffers.
+ template <typename T>
+ class AutoLock {
+ public:
+ explicit AutoLock(T* obj) : obj_(obj) { obj_->Lock(); }
+ ~AutoLock() { obj_->Unlock(); }
+ protected:
+ T* obj_;
+ };
+
+ 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:
+ talk_base::scoped_ptr<VideoRenderer> remote_video_;
+ talk_base::scoped_ptr<VideoRenderer> local_video_;
+ UI ui_;
+ HWND wnd_;
+ DWORD ui_thread_id_;
+ HWND edit1_;
+ HWND edit2_;
+ HWND label1_;
+ HWND label2_;
+ HWND button_;
+ HWND listbox_;
+ bool destroyed_;
+ void* nested_msg_;
+ MainWndCallback* callback_;
+ static ATOM wnd_class_;
+};
+#endif // WIN32
+
+#endif // PEERCONNECTION_SAMPLES_CLIENT_MAIN_WND_H_
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.cc b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.cc
new file mode 100644
index 0000000000..07e3c74671
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.cc
@@ -0,0 +1,479 @@
+/*
+ * 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 "talk/examples/peerconnection_client/peer_connection_client.h"
+
+#include "talk/examples/peerconnection_client/defaults.h"
+#include "talk/base/common.h"
+#include "talk/base/nethelpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+
+#ifdef WIN32
+#include "talk/base/win32socketserver.h"
+#endif
+
+using talk_base::sprintfn;
+
+namespace {
+
+// This is our magical hangup signal.
+const char kByeMessage[] = "BYE";
+
+talk_base::AsyncSocket* CreateClientSocket() {
+#ifdef WIN32
+ return new talk_base::Win32Socket();
+#elif defined(POSIX)
+ talk_base::Thread* thread = talk_base::Thread::Current();
+ ASSERT(thread != NULL);
+ return thread->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+#else
+#error Platform not supported.
+#endif
+}
+
+}
+
+PeerConnectionClient::PeerConnectionClient()
+ : callback_(NULL),
+ control_socket_(CreateClientSocket()),
+ hanging_get_(CreateClientSocket()),
+ state_(NOT_CONNECTED),
+ my_id_(-1) {
+ 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());
+
+ if (state_ != NOT_CONNECTED) {
+ LOG(WARNING)
+ << "The client must not be connected before you can call Connect()";
+ return false;
+ }
+
+ 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()) {
+ int errcode = 0;
+ hostent* h = talk_base::SafeGetHostByName(
+ server_address_.IPAsString().c_str(), &errcode);
+ 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])));
+ talk_base::FreeHostEnt(h);
+ }
+ }
+
+ char buffer[1024];
+ sprintfn(buffer, sizeof(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];
+ sprintfn(headers, sizeof(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::SendHangUp(int peer_id) {
+ return SendToPeer(peer_id, kByeMessage);
+}
+
+bool PeerConnectionClient::IsSendingMessage() {
+ return state_ == CONNECTED &&
+ control_socket_->GetState() != talk_base::Socket::CS_CLOSED;
+}
+
+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) {
+ state_ = SIGNING_OUT;
+
+ if (my_id_ != -1) {
+ char buffer[1024];
+ sprintfn(buffer, sizeof(buffer),
+ "GET /sign_out?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
+ onconnect_data_ = buffer;
+ return ConnectControlSocket();
+ } else {
+ // Can occur if the app is closed before we finish connecting.
+ return true;
+ }
+ } 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());
+ size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());
+ ASSERT(sent == onconnect_data_.length());
+ UNUSED(sent);
+ onconnect_data_.clear();
+}
+
+void PeerConnectionClient::OnHangingGetConnect(talk_base::AsyncSocket* socket) {
+ char buffer[1024];
+ sprintfn(buffer, sizeof(buffer),
+ "GET /wait?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
+ int len = strlen(buffer);
+ int sent = socket->Send(buffer, len);
+ ASSERT(sent == len);
+ UNUSED2(sent, len);
+}
+
+void PeerConnectionClient::OnMessageFromPeer(int peer_id,
+ const std::string& message) {
+ if (message.length() == (sizeof(kByeMessage) - 1) &&
+ message.compare(kByeMessage) == 0) {
+ callback_->OnPeerDisconnected(peer_id);
+ } else {
+ callback_->OnMessageFromPeer(peer_id, message);
+ }
+}
+
+bool PeerConnectionClient::GetHeaderValue(const std::string& data,
+ size_t eoh,
+ const char* header_pattern,
+ size_t* value) {
+ ASSERT(value != NULL);
+ size_t found = data.find(header_pattern);
+ if (found != std::string::npos && found < eoh) {
+ *value = atoi(&data[found + strlen(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 != NULL);
+ size_t found = data.find(header_pattern);
+ if (found != std::string::npos && found < eoh) {
+ size_t begin = found + strlen(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";
+ 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, &notification_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_ == static_cast<int>(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);
+ }
+ }
+ } else {
+ 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 != NULL);
+ ASSERT(id != NULL);
+ ASSERT(connected != NULL);
+ ASSERT(!entry.empty());
+
+ *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();
+
+#ifdef WIN32
+ if (err != WSAECONNREFUSED) {
+#else
+ if (err != ECONNREFUSED) {
+#endif
+ if (socket == hanging_get_.get()) {
+ if (state_ == CONNECTED) {
+ LOG(INFO) << "Issuing a new hanging get";
+ hanging_get_->Close();
+ hanging_get_->Connect(server_address_);
+ }
+ } else {
+ callback_->OnMessageSent(err);
+ }
+ } else {
+ // Failed to connect to the server.
+ Close();
+ callback_->OnDisconnected();
+ }
+}
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.h b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.h
new file mode 100644
index 0000000000..bc02bdd823
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peer_connection_client.h
@@ -0,0 +1,109 @@
+/*
+ * 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/physicalsocketserver.h"
+#include "talk/base/scoped_ptr.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 peer_id) = 0;
+ virtual void OnMessageFromPeer(int peer_id, const std::string& message) = 0;
+ virtual void OnMessageSent(int err) = 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 SendHangUp(int peer_id);
+ bool IsSendingMessage();
+
+ bool SignOut();
+
+ protected:
+ void Close();
+ bool ConnectControlSocket();
+ void OnConnect(talk_base::AsyncSocket* socket);
+ void OnHangingGetConnect(talk_base::AsyncSocket* socket);
+ void OnMessageFromPeer(int peer_id, const std::string& message);
+
+ // 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::scoped_ptr<talk_base::AsyncSocket> control_socket_;
+ talk_base::scoped_ptr<talk_base::AsyncSocket> 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_
diff --git a/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peerconnection_client.gyp b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peerconnection_client.gyp
new file mode 100644
index 0000000000..27cce641e8
--- /dev/null
+++ b/third_party_mods/libjingle/source/talk/examples/peerconnection_client/peerconnection_client.gyp
@@ -0,0 +1,55 @@
+# 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.
+
+{
+ 'conditions': [
+ ['OS=="linux"', {
+ 'targets': [
+ {
+ 'target_name': 'peerconnection_client_dev',
+ 'type': 'executable',
+ 'sources': [
+ 'conductor.cc',
+ 'conductor.h',
+ 'defaults.cc',
+ 'defaults.h',
+ 'linux/main.cc',
+ 'linux/main_wnd.cc',
+ 'linux/main_wnd.h',
+ 'peer_connection_client.cc',
+ 'peer_connection_client.h',
+ ],
+ 'dependencies': [
+ '../../../../libjingle.gyp:libjingle_app',
+ # TODO(tommi): Switch to this and remove specific gtk dependency
+ # sections below for cflags and link_settings.
+ # '<(DEPTH)/build/linux/system.gyp:gtk',
+ ],
+ 'include_dirs': [
+ '../../../',
+ #TODO(perkj): Remove when this project is in the correct folder.
+ '../../../../../../third_party/libjingle/source/',
+ ],
+ 'cflags': [
+ '<!@(pkg-config --cflags gtk+-2.0)',
+ ],
+ 'link_settings': {
+ 'ldflags': [
+ '<!@(pkg-config --libs-only-L --libs-only-other gtk+-2.0 gthread-2.0)',
+ ],
+ 'libraries': [
+ '<!@(pkg-config --libs-only-l gtk+-2.0 gthread-2.0)',
+ '-lX11',
+ '-lXext',
+ ],
+ },
+ },
+ ], # targets
+ }, ], # OS="linux"
+ ], # conditions
+} \ No newline at end of file