summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/video_frame.cc27
-rw-r--r--media/base/video_frame.h16
-rw-r--r--media/base/video_frame_unittest.cc20
-rw-r--r--remoting/base/protocol/chromotocol.proto2
-rw-r--r--remoting/base/protocol_decoder.cc4
-rw-r--r--remoting/base/protocol_decoder.h4
-rw-r--r--remoting/base/protocol_decoder_unittest.cc4
-rw-r--r--remoting/chromoting.gyp31
-rw-r--r--remoting/client/chromoting_view.h37
-rw-r--r--remoting/client/client_util.cc76
-rw-r--r--remoting/client/client_util.h19
-rw-r--r--remoting/client/decoder.h43
-rw-r--r--remoting/client/decoder_verbatim.cc56
-rw-r--r--remoting/client/decoder_verbatim.h16
-rw-r--r--remoting/client/host_connection.cc16
-rw-r--r--remoting/client/simple_client.cc84
-rw-r--r--remoting/client/x11_client.cc262
-rw-r--r--remoting/client/x11_view.cc151
-rw-r--r--remoting/client/x11_view.h56
-rw-r--r--remoting/host/capturer.cc4
-rw-r--r--remoting/host/capturer.h4
-rw-r--r--remoting/host/capturer_fake.cc12
-rw-r--r--remoting/host/capturer_fake_ascii.cc2
-rw-r--r--remoting/host/capturer_gdi.cc2
-rw-r--r--remoting/host/client_connection.cc10
-rw-r--r--remoting/host/client_connection.h2
-rw-r--r--remoting/host/client_connection_unittest.cc3
-rw-r--r--remoting/host/encoder.h4
-rw-r--r--remoting/host/encoder_verbatim.cc38
-rw-r--r--remoting/host/encoder_verbatim.h7
-rw-r--r--remoting/host/encoder_vp8.cc2
-rw-r--r--remoting/host/encoder_vp8.h2
-rw-r--r--remoting/host/encoder_vp8_unittest.cc3
-rw-r--r--remoting/host/event_executor_win.cc10
-rw-r--r--remoting/host/mock_objects.h8
-rw-r--r--remoting/host/session_manager.cc23
-rw-r--r--remoting/host/session_manager.h6
-rw-r--r--remoting/host/session_manager_unittest.cc6
-rw-r--r--remoting/host/simple_host.cc30
39 files changed, 875 insertions, 227 deletions
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 3e2164e..a7eeb1e 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -47,6 +47,29 @@ void VideoFrame::CreateFrame(VideoFrame::Format format,
*frame_out = alloc_worked ? frame : NULL;
}
+void VideoFrame::CreateFrameExternal(VideoFrame::Format format,
+ size_t width,
+ size_t height,
+ uint8* const data[kMaxPlanes],
+ const int32 strides[kMaxPlanes],
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<VideoFrame>* frame_out) {
+ DCHECK(frame_out);
+ scoped_refptr<VideoFrame> frame =
+ new VideoFrame(VideoFrame::TYPE_SYSTEM_MEMORY, format, width, height);
+ if (frame) {
+ frame->SetTimestamp(timestamp);
+ frame->SetDuration(duration);
+ frame->external_memory_ = true;
+ for (size_t i = 0; i < kMaxPlanes; ++i) {
+ frame->data_[i] = data[i];
+ frame->strides_[i] = strides[i];
+ }
+ }
+ *frame_out = frame;
+}
+
// static
void VideoFrame::CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out) {
*frame_out = new VideoFrame(VideoFrame::TYPE_SYSTEM_MEMORY,
@@ -179,6 +202,7 @@ VideoFrame::VideoFrame(VideoFrame::BufferType type,
planes_ = 0;
memset(&strides_, 0, sizeof(strides_));
memset(&data_, 0, sizeof(data_));
+ external_memory_ = false;
private_buffer_ = NULL;
}
@@ -186,7 +210,8 @@ VideoFrame::~VideoFrame() {
// In multi-plane allocations, only a single block of memory is allocated
// on the heap, and other |data| pointers point inside the same, single block
// so just delete index 0.
- delete[] data_[0];
+ if (!external_memory_)
+ delete[] data_[0];
}
bool VideoFrame::IsEndOfStream() const {
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 77563a9..d4f3b94 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -52,6 +52,18 @@ class VideoFrame : public StreamSample {
base::TimeDelta duration,
scoped_refptr<VideoFrame>* frame_out);
+ // Creates a new frame with given parameters. Buffers for the frame are
+ // provided externally. Reference to the buffers and strides are copied
+ // from |data| and |strides| respectively.
+ static void CreateFrameExternal(Format format,
+ size_t width,
+ size_t height,
+ uint8* const data[kMaxPlanes],
+ const int32 strides[kMaxPlanes],
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<VideoFrame>* frame_out);
+
// Creates a frame with format equals to VideoFrame::EMPTY, width, height
// timestamp and duration are all 0.
static void CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out);
@@ -127,6 +139,10 @@ class VideoFrame : public StreamSample {
// Array of data pointers to each plane.
uint8* data_[kMaxPlanes];
+ // True of memory referenced by |data_| is provided externally and shouldn't
+ // be deleted.
+ bool external_memory_;
+
// Private buffer pointer, can be used for EGLImage.
void* private_buffer_;
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc
index caa5675..1cacdd1 100644
--- a/media/base/video_frame_unittest.cc
+++ b/media/base/video_frame_unittest.cc
@@ -195,4 +195,24 @@ TEST(VideoFrame, CreatePrivateFrame) {
EXPECT_EQ(NULL, frame->data(VideoFrame::kYPlane));
}
+TEST(VideoFram, CreateExternalFrame) {
+ scoped_array<uint8> memory(new uint8[1]);
+
+ scoped_refptr<media::VideoFrame> frame;
+ uint8* data[3] = {memory.get(), NULL, NULL};
+ int strides[3] = {1, 0, 0};
+ VideoFrame::CreateFrameExternal(media::VideoFrame::RGB32, 0, 0,
+ data, strides,
+ base::TimeDelta(), base::TimeDelta(), &frame);
+ ASSERT_TRUE(frame);
+
+ // Test frame properties.
+ EXPECT_EQ(1, frame->stride(VideoFrame::kRGBPlane));
+ EXPECT_EQ(memory.get(), frame->data(VideoFrame::kRGBPlane));
+
+ // Delete |memory| and then |frame|.
+ memory.reset();
+ frame = NULL;
+}
+
} // namespace media
diff --git a/remoting/base/protocol/chromotocol.proto b/remoting/base/protocol/chromotocol.proto
index a7907f9..e60716f 100644
--- a/remoting/base/protocol/chromotocol.proto
+++ b/remoting/base/protocol/chromotocol.proto
@@ -8,7 +8,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
-package chromotocol_pb;
+package remoting;
// A message that gets sent to the client after the client is connected to the
// host. It contains information that the client needs to know about the host.
diff --git a/remoting/base/protocol_decoder.cc b/remoting/base/protocol_decoder.cc
index 1334b20..3b60f2b 100644
--- a/remoting/base/protocol_decoder.cc
+++ b/remoting/base/protocol_decoder.cc
@@ -18,12 +18,12 @@ ProtocolDecoder::ProtocolDecoder()
void ProtocolDecoder::ParseClientMessages(scoped_refptr<media::DataBuffer> data,
ClientMessageList* messages) {
- ParseMessages<chromotocol_pb::ClientMessage>(data, messages);
+ ParseMessages<ClientMessage>(data, messages);
}
void ProtocolDecoder::ParseHostMessages(scoped_refptr<media::DataBuffer> data,
HostMessageList* messages) {
- ParseMessages<chromotocol_pb::HostMessage>(data, messages);
+ ParseMessages<HostMessage>(data, messages);
}
template <typename T>
diff --git a/remoting/base/protocol_decoder.h b/remoting/base/protocol_decoder.h
index 9350e51..c817723 100644
--- a/remoting/base/protocol_decoder.h
+++ b/remoting/base/protocol_decoder.h
@@ -15,8 +15,8 @@
namespace remoting {
-typedef std::vector<chromotocol_pb::HostMessage*> HostMessageList;
-typedef std::vector<chromotocol_pb::ClientMessage*> ClientMessageList;
+typedef std::vector<HostMessage*> HostMessageList;
+typedef std::vector<ClientMessage*> ClientMessageList;
// A protocol decoder is used to decode data transmitted in the chromoting
// network.
diff --git a/remoting/base/protocol_decoder_unittest.cc b/remoting/base/protocol_decoder_unittest.cc
index 900f7eb..d1bd584 100644
--- a/remoting/base/protocol_decoder_unittest.cc
+++ b/remoting/base/protocol_decoder_unittest.cc
@@ -16,7 +16,7 @@ static const int kWidth = 640;
static const int kHeight = 480;
static const std::string kTestData = "Chromoting rockz";
-static void AppendMessage(const chromotocol_pb::HostMessage& msg,
+static void AppendMessage(const HostMessage& msg,
std::string* buffer) {
// Contains one encoded message.
scoped_refptr<media::DataBuffer> encoded_msg;
@@ -31,7 +31,7 @@ static void PrepareData(uint8** buffer, int* size) {
std::string encoded_data;
// The first message is InitClient.
- chromotocol_pb::HostMessage msg;
+ HostMessage msg;
msg.mutable_init_client()->set_width(kWidth);
msg.mutable_init_client()->set_height(kHeight);
AppendMessage(msg, &encoded_data);
diff --git a/remoting/chromoting.gyp b/remoting/chromoting.gyp
index 1b13059..22dd2bf 100644
--- a/remoting/chromoting.gyp
+++ b/remoting/chromoting.gyp
@@ -70,7 +70,6 @@
'client/pepper/pepper_main.cc',
],
'conditions': [
-
['OS=="linux" and target_arch=="x64" and linux_fpic!=1', {
# Shared libraries need -fPIC on x86-64
'cflags': [
@@ -194,9 +193,14 @@
'chromoting_jingle_glue',
],
'sources': [
+ 'client/client_util.cc',
+ 'client/client_util.h',
+ 'client/chromoting_view.h',
'client/decoder.h',
'client/decoder_verbatim.cc',
'client/decoder_verbatim.h',
+ 'client/host_connection.cc',
+ 'client/host_connection.h',
],
}, # end of target 'chromoting_client'
@@ -228,13 +232,34 @@
'chromoting_jingle_glue',
],
'sources': [
- 'client/host_connection.cc',
- 'client/host_connection.h',
'client/simple_client.cc',
],
}, # end of target 'chromoting_simple_client'
{
+ 'target_name': 'chromoting_x11_client',
+ 'type': 'executable',
+ 'dependencies': [
+ 'chromoting_base',
+ 'chromoting_client',
+ 'chromoting_jingle_glue',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-ldl',
+ '-lX11',
+ '-lXrender',
+ '-lXext',
+ ],
+ },
+ 'sources': [
+ 'client/x11_client.cc',
+ 'client/x11_view.cc',
+ 'client/x11_view.h',
+ ],
+ }, # end of target 'chromoting_x11_client'
+
+ {
'target_name': 'chromoting_jingle_glue',
'type': '<(library)',
'dependencies': [
diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h
new file mode 100644
index 0000000..cf70b99
--- /dev/null
+++ b/remoting/client/chromoting_view.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_CHROMOTING_VIEW_H_
+#define REMOTING_CLIENT_CHROMOTING_VIEW_H_
+
+#include "base/ref_counted.h"
+
+namespace remoting {
+
+class HostMessage;
+
+// ChromotingView defines the behavior of an object that draws a view of the
+// remote desktop. Its main function is to choose the right decoder and render
+// the update stream onto the screen.
+class ChromotingView : public base::RefCountedThreadSafe<ChromotingView> {
+ public:
+ virtual ~ChromotingView() {}
+
+ // Tells the ChromotingView to paint the current image on the screen.
+ // TODO(hclam): Add rects as parameter if needed.
+ virtual void Paint() = 0;
+
+ // Handle the BeginUpdateStream message.
+ virtual void HandleBeginUpdateStream(HostMessage* msg) = 0;
+
+ // Handle the UpdateStreamPacket message.
+ virtual void HandleUpdateStreamPacket(HostMessage* msg) = 0;
+
+ // Handle the EndUpdateStream message.
+ virtual void HandleEndUpdateStream(HostMessage* msg) = 0;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_CHROMOTING_VIEW_H_
diff --git a/remoting/client/client_util.cc b/remoting/client/client_util.cc
new file mode 100644
index 0000000..66906f8
--- /dev/null
+++ b/remoting/client/client_util.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/client/client_util.h"
+
+#include <iostream>
+
+static void SetConsoleEcho(bool on) {
+#ifdef WIN32
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
+ return;
+
+ DWORD mode;
+ if (!GetConsoleMode(hIn, &mode))
+ return;
+
+ if (on) {
+ mode = mode | ENABLE_ECHO_INPUT;
+ } else {
+ mode = mode & ~ENABLE_ECHO_INPUT;
+ }
+
+ SetConsoleMode(hIn, mode);
+#else
+ if (on)
+ system("stty echo");
+ else
+ system("stty -echo");
+#endif
+}
+
+namespace remoting {
+
+// Get host JID from command line arguments, or stdin if not specified.
+bool GetLoginInfo(std::string& host_jid,
+ std::string& username,
+ std::string& password) {
+ std::cout << "Host JID: ";
+ std::cin >> host_jid;
+ std::cin.ignore(); // Consume the leftover '\n'
+
+ if (host_jid.find("/chromoting") == std::string::npos) {
+ std::cerr << "Error: Expected Host JID in format: <jid>/chromoting<id>"
+ << std::endl;
+ return false;
+ }
+
+ // Get username (JID).
+ // Extract default JID from host_jid.
+ std::string default_username;
+ size_t jid_end = host_jid.find('/');
+ if (jid_end != std::string::npos) {
+ default_username = host_jid.substr(0, jid_end);
+ }
+ std::cout << "JID [" << default_username << "]: ";
+ getline(std::cin, username);
+ if (username.length() == 0) {
+ username = default_username;
+ }
+ if (username.length() == 0) {
+ std::cerr << "Error: Expected valid JID username" << std::endl;
+ return 1;
+ }
+
+ // Get password (with console echo turned off).
+ SetConsoleEcho(false);
+ std::cout << "Password: ";
+ getline(std::cin, password);
+ SetConsoleEcho(true);
+ std::cout << std::endl;
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/client/client_util.h b/remoting/client/client_util.h
new file mode 100644
index 0000000..4c2902e
--- /dev/null
+++ b/remoting/client/client_util.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_CLIENT_UTIL_H_
+#define REMOTING_CLIENT_CLIENT_UTIL_H_
+
+#include <string>
+
+namespace remoting {
+
+// Get the login info from the console and writes into |host_jid|, |username|
+// and |password|. Return true if successful.
+bool GetLoginInfo(std::string& host_jid, std::string& username,
+ std::string& password);
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_CLIENT_UTIL_H_
diff --git a/remoting/client/decoder.h b/remoting/client/decoder.h
index d39be07..ac2fe4d 100644
--- a/remoting/client/decoder.h
+++ b/remoting/client/decoder.h
@@ -7,7 +7,7 @@
#include <vector>
-#include "base/callback.h"
+#include "base/task.h"
#include "base/scoped_ptr.h"
#include "gfx/rect.h"
#include "media/base/video_frame.h"
@@ -15,12 +15,15 @@
namespace remoting {
+// TODO(hclam): Merge this with the one in remoting/host/encoder.h.
+typedef std::vector<gfx::Rect> UpdatedRects;
+
// Defines the behavior of a decoder for decoding images received from the
// host.
//
// Sequence of actions with a decoder is as follows:
//
-// 1. BeginDecode(VideoFrame)
+// 1. BeginDecode(PartialDecodeDone, DecodeDone, VideoFrame)
// 2. PartialDecode(HostMessage)
// ...
// 3. EndDecode()
@@ -34,27 +37,22 @@ namespace remoting {
// decoder (most likely the renderer) and the decoder.
class Decoder {
public:
- typedef std::vector<gfx::Rect> UpdatedRects;
- typedef Callback2<scoped_refptr<media::VideoFrame>, UpdatedRects>::Type
- PartialDecodeDoneCallback;
- typedef Callback1<scoped_refptr<media::VideoFrame> >::Type
- DecodeDoneCallback;
-
- Decoder(PartialDecodeDoneCallback* partial_decode_done_callback,
- DecodeDoneCallback* decode_done_callback)
- : partial_decode_done_(partial_decode_done_callback),
- decode_done_(decode_done_callback) {
- }
virtual ~Decoder() {
}
// Tell the decoder to use |frame| as a target to write the decoded image
// for the coming update stream.
+ // If decode is partially done and |frame| can be read, |partial_decode_done|
+ // is called and |update_rects| contains the updated regions.
+ // If decode is completed |decode_done| is called.
// Return true if the decoder can writes output to |frame| and accept
// the codec format.
// TODO(hclam): Provide more information when calling this function.
- virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame) = 0;
+ virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* updated_rects,
+ Task* partial_decode_done,
+ Task* decode_done) = 0;
// Give a HostMessage that contains the update stream packet that contains
// the encoded data to the decoder.
@@ -62,7 +60,7 @@ class Decoder {
// If the decoder has written something into |frame|,
// |partial_decode_done_| is called with |frame| and updated regions.
// Return true if the decoder can accept |message| and decode it.
- virtual bool PartialDecode(chromotocol_pb::HostMessage* message) = 0;
+ virtual bool PartialDecode(HostMessage* message) = 0;
// Notify the decoder that we have received the last update stream packet.
// If the decoding of the update stream has completed |decode_done_| is
@@ -70,21 +68,6 @@ class Decoder {
// If the update stream is not received fully and this method is called the
// decoder should also call |decode_done_| as soon as possible.
virtual void EndDecode() = 0;
-
- protected:
- PartialDecodeDoneCallback* partial_decode_done() {
- return partial_decode_done_.get();
- }
-
- DecodeDoneCallback* decode_done() {
- return decode_done_.get();
- }
-
- private:
- scoped_ptr<PartialDecodeDoneCallback> partial_decode_done_;
- scoped_ptr<DecodeDoneCallback> decode_done_;
-
- DISALLOW_COPY_AND_ASSIGN(Decoder);
};
} // namespace remoting
diff --git a/remoting/client/decoder_verbatim.cc b/remoting/client/decoder_verbatim.cc
index 7cbf428..8fb54ac 100644
--- a/remoting/client/decoder_verbatim.cc
+++ b/remoting/client/decoder_verbatim.cc
@@ -6,62 +6,80 @@
namespace remoting {
-bool DecoderVerbatim::BeginDecode(scoped_refptr<media::VideoFrame> frame) {
- // TODO(hclam): Check if we can accept the codec.
+bool DecoderVerbatim::BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* updated_rects,
+ Task* partial_decode_done,
+ Task* decode_done) {
+ DCHECK(!partial_decode_done_.get());
+ DCHECK(!decode_done_.get());
+ DCHECK(!updated_rects_);
+
+ partial_decode_done_.reset(partial_decode_done);
+ decode_done_.reset(decode_done);
+ updated_rects_ = updated_rects;
+
+ // TODO(hclam): Check if we can accept the color format of the video frame and
+ // the codec.
frame_ = frame;
return true;
}
-bool DecoderVerbatim::PartialDecode(chromotocol_pb::HostMessage* message) {
- scoped_ptr<chromotocol_pb::HostMessage> msg_deleter(message);
+bool DecoderVerbatim::PartialDecode(HostMessage* message) {
+ scoped_ptr<HostMessage> msg_deleter(message);
- // TODO(hclam): Support YUV.
- if (static_cast<int>(message->update_stream_packet().header().pixel_format())
- != static_cast<int>(frame_->format())) {
- return false;
- }
int width = message->update_stream_packet().header().width();
int height = message->update_stream_packet().header().height();
int x = message->update_stream_packet().header().x();
int y = message->update_stream_packet().header().y();
- chromotocol_pb::PixelFormat pixel_format =
+ PixelFormat pixel_format =
message->update_stream_packet().header().pixel_format();
int bytes_per_pixel = 0;
// TODO(hclam): Extract the following to an util function.
- if (pixel_format == chromotocol_pb::PixelFormatRgb24) {
+ if (pixel_format == PixelFormatRgb24) {
bytes_per_pixel = 3;
- } else if (pixel_format == chromotocol_pb::PixelFormatRgb565) {
+ } else if (pixel_format == PixelFormatRgb565) {
bytes_per_pixel = 2;
- } else if (pixel_format == chromotocol_pb::PixelFormatRgb32) {
+ } else if (pixel_format == PixelFormatRgb32) {
bytes_per_pixel = 4;
- } else if (pixel_format != chromotocol_pb::PixelFormatAscii) {
+ } else if (pixel_format != PixelFormatAscii) {
bytes_per_pixel = 1;
} else {
NOTREACHED() << "Pixel format not supported";
}
+ if (static_cast<PixelFormat>(frame_->format()) != pixel_format) {
+ NOTREACHED() << "Pixel format of message doesn't match the video frame. "
+ "Expected vs received = "
+ << frame_->format() << " vs " << pixel_format
+ << " Color space conversion required.";
+ }
+
// Copy the data line by line.
const int src_stride = bytes_per_pixel * width;
const char* src = message->update_stream_packet().data().c_str();
const int dest_stride = frame_->stride(media::VideoFrame::kRGBPlane);
uint8* dest = frame_->data(media::VideoFrame::kRGBPlane) +
- dest_stride * y + bytes_per_pixel * x;
+ dest_stride * y + bytes_per_pixel * x;
for (int i = 0; i < height; ++i) {
memcpy(dest, src, src_stride);
dest += dest_stride;
src += src_stride;
}
- UpdatedRects rects;
- rects.push_back(gfx::Rect(x, y, width, height));
- partial_decode_done()->Run(frame_, rects);
+ updated_rects_->clear();
+ updated_rects_->push_back(gfx::Rect(x, y, width, height));
+ partial_decode_done_->Run();
return true;
}
void DecoderVerbatim::EndDecode() {
- decode_done()->Run(frame_);
+ decode_done_->Run();
+
+ partial_decode_done_.reset();
+ decode_done_.reset();
frame_ = NULL;
+ updated_rects_ = NULL;
}
} // namespace remoting
diff --git a/remoting/client/decoder_verbatim.h b/remoting/client/decoder_verbatim.h
index 94d2f81..5d18a48 100644
--- a/remoting/client/decoder_verbatim.h
+++ b/remoting/client/decoder_verbatim.h
@@ -11,19 +11,25 @@ namespace remoting {
class DecoderVerbatim : public Decoder {
public:
- DecoderVerbatim(PartialDecodeDoneCallback* partial_decode_done_callback,
- DecodeDoneCallback* decode_done_callback)
- : Decoder(partial_decode_done_callback, decode_done_callback) {
+ DecoderVerbatim() {
}
// Decoder implementations.
- virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame);
- virtual bool PartialDecode(chromotocol_pb::HostMessage* message);
+ virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* update_rects,
+ Task* partial_decode_done,
+ Task* decode_done);
+ virtual bool PartialDecode(HostMessage* message);
virtual void EndDecode();
private:
+ // Tasks to call when decode is done.
+ scoped_ptr<Task> partial_decode_done_;
+ scoped_ptr<Task> decode_done_;
+
// The video frame to write to.
scoped_refptr<media::VideoFrame> frame_;
+ UpdatedRects* updated_rects_;
DISALLOW_COPY_AND_ASSIGN(DecoderVerbatim);
};
diff --git a/remoting/client/host_connection.cc b/remoting/client/host_connection.cc
index 00c542e..cc73b73 100644
--- a/remoting/client/host_connection.cc
+++ b/remoting/client/host_connection.cc
@@ -26,22 +26,28 @@ void HostConnection::Connect(const std::string& username,
}
void HostConnection::Disconnect() {
- if (jingle_channel_.get())
+ // TODO(hclam): It's not thread safe to read the state.
+ if (jingle_channel_.get() &&
+ jingle_channel_->state() != JingleChannel::CLOSED) {
jingle_channel_->Close();
+ }
- if (jingle_client_.get())
+ // TODO(hclam): It's not thread safe to read the state.
+ if (jingle_client_.get() &&
+ jingle_client_->state() != JingleClient::CLOSED) {
jingle_client_->Close();
+ }
}
void HostConnection::OnStateChange(JingleChannel* channel,
JingleChannel::State state) {
DCHECK(handler_);
- if (state == JingleChannel::FAILED)
+ if (state == JingleChannel::OPEN)
+ handler_->OnConnectionOpened(this);
+ else if (state == JingleChannel::FAILED)
handler_->OnConnectionFailed(this);
else if (state == JingleChannel::CLOSED)
handler_->OnConnectionClosed(this);
- else if (state == JingleChannel::OPEN)
- handler_->OnConnectionOpened(this);
}
void HostConnection::OnPacketReceived(JingleChannel* channel,
diff --git a/remoting/client/simple_client.cc b/remoting/client/simple_client.cc
index da65b2a..474f02f 100644
--- a/remoting/client/simple_client.cc
+++ b/remoting/client/simple_client.cc
@@ -11,47 +11,20 @@
#include "base/at_exit.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
-#include "media/base/data_buffer.h"
#include "remoting/base/protocol_decoder.h"
+#include "remoting/client/client_util.h"
#include "remoting/client/host_connection.h"
-#include "remoting/jingle_glue/jingle_channel.h"
-#include "remoting/jingle_glue/jingle_client.h"
-
-using chromotocol_pb::HostMessage;
-using chromotocol_pb::InitClientMessage;
-using chromotocol_pb::BeginUpdateStreamMessage;
-using chromotocol_pb::EndUpdateStreamMessage;
-using chromotocol_pb::UpdateStreamPacketMessage;
+
+using remoting::BeginUpdateStreamMessage;
+using remoting::EndUpdateStreamMessage;
using remoting::HostConnection;
+using remoting::HostMessage;
using remoting::HostMessageList;
+using remoting::InitClientMessage;
using remoting::JingleClient;
using remoting::JingleChannel;
using remoting::ProtocolDecoder;
-
-void SetConsoleEcho(bool on) {
-#ifdef WIN32
- HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
- if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
- return;
-
- DWORD mode;
- if (!GetConsoleMode(hIn, &mode))
- return;
-
- if (on) {
- mode = mode | ENABLE_ECHO_INPUT;
- } else {
- mode = mode & ~ENABLE_ECHO_INPUT;
- }
-
- SetConsoleMode(hIn, mode);
-#else
- if (on)
- system("stty echo");
- else
- system("stty -echo");
-#endif
-}
+using remoting::UpdateStreamPacketMessage;
class SimpleHostEventHandler : public HostConnection::EventHandler {
public:
@@ -137,50 +110,13 @@ class SimpleHostEventHandler : public HostConnection::EventHandler {
int main(int argc, char** argv) {
base::AtExitManager exit_manager;
+ std::string host_jid, username, auth_token;
- if (argc > 2) {
- std::cerr << "Usage: " << argv[0] << " [<host_jid>]" << std::endl;
- return 1;
- }
-
- // Get host JID from command line arguments, or stdin if not specified.
- std::string host_jid;
- if (argc == 2) {
- host_jid = argv[1];
- } else {
- std::cout << "Host JID: ";
- std::cin >> host_jid;
- std::cin.ignore(); // Consume the leftover '\n'
- }
- if (host_jid.find("/chromoting") == std::string::npos) {
- std::cerr << "Error: Expected Host JID in format: <jid>/chromoting<id>"
- << std::endl;
+ if (remoting::GetLoginInfo(host_jid, username, auth_token)) {
+ std::cerr << "Cannot get valid login info." << std::endl;
return 1;
}
- // Get username (JID).
- // Extract default JID from host_jid.
- std::string username;
- std::string default_username;
- size_t jid_end = host_jid.find('/');
- if (jid_end != std::string::npos) {
- default_username = host_jid.substr(0, jid_end);
- }
- std::cout << "JID [" << default_username << "]: ";
- getline(std::cin, username);
- if (username.length() == 0) {
- username = default_username;
- }
- if (username.length() == 0) {
- std::cerr << "Error: Expected valid JID username" << std::endl;
- return 1;
- }
-
- // Get auth token.
- std::string auth_token;
- std::cout << "Auth Token: ";
- getline(std::cin, auth_token);
-
// The message loop that everything runs on.
MessageLoop main_loop;
SimpleHostEventHandler handler(&main_loop);
diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc
new file mode 100644
index 0000000..6b8e4e2
--- /dev/null
+++ b/remoting/client/x11_client.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file implements a X11 chromoting client.
+
+#include <iostream>
+
+#include "base/at_exit.h"
+#include "base/message_loop.h"
+#include "base/stl_util-inl.h"
+#include "remoting/client/host_connection.h"
+#include "remoting/client/client_util.h"
+
+// Include Xlib at the end because it clashes with ClientMessage defined in
+// the protocol buffer.
+// x11_view.h also includes Xlib.h so put it behind all other headers but
+// before Xlib.h
+#include "remoting/client/x11_view.h"
+#include <X11/Xlib.h>
+
+namespace remoting {
+
+class X11Client : public base::RefCountedThreadSafe<X11Client>,
+ public HostConnection::EventHandler {
+ public:
+ X11Client(std::string host_jid, std::string username, std::string auth_token)
+ : display_(NULL),
+ window_(0),
+ width_(0),
+ height_(0),
+ host_jid_(host_jid),
+ username_(username),
+ auth_token_(auth_token) {
+ }
+
+ virtual ~X11Client() {
+ DCHECK(!display_);
+ DCHECK(!window_);
+ }
+
+ // Starts the remoting client and the message loop. Returns only after
+ // the message loop has terminated.
+ void Run() {
+ message_loop_.PostTask(FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoInitX11));
+ message_loop_.PostTask(FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoInitConnection));
+ message_loop_.Run();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HostConnection::EventHandler implementations.
+ virtual void HandleMessages(HostConnection* conn,
+ remoting::HostMessageList* messages) {
+ for (size_t i = 0; i < messages->size(); ++i) {
+ HostMessage* msg = (*messages)[i];
+ if (msg->has_init_client()) {
+ message_loop_.PostTask(
+ FROM_HERE, NewRunnableMethod(this, &X11Client::DoInitClient, msg));
+ } else if (msg->has_begin_update_stream()) {
+ message_loop_.PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoBeginUpdate, msg));
+ } else if (msg->has_update_stream_packet()) {
+ message_loop_.PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoHandleUpdate, msg));
+ } else if (msg->has_end_update_stream()) {
+ message_loop_.PostTask(
+ FROM_HERE, NewRunnableMethod(this, &X11Client::DoEndUpdate, msg));
+ } else {
+ NOTREACHED() << "Unknown message received";
+ }
+ }
+ // Assume we have processed all the messages.
+ messages->clear();
+ }
+
+ virtual void OnConnectionOpened(HostConnection* conn) {
+ std::cout << "Connection establised." << std::endl;
+ }
+
+ virtual void OnConnectionClosed(HostConnection* conn) {
+ std::cout << "Connection closed." << std::endl;
+ Exit();
+ }
+
+ virtual void OnConnectionFailed(HostConnection* conn) {
+ std::cout << "Conection failed." << std::endl;
+ Exit();
+ }
+
+ private:
+ void DoInitX11() {
+ display_ = XOpenDisplay(NULL);
+ if (!display_) {
+ std::cout << "Error - cannot open display" << std::endl;
+ Exit();
+ }
+
+ // Get properties of the screen.
+ int screen = DefaultScreen(display_);
+ int root_window = RootWindow(display_, screen);
+
+ // Creates the window.
+ window_ = XCreateSimpleWindow(display_, root_window, 1, 1, 640, 480, 0,
+ BlackPixel(display_, screen),
+ BlackPixel(display_, screen));
+ DCHECK(window_);
+ XStoreName(display_, window_, "X11 Remoting");
+
+ // Specifies what kind of messages we want to receive.
+ XSelectInput(display_, window_, ExposureMask | ButtonPressMask);
+ XMapWindow(display_, window_);
+ }
+
+ void DoInitConnection() {
+ // If the initialization of X11 has failed then return directly.
+ if (!display_)
+ return;
+
+ // Creates a HostConnection object and connection to the host.
+ LOG(INFO) << "Connecting...";
+ connection_.reset(new HostConnection(new ProtocolDecoder(), this));
+ connection_->Connect(username_, auth_token_, host_jid_);
+ }
+
+ void DoDestroyX11() {
+ if (display_ && window_) {
+ // Shutdown the window system.
+ XDestroyWindow(display_, window_);
+ XCloseDisplay(display_);
+ display_ = NULL;
+ window_ = 0;
+ }
+ }
+
+ void DoDisconnect() {
+ if (connection_.get())
+ connection_->Disconnect();
+ }
+
+ void Exit() {
+ // Disconnect the jingle channel and client.
+ message_loop_.PostTask(FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoDisconnect));
+
+ // Post a task to shutdown X11.
+ message_loop_.PostTask(FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoDestroyX11));
+
+ // Quit the current message loop.
+ message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ }
+
+ // This method is executed on the main loop.
+ void DoInitClient(HostMessage* msg) {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ DCHECK(msg->has_init_client());
+ scoped_ptr<HostMessage> deleter(msg);
+
+ // Saves the dimension and resize the window.
+ width_ = msg->init_client().width();
+ height_ = msg->init_client().height();
+ LOG(INFO) << "Init client receievd: " << width_ << "x" << height_;
+ XResizeWindow(display_, window_, width_, height_);
+
+ // Now construct the X11View that renders the remote desktop.
+ view_ = new X11View(display_, window_, width_, height_);
+
+ // Schedule the event handler to process the event queue of X11.
+ ScheduleX11EventHandler();
+ }
+
+ // The following methods are executed on the same thread as libjingle.
+ void DoBeginUpdate(HostMessage* msg) {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ DCHECK(msg->has_begin_update_stream());
+
+ view_->HandleBeginUpdateStream(msg);
+ }
+
+ void DoHandleUpdate(HostMessage* msg) {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ DCHECK(msg->has_update_stream_packet());
+
+ view_->HandleUpdateStreamPacket(msg);
+ }
+
+ void DoEndUpdate(HostMessage* msg) {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ DCHECK(msg->has_end_update_stream());
+
+ view_->HandleEndUpdateStream(msg);
+ }
+
+ void DoProcessX11Events() {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ if (XPending(display_)) {
+ XEvent e;
+ XNextEvent(display_, &e);
+ if (e.type == Expose) {
+ // Tell the ChromotingView to paint again.
+ view_->Paint();
+ } else if (e.type == ButtonPress) {
+ // TODO(hclam): Implement.
+ NOTIMPLEMENTED();
+ } else {
+ // TODO(hclam): Implement.
+ NOTIMPLEMENTED();
+ }
+ }
+
+ // Schedule the next event handler.
+ ScheduleX11EventHandler();
+ }
+
+ void ScheduleX11EventHandler() {
+ // Schedule a delayed task to process X11 events in 10ms.
+ static const int kProcessEventsInterval = 10;
+ message_loop_.PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &X11Client::DoProcessX11Events),
+ kProcessEventsInterval);
+ }
+
+ // Members used for display.
+ Display* display_;
+ Window window_;
+
+ // Dimension of the window. They are initialized when InitClient message is
+ // received.
+ int width_;
+ int height_;
+
+ std::string host_jid_;
+ std::string username_;
+ std::string auth_token_;
+ scoped_ptr<HostConnection> connection_;
+ scoped_refptr<ChromotingView> view_;
+
+ MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(X11Client);
+};
+
+} // namespace remoting
+
+int main() {
+ base::AtExitManager at_exit;
+ std::string host_jid, username, auth_token;
+
+ if (!remoting::GetLoginInfo(host_jid, username, auth_token)) {
+ std::cout << "Cannot obtain login info" << std::endl;
+ return 1;
+ }
+
+ scoped_refptr<remoting::X11Client> client =
+ new remoting::X11Client(host_jid, username, auth_token);
+ client->Run();
+}
diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc
new file mode 100644
index 0000000..bdb54d4
--- /dev/null
+++ b/remoting/client/x11_view.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/client/x11_view.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/Xcomposite.h>
+#include "remoting/client/decoder_verbatim.h"
+
+namespace remoting {
+
+X11View::X11View(Display* display, int window, int width, int height)
+ : display_(display),
+ window_(window),
+ picture_(0),
+ width_(width),
+ height_(height) {
+}
+
+X11View::~X11View() {
+}
+
+void X11View::Paint() {
+ // TODO(hclam): Paint only the updated regions.
+ all_update_rects_.clear();
+
+ // If we have not initialized the render target then do it now.
+ if (!frame_)
+ InitPaintTarget();
+
+ // Upload the image to a pixmap. And then creats a picture from the pixmap
+ // and composite the picture over the picture represending the window.
+
+ // Creates a XImage.
+ XImage image;
+ memset(&image, 0, sizeof(image));
+ image.width = width_;
+ image.height = height_;
+ image.depth = 32;
+ image.bits_per_pixel = 32;
+ image.format = ZPixmap;
+ image.byte_order = LSBFirst;
+ image.bitmap_unit = 8;
+ image.bitmap_bit_order = LSBFirst;
+ image.bytes_per_line = frame_->stride(media::VideoFrame::kRGBPlane);
+ image.red_mask = 0xff;
+ image.green_mask = 0xff00;
+ image.blue_mask = 0xff0000;
+ image.data = reinterpret_cast<char*>(
+ frame_->data(media::VideoFrame::kRGBPlane));
+
+ // Creates a pixmap and uploads from the XImage.
+ unsigned long pixmap = XCreatePixmap(display_, window_, width_, height_, 32);
+
+ GC gc = XCreateGC(display_, pixmap, 0, NULL);
+ XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0, width_, height_);
+ XFreeGC(display_, gc);
+
+ // Creates the picture representing the pixmap.
+ unsigned long picture = XRenderCreatePicture(
+ display_, pixmap,
+ XRenderFindStandardFormat(display_, PictStandardARGB32),
+ 0, NULL);
+
+ // Composite the picture over the picture representing the window.
+ XRenderComposite(display_, PictOpSrc, picture, 0,
+ picture_, 0, 0, 0, 0, 0, 0,
+ width_, height_);
+
+ XRenderFreePicture(display_, picture);
+ XFreePixmap(display_, pixmap);
+}
+
+void X11View::InitPaintTarget() {
+ // Testing XRender support.
+ int dummy;
+ bool xrender_support = XRenderQueryExtension(display_, &dummy, &dummy);
+ CHECK(xrender_support) << "XRender is not supported!";
+
+ XWindowAttributes attr;
+ XGetWindowAttributes(display_, window_, &attr);
+
+ XRenderPictFormat* pictformat = XRenderFindVisualFormat(
+ display_,
+ attr.visual);
+ CHECK(pictformat) << "XRENDER does not support default visual";
+
+ picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL);
+ CHECK(picture_) << "Backing picture not created";
+
+ // Create the video frame to carry the decoded image.
+ media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, width_, height_,
+ base::TimeDelta(), base::TimeDelta(), &frame_);
+ DCHECK(frame_);
+}
+
+void X11View::HandleBeginUpdateStream(HostMessage* msg) {
+ scoped_ptr<HostMessage> deleter(msg);
+
+ // TODO(hclam): Use the information from the message to create the decoder.
+ // We lazily construct the decoder.
+ if (!decoder_.get()) {
+ decoder_.reset(new DecoderVerbatim());
+ }
+
+ // Tell the decoder to do start decoding.
+ decoder_->BeginDecode(frame_, &update_rects_,
+ NewRunnableMethod(this, &X11View::OnPartialDecodeDone),
+ NewRunnableMethod(this, &X11View::OnDecodeDone));
+}
+
+void X11View::HandleUpdateStreamPacket(HostMessage* msg) {
+ decoder_->PartialDecode(msg);
+}
+
+void X11View::HandleEndUpdateStream(HostMessage* msg) {
+ scoped_ptr<HostMessage> deleter(msg);
+ decoder_->EndDecode();
+}
+
+void X11View::OnPartialDecodeDone() {
+ // Decoder has produced some output so schedule a paint. We'll get a Paint()
+ // call in the short future. Note that we can get UpdateStreamPacket during
+ // this short period of time and we will perform decode again and the
+ // information of updated rects will be lost.
+ // There are several ways to solve this problem.
+ // 1. Merge the updated rects and perform one paint.
+ // 2. Queue the updated rects and perform two paints.
+ // 3. Ignore the updated rects and always paint the full image. Since we
+ // use one frame as output this will always be correct.
+ // We will take (1) and simply concat the list of rectangles.
+ all_update_rects_.insert(all_update_rects_.begin() +
+ all_update_rects_.size(),
+ update_rects_.begin(), update_rects_.end());
+
+ // TODO(hclam): Make sure we call this method on the right thread. Since
+ // decoder is single-threaded we don't have a problem but we better post
+ // a task to do the right thing.
+ XEvent event;
+ event.type = Expose;
+ XSendEvent(display_, static_cast<int>(window_), true, ExposureMask, &event);
+}
+
+void X11View::OnDecodeDone() {
+ // Since we do synchronous decoding here there's nothing in this method.
+}
+
+} // namespace remoting
diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h
new file mode 100644
index 0000000..1959e18
--- /dev/null
+++ b/remoting/client/x11_view.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_X11_VIEW_H_
+#define REMOTING_CLIENT_X11_VIEW_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "media/base/video_frame.h"
+#include "remoting/client/decoder.h"
+#include "remoting/client/chromoting_view.h"
+
+typedef struct _XDisplay Display;
+
+namespace remoting {
+
+// A ChromotingView implemented using X11 and XRender.
+class X11View : public ChromotingView {
+ public:
+ X11View(Display* display, int window, int width, int height);
+
+ virtual ~X11View();
+
+ // ChromotingView implementations.
+ virtual void Paint();
+ virtual void HandleBeginUpdateStream(HostMessage* msg);
+ virtual void HandleUpdateStreamPacket(HostMessage* msg);
+ virtual void HandleEndUpdateStream(HostMessage* msg) ;
+
+ private:
+ void InitPaintTarget();
+ void OnPartialDecodeDone();
+ void OnDecodeDone();
+
+ Display* display_;
+ int window_;
+ int width_;
+ int height_;
+
+ // A picture created in the X server that represents drawing area of the
+ // window.
+ int picture_;
+
+ scoped_refptr<media::VideoFrame> frame_;
+ UpdatedRects update_rects_;
+ UpdatedRects all_update_rects_;
+
+ scoped_ptr<Decoder> decoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(X11View);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_X11_VIEW_H_
diff --git a/remoting/host/capturer.cc b/remoting/host/capturer.cc
index 2f92d18..789723b 100644
--- a/remoting/host/capturer.cc
+++ b/remoting/host/capturer.cc
@@ -9,7 +9,7 @@ namespace remoting {
Capturer::Capturer()
: width_(0),
height_(0),
- pixel_format_(chromotocol_pb::PixelFormatInvalid),
+ pixel_format_(PixelFormatInvalid),
bytes_per_pixel_(0),
bytes_per_row_(0),
current_buffer_(0) {
@@ -30,7 +30,7 @@ int Capturer::GetHeight() const {
return height_;
}
-chromotocol_pb::PixelFormat Capturer::GetPixelFormat() const {
+PixelFormat Capturer::GetPixelFormat() const {
return pixel_format_;
}
diff --git a/remoting/host/capturer.h b/remoting/host/capturer.h
index 64e25f2..6007b350 100644
--- a/remoting/host/capturer.h
+++ b/remoting/host/capturer.h
@@ -77,7 +77,7 @@ class Capturer {
virtual int GetHeight() const;
// Get the pixel format of the image captured.
- virtual chromotocol_pb::PixelFormat GetPixelFormat() const;
+ virtual PixelFormat GetPixelFormat() const;
// Invalidate the specified screen rect.
virtual void InvalidateRect(gfx::Rect dirty);
@@ -100,7 +100,7 @@ class Capturer {
int height_;
// Format of pixels returned in buffer.
- chromotocol_pb::PixelFormat pixel_format_;
+ PixelFormat pixel_format_;
// Information about screen.
int bytes_per_pixel_;
diff --git a/remoting/host/capturer_fake.cc b/remoting/host/capturer_fake.cc
index 5c087b39..f1a8135 100644
--- a/remoting/host/capturer_fake.cc
+++ b/remoting/host/capturer_fake.cc
@@ -8,9 +8,9 @@
namespace remoting {
-static const int kWidth = 640;
-static const int kHeight = 480;
-static const int kBytesPerPixel = 3; // 24 bit RGB is 3 bytes per pixel.
+static const int kWidth = 320;
+static const int kHeight = 240;
+static const int kBytesPerPixel = 4; // 32 bit RGB is 4 bytes per pixel.
static const int kMaxColorChannelValue = 255;
CapturerFake::CapturerFake()
@@ -18,7 +18,7 @@ CapturerFake::CapturerFake()
// Dimensions of screen.
width_ = kWidth;
height_ = kHeight;
- pixel_format_ = chromotocol_pb::PixelFormatRgb24;
+ pixel_format_ = PixelFormatRgb32;
bytes_per_pixel_ = kBytesPerPixel;
bytes_per_row_ = width_ * bytes_per_pixel_;
@@ -75,12 +75,14 @@ void CapturerFake::GetDataStride(int strides[]) const {
void CapturerFake::GenerateImage() {
uint8* row = buffers_[current_buffer_].get();
for (int y = 0; y < height_; ++y) {
+ int offset = y % 3;
for (int x = 0; x < width_; ++x) {
- row[x] = seed_++;
+ row[x * kBytesPerPixel + offset] = seed_++;
seed_ &= kMaxColorChannelValue;
}
row += bytes_per_row_;
}
+ ++seed_;
}
} // namespace remoting
diff --git a/remoting/host/capturer_fake_ascii.cc b/remoting/host/capturer_fake_ascii.cc
index 7ab7e6b..00f8529 100644
--- a/remoting/host/capturer_fake_ascii.cc
+++ b/remoting/host/capturer_fake_ascii.cc
@@ -16,7 +16,7 @@ CapturerFakeAscii::CapturerFakeAscii() {
// Dimensions of screen.
width_ = kWidth;
height_ = kHeight;
- pixel_format_ = chromotocol_pb::PixelFormatAscii;
+ pixel_format_ = PixelFormatAscii;
bytes_per_pixel_ = kBytesPerPixel;
bytes_per_row_ = width_ * bytes_per_pixel_;
diff --git a/remoting/host/capturer_gdi.cc b/remoting/host/capturer_gdi.cc
index 3b5a844..af32f9b 100644
--- a/remoting/host/capturer_gdi.cc
+++ b/remoting/host/capturer_gdi.cc
@@ -84,7 +84,7 @@ void CapturerGdi::InitializeBuffers() {
int rounded_width = (width_ + 3) & (~3);
// Dimensions of screen.
- pixel_format_ = chromotocol_pb::PixelFormatRgb24;
+ pixel_format_ = PixelFormatRgb24;
bytes_per_pixel_ = kBytesPerPixel;
bytes_per_row_ = rounded_width * bytes_per_pixel_;
diff --git a/remoting/host/client_connection.cc b/remoting/host/client_connection.cc
index 9ae7b95..5bf962c 100644
--- a/remoting/host/client_connection.cc
+++ b/remoting/host/client_connection.cc
@@ -40,7 +40,7 @@ void ClientConnection::SendInitClientMessage(int width, int height) {
DCHECK(!update_stream_size_);
DCHECK(channel_.get());
- chromotocol_pb::HostMessage msg;
+ HostMessage msg;
msg.mutable_init_client()->set_width(width);
msg.mutable_init_client()->set_height(height);
DCHECK(msg.IsInitialized());
@@ -51,7 +51,7 @@ void ClientConnection::SendBeginUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
DCHECK(channel_.get());
- chromotocol_pb::HostMessage msg;
+ HostMessage msg;
msg.mutable_begin_update_stream();
DCHECK(msg.IsInitialized());
@@ -62,12 +62,12 @@ void ClientConnection::SendBeginUpdateStreamMessage() {
}
void ClientConnection::SendUpdateStreamPacketMessage(
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<DataBuffer> data) {
DCHECK_EQ(loop_, MessageLoop::current());
DCHECK(channel_.get());
- chromotocol_pb::HostMessage msg;
+ HostMessage msg;
msg.mutable_update_stream_packet()->mutable_header()->CopyFrom(*header);
// TODO(hclam): This introduce one memory copy. Eliminate it.
msg.mutable_update_stream_packet()->set_data(
@@ -83,7 +83,7 @@ void ClientConnection::SendEndUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
DCHECK(channel_.get());
- chromotocol_pb::HostMessage msg;
+ HostMessage msg;
msg.mutable_end_update_stream();
DCHECK(msg.IsInitialized());
diff --git a/remoting/host/client_connection.h b/remoting/host/client_connection.h
index 13318c3..4b46ee3 100644
--- a/remoting/host/client_connection.h
+++ b/remoting/host/client_connection.h
@@ -78,7 +78,7 @@ class ClientConnection : public base::RefCountedThreadSafe<ClientConnection>,
// Send encoded update stream data to the viewer. The viewer
// should not take ownership of the data.
virtual void SendUpdateStreamPacketMessage(
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer> data);
// Notifies the viewer the update stream has ended.
diff --git a/remoting/host/client_connection_unittest.cc b/remoting/host/client_connection_unittest.cc
index 1256f25..d7be888 100644
--- a/remoting/host/client_connection_unittest.cc
+++ b/remoting/host/client_connection_unittest.cc
@@ -51,8 +51,7 @@ TEST_F(ClientConnectionTest, SendUpdateStream) {
// Then send the actual data.
EXPECT_CALL(*channel_, Write(_));
- chromotocol_pb::UpdateStreamPacketHeader* header
- = new chromotocol_pb::UpdateStreamPacketHeader();
+ UpdateStreamPacketHeader* header = new UpdateStreamPacketHeader();
header->set_x(0);
header->set_y(0);
header->set_width(640);
diff --git a/remoting/host/encoder.h b/remoting/host/encoder.h
index e3af66b..53ddb63 100644
--- a/remoting/host/encoder.h
+++ b/remoting/host/encoder.h
@@ -44,7 +44,7 @@ class Encoder {
const uint8** input_data,
const int* strides,
bool key_frame,
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer>* output_data,
bool* encode_done,
Task* data_available_task) = 0;
@@ -55,7 +55,7 @@ class Encoder {
// Set the pixel format of the incoming images. Need to call this before
// calling Encode().
- virtual void SetPixelFormat(chromotocol_pb::PixelFormat pixel_format) = 0;
+ virtual void SetPixelFormat(PixelFormat pixel_format) = 0;
};
} // namespace remoting
diff --git a/remoting/host/encoder_verbatim.cc b/remoting/host/encoder_verbatim.cc
index 0ef7677..58525f1 100644
--- a/remoting/host/encoder_verbatim.cc
+++ b/remoting/host/encoder_verbatim.cc
@@ -10,17 +10,16 @@
namespace remoting {
-using chromotocol_pb::UpdateStreamPacketHeader;
using media::DataBuffer;
void EncoderVerbatim::Encode(const DirtyRects& dirty_rects,
- const uint8** input_data,
- const int* strides,
- bool key_frame,
- UpdateStreamPacketHeader* header,
- scoped_refptr<DataBuffer>* output_data,
- bool* encode_done,
- Task* data_available_task) {
+ const uint8** input_data,
+ const int* strides,
+ bool key_frame,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<DataBuffer>* output_data,
+ bool* encode_done,
+ Task* data_available_task) {
int num_rects = dirty_rects.size();
for (int i = 0; i < num_rects; i++) {
if (EncodeRect(dirty_rects[i], input_data, strides, header, output_data)) {
@@ -37,26 +36,28 @@ void EncoderVerbatim::SetSize(int width, int height) {
height_ = height;
}
-void EncoderVerbatim::SetPixelFormat(chromotocol_pb::PixelFormat pixel_format) {
+void EncoderVerbatim::SetPixelFormat(PixelFormat pixel_format) {
// These are sorted so that the most common formats are checked first.
- if (pixel_format == chromotocol_pb::PixelFormatRgb24) {
+ // TODO(hclam): Extract this into a util function.
+ if (pixel_format == PixelFormatRgb24) {
bytes_per_pixel_ = 3;
- } else if (pixel_format == chromotocol_pb::PixelFormatRgb565) {
+ } else if (pixel_format == PixelFormatRgb565) {
bytes_per_pixel_ = 2;
- } else if (pixel_format == chromotocol_pb::PixelFormatRgb32) {
+ } else if (pixel_format == PixelFormatRgb32) {
bytes_per_pixel_ = 4;
- } else if (pixel_format != chromotocol_pb::PixelFormatAscii) {
+ } else if (pixel_format != PixelFormatAscii) {
bytes_per_pixel_ = 1;
} else {
NOTREACHED() << "Pixel format not supported";
}
+ pixel_format_ = pixel_format;
}
bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty,
- const uint8** input_data,
- const int* strides,
- UpdateStreamPacketHeader* header,
- scoped_refptr<DataBuffer>* output_data) {
+ const uint8** input_data,
+ const int* strides,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<DataBuffer>* output_data) {
const int kPlanes = 3;
// Calculate the size of output.
@@ -70,7 +71,8 @@ bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty,
header->set_y(dirty.y());
header->set_width(dirty.width());
header->set_height(dirty.height());
- header->set_encoding(chromotocol_pb::EncodingNone);
+ header->set_encoding(EncodingNone);
+ header->set_pixel_format(pixel_format_);
*output_data = new DataBuffer(output_size);
(*output_data)->SetDataSize(output_size);
diff --git a/remoting/host/encoder_verbatim.h b/remoting/host/encoder_verbatim.h
index 2b5e39d..d7a1760 100644
--- a/remoting/host/encoder_verbatim.h
+++ b/remoting/host/encoder_verbatim.h
@@ -21,12 +21,12 @@ class EncoderVerbatim : public Encoder {
const uint8** input_data,
const int* strides,
bool key_frame,
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer>* output_data,
bool* encode_done,
Task* data_available_task);
virtual void SetSize(int width, int height);
- virtual void SetPixelFormat(chromotocol_pb::PixelFormat pixel_format);
+ virtual void SetPixelFormat(PixelFormat pixel_format);
private:
// Encode a single dirty rect. Called by Encode().
@@ -34,12 +34,13 @@ class EncoderVerbatim : public Encoder {
bool EncodeRect(const gfx::Rect& dirty,
const uint8** input_data,
const int* strides,
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer>* output_data);
int width_;
int height_;
int bytes_per_pixel_;
+ PixelFormat pixel_format_;
};
} // namespace remoting
diff --git a/remoting/host/encoder_vp8.cc b/remoting/host/encoder_vp8.cc
index a1e45e7..231acd5 100644
--- a/remoting/host/encoder_vp8.cc
+++ b/remoting/host/encoder_vp8.cc
@@ -56,7 +56,7 @@ void EncoderVp8::Encode(const DirtyRects& dirty_rects,
const uint8** input_data,
const int* strides,
bool key_frame,
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer>* output_data,
bool* encode_done,
Task* data_available_task) {
diff --git a/remoting/host/encoder_vp8.h b/remoting/host/encoder_vp8.h
index e8c73b7..1cd16ac 100644
--- a/remoting/host/encoder_vp8.h
+++ b/remoting/host/encoder_vp8.h
@@ -32,7 +32,7 @@ class EncoderVp8 : public Encoder {
const uint8** input_data,
const int* strides,
bool key_frame,
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer>* output_data,
bool* encode_done,
Task* data_available_task);
diff --git a/remoting/host/encoder_vp8_unittest.cc b/remoting/host/encoder_vp8_unittest.cc
index 2dbc81b..0b29830 100644
--- a/remoting/host/encoder_vp8_unittest.cc
+++ b/remoting/host/encoder_vp8_unittest.cc
@@ -47,8 +47,7 @@ TEST(EncoderVp8Test, SimpleEncode) {
GenerateData(planes[2], kWidth * kHeight / 4);
scoped_refptr<EncodeDoneHandler> handler = new EncodeDoneHandler();
- chromotocol_pb::UpdateStreamPacketHeader* header
- = new chromotocol_pb::UpdateStreamPacketHeader();
+ UpdateStreamPacketHeader* header = new UpdateStreamPacketHeader();
scoped_refptr<media::DataBuffer> encoded_data;
bool encode_done = false;
EXPECT_CALL(*handler, EncodeDone());
diff --git a/remoting/host/event_executor_win.cc b/remoting/host/event_executor_win.cc
index cc7f8f8..de82a5a 100644
--- a/remoting/host/event_executor_win.cc
+++ b/remoting/host/event_executor_win.cc
@@ -355,7 +355,7 @@ EventExecutorWin::~EventExecutorWin() {
void EventExecutorWin::HandleInputEvents(ClientMessageList* messages) {
for (size_t i = 0; i < messages->size(); ++i) {
- chromotocol_pb::ClientMessage* msg = (*messages)[i];
+ ClientMessage* msg = (*messages)[i];
if (msg->has_mouse_set_position_event()) {
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
static_cast<int>((msg->mouse_set_position_event().x() * 65535)),
@@ -369,20 +369,20 @@ void EventExecutorWin::HandleInputEvents(ClientMessageList* messages) {
// TODO(hclam): Handle wheel events.
} else if (msg->has_mouse_down_event()) {
if (msg->mouse_down_event().button() ==
- chromotocol_pb::MouseDownEvent::LEFT) {
+ MouseDownEvent::LEFT) {
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
} else if (msg->mouse_down_event().button() ==
- chromotocol_pb::MouseDownEvent::RIGHT) {
+ MouseDownEvent::RIGHT) {
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
} else {
// TODO(hclam): Handle other buttons.
}
} else if (msg->has_mouse_up_event()) {
if (msg->mouse_up_event().button() ==
- chromotocol_pb::MouseUpEvent::LEFT) {
+ MouseUpEvent::LEFT) {
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
} else if (msg->mouse_up_event().button() ==
- chromotocol_pb::MouseUpEvent::RIGHT) {
+ MouseUpEvent::RIGHT) {
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
} else {
// TODO(hclam): Handle other buttons.
diff --git a/remoting/host/mock_objects.h b/remoting/host/mock_objects.h
index 1f33263..d492376 100644
--- a/remoting/host/mock_objects.h
+++ b/remoting/host/mock_objects.h
@@ -27,7 +27,7 @@ class MockCapturer : public Capturer {
MOCK_CONST_METHOD1(GetDirtyRects, void(DirtyRects* rects));
MOCK_CONST_METHOD0(GetWidth, int());
MOCK_CONST_METHOD0(GetHeight, int());
- MOCK_CONST_METHOD0(GetPixelFormat, chromotocol_pb::PixelFormat());
+ MOCK_CONST_METHOD0(GetPixelFormat, PixelFormat());
private:
DISALLOW_COPY_AND_ASSIGN(MockCapturer);
@@ -42,12 +42,12 @@ class MockEncoder : public Encoder {
const uint8** planes,
const int* strides,
bool key_frame,
- chromotocol_pb::UpdateStreamPacketHeader* output_data_header,
+ UpdateStreamPacketHeader* output_data_header,
scoped_refptr<media::DataBuffer>* output_data,
bool* encode_done,
Task* data_available_task));
MOCK_METHOD2(SetSize, void(int width, int height));
- MOCK_METHOD1(SetPixelFormat, void(chromotocol_pb::PixelFormat pixel_format));
+ MOCK_METHOD1(SetPixelFormat, void(PixelFormat pixel_format));
private:
DISALLOW_COPY_AND_ASSIGN(MockEncoder);
@@ -70,7 +70,7 @@ class MockClientConnection : public ClientConnection {
MOCK_METHOD2(SendInitClientMessage, void(int width, int height));
MOCK_METHOD0(SendBeginUpdateStreamMessage, void());
MOCK_METHOD2(SendUpdateStreamPacketMessage,
- void(chromotocol_pb::UpdateStreamPacketHeader* header,
+ void(UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer> data));
MOCK_METHOD0(SendEndUpdateStreamMessage, void());
MOCK_METHOD0(GetPendingUpdateStreamMessages, int());
diff --git a/remoting/host/session_manager.cc b/remoting/host/session_manager.cc
index da16a55..0160993 100644
--- a/remoting/host/session_manager.cc
+++ b/remoting/host/session_manager.cc
@@ -49,7 +49,7 @@ SessionManager::SessionManager(
rate_control_started_(false),
capture_width_(0),
capture_height_(0),
- capture_pixel_format_(chromotocol_pb::PixelFormatInvalid),
+ capture_pixel_format_(PixelFormatInvalid),
encode_stream_started_(false),
encode_done_(false) {
DCHECK(capture_loop_);
@@ -59,7 +59,6 @@ SessionManager::SessionManager(
SessionManager::~SessionManager() {
clients_.clear();
- DCHECK_EQ(0u, clients_.size());
}
void SessionManager::Start() {
@@ -132,9 +131,10 @@ void SessionManager::SetMaxRate(double rate) {
}
void SessionManager::AddClient(scoped_refptr<ClientConnection> client) {
- network_loop_->PostTask(
+ // Gets the init information for the client.
+ capture_loop_->PostTask(
FROM_HERE,
- NewRunnableMethod(this, &SessionManager::DoAddClient, client));
+ NewRunnableMethod(this, &SessionManager::DoGetInitInfo, client));
}
void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
@@ -214,7 +214,7 @@ void SessionManager::DoEncode() {
}
void SessionManager::DoSendUpdate(
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer> encoded_data,
bool begin_update, bool end_update) {
DCHECK_EQ(network_loop_, MessageLoop::current());
@@ -242,10 +242,18 @@ void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client,
void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
DCHECK_EQ(capture_loop_, MessageLoop::current());
+ // Sends the init message to the cleint.
network_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(this, &SessionManager::DoSendInit, client,
capturer_->GetWidth(), capturer_->GetHeight()));
+
+ // And then add the client to the list so it can receive update stream.
+ // It is important we do so in such order or the client will receive
+ // update stream before init message.
+ network_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SessionManager::DoAddClient, client));
}
void SessionManager::DoSetRate(double rate) {
@@ -279,11 +287,6 @@ void SessionManager::DoAddClient(scoped_refptr<ClientConnection> client) {
// TODO(hclam): Force a full frame for next encode.
clients_.push_back(client);
-
- // Gets the init information for the client.
- capture_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &SessionManager::DoGetInitInfo, client));
}
void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
diff --git a/remoting/host/session_manager.h b/remoting/host/session_manager.h
index 2a88020..677e3bd 100644
--- a/remoting/host/session_manager.h
+++ b/remoting/host/session_manager.h
@@ -104,7 +104,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
void DoFinishEncode();
void DoEncode();
void DoSendUpdate(
- chromotocol_pb::UpdateStreamPacketHeader* header,
+ UpdateStreamPacketHeader* header,
scoped_refptr<media::DataBuffer> encoded_data,
bool begin_update,
bool end_update);
@@ -168,11 +168,11 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
int capture_data_strides_[3];
int capture_width_;
int capture_height_;
- chromotocol_pb::PixelFormat capture_pixel_format_;
+ PixelFormat capture_pixel_format_;
// The following members are accessed on the encode thread.
// Output parameter written by Encoder to carry encoded data.
- chromotocol_pb::UpdateStreamPacketHeader encoded_data_header_;
+ UpdateStreamPacketHeader encoded_data_header_;
scoped_refptr<media::DataBuffer> encoded_data_;
// True if we have started receiving encoded data from the Encoder.
diff --git a/remoting/host/session_manager_unittest.cc b/remoting/host/session_manager_unittest.cc
index 9f4cdea..008d063 100644
--- a/remoting/host/session_manager_unittest.cc
+++ b/remoting/host/session_manager_unittest.cc
@@ -28,8 +28,8 @@ static uint8* kData[3] = {
reinterpret_cast<uint8*>(0x02),
reinterpret_cast<uint8*>(0x03),
};
-static const chromotocol_pb::PixelFormat kFormat =
- chromotocol_pb::PixelFormatRgb32;
+static const PixelFormat kFormat =
+ PixelFormatRgb32;
class SessionManagerTest : public testing::Test {
public:
@@ -106,7 +106,7 @@ TEST_F(SessionManagerTest, OneRecordCycle) {
.WillOnce(Return(kFormat));
// Expect the encoder be called.
- chromotocol_pb::UpdateStreamPacketHeader header;
+ UpdateStreamPacketHeader header;
scoped_refptr<media::DataBuffer> buffer = new media::DataBuffer(0);
EXPECT_CALL(*encoder_, SetSize(kWidth, kHeight));
EXPECT_CALL(*encoder_, SetPixelFormat(kFormat));
diff --git a/remoting/host/simple_host.cc b/remoting/host/simple_host.cc
index 8455ff5..ab9f6b7 100644
--- a/remoting/host/simple_host.cc
+++ b/remoting/host/simple_host.cc
@@ -45,11 +45,16 @@ void SimpleHost::DestroySession() {
// First we tell the session to pause and then we wait until all
// the tasks are done.
- session_->Pause();
+ if (session_.get()) {
+ session_->Pause();
- // TODO(hclam): Revise the order.
- encode_thread_.Stop();
- capture_thread_.Stop();
+ // TODO(hclam): Revise the order.
+ DCHECK(encode_thread_.IsRunning());
+ encode_thread_.Stop();
+
+ DCHECK(capture_thread_.IsRunning());
+ capture_thread_.Stop();
+ }
}
// This method talks to the cloud to register the host process. If
@@ -69,7 +74,7 @@ void SimpleHost::OnClientConnected(ClientConnection* client) {
DCHECK_EQ(&main_loop_, MessageLoop::current());
// Create a new RecordSession if there was none.
- if (!session_) {
+ if (!session_.get()) {
// The first we need to make sure capture and encode thread are
// running.
capture_thread_.Start();
@@ -101,8 +106,8 @@ void SimpleHost::OnClientDisconnected(ClientConnection* client) {
DCHECK_EQ(&main_loop_, MessageLoop::current());
// Remove the client from the session manager.
- DCHECK(session_);
- session_->RemoveClient(client);
+ if (session_.get())
+ session_->RemoveClient(client);
// Also remove reference to ClientConnection from this object.
client_ = NULL;
@@ -157,10 +162,8 @@ void SimpleHost::OnStateChange(JingleClient* jingle_client,
DCHECK_EQ(jingle_client_.get(), jingle_client);
if (state == JingleClient::CONNECTED) {
- // TODO(hclam): Change to use LOG(INFO).
- // LOG(INFO) << "Host connected as "
- // << jingle_client->GetFullJid() << "." << std::endl;
- printf("Host connected as %s\n", jingle_client->GetFullJid().c_str());
+ LOG(INFO) << "Host connected as "
+ << jingle_client->GetFullJid() << "." << std::endl;
// Start heartbeating after we connected
heartbeat_sender_ = new HeartbeatSender();
@@ -168,8 +171,10 @@ void SimpleHost::OnStateChange(JingleClient* jingle_client,
heartbeat_sender_->Start(jingle_client_.get(), "HostID");
} else if (state == JingleClient::CLOSED) {
LOG(INFO) << "Host disconnected from talk network." << std::endl;
-
heartbeat_sender_ = NULL;
+
+ // Quit the message loop if disconected.
+ main_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask());
}
}
@@ -178,6 +183,7 @@ bool SimpleHost::OnAcceptConnection(
JingleChannel::Callback** channel_callback) {
DCHECK_EQ(jingle_client_.get(), jingle_client);
+ // TODO(hclam): Allow multiple clients to connect to the host.
if (client_.get())
return false;