diff options
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; |