diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 21:56:39 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 21:56:39 +0000 |
commit | ef0a59a6fd722a0910196c951dc676c19127a28b (patch) | |
tree | 9d90ab9227e4222fa312dadae114c8d2247028bf /remoting | |
parent | e9fdd159ffd94e3e097bd6905d84e6b564b04c2c (diff) | |
download | chromium_src-ef0a59a6fd722a0910196c951dc676c19127a28b.zip chromium_src-ef0a59a6fd722a0910196c951dc676c19127a28b.tar.gz chromium_src-ef0a59a6fd722a0910196c951dc676c19127a28b.tar.bz2 |
Implement a chromoting client using X11
Using XRender to render the chromoting client. This patch has done several things:
1. Rename chromotocol_pb to remoting
2. Defined ChromotingView as the display area of the remote view
3. Implemented X11Client as the client that uses X11 for display
4. Implemented X11View that uses XRender for drawing
5. Fixed several problems in host capturer and encoder
Review URL: http://codereview.chromium.org/2745006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49329 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
36 files changed, 813 insertions, 226 deletions
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; |