diff options
31 files changed, 397 insertions, 214 deletions
diff --git a/remoting/base/capture_data.h b/remoting/base/capture_data.h index f399e70..dd6c4b9 100644 --- a/remoting/base/capture_data.h +++ b/remoting/base/capture_data.h @@ -38,6 +38,10 @@ class CaptureData : public base::RefCountedThreadSafe<CaptureData> { // written into |rects|. const InvalidRects& dirty_rects() const { return dirty_rects_; } + // TODO(simonmorris): The next two methods should be replaced by a + // gfx::Size size(), and objects that store that size should also + // use a gfx::Size to do so. + // Get the width of the image captured. int width() const { return width_; } diff --git a/remoting/base/encoder_row_based.cc b/remoting/base/encoder_row_based.cc index e035a42..6159207a 100644 --- a/remoting/base/encoder_row_based.cc +++ b/remoting/base/encoder_row_based.cc @@ -42,6 +42,8 @@ EncoderRowBased::EncoderRowBased(Compressor* compressor, VideoPacketFormat::Encoding encoding) : encoding_(encoding), compressor_(compressor), + screen_width_(0), + screen_height_(0), packet_size_(kPacketSize) { } @@ -50,6 +52,8 @@ EncoderRowBased::EncoderRowBased(Compressor* compressor, int packet_size) : encoding_(encoding), compressor_(compressor), + screen_width_(0), + screen_height_(0), packet_size_(packet_size) { } @@ -147,6 +151,13 @@ void EncoderRowBased::PrepareUpdateStart(const gfx::Rect& rect, format->set_width(rect.width()); format->set_height(rect.height()); format->set_encoding(encoding_); + if ((capture_data_->width() != screen_width_) || + (capture_data_->height() != screen_height_)) { + screen_width_ = capture_data_->width(); + screen_height_ = capture_data_->height(); + format->set_screen_width(screen_width_); + format->set_screen_height(screen_height_); + } } uint8* EncoderRowBased::GetOutputBuffer(VideoPacket* packet, size_t size) { diff --git a/remoting/base/encoder_row_based.h b/remoting/base/encoder_row_based.h index b3b62d5..fe7f49c 100644 --- a/remoting/base/encoder_row_based.h +++ b/remoting/base/encoder_row_based.h @@ -61,6 +61,10 @@ class EncoderRowBased : public Encoder { scoped_refptr<CaptureData> capture_data_; scoped_ptr<DataAvailableCallback> callback_; + // The most recent screen size. + int screen_width_; + int screen_height_; + int packet_size_; }; diff --git a/remoting/base/encoder_vp8.cc b/remoting/base/encoder_vp8.cc index 1d8f8c6..492987d 100644 --- a/remoting/base/encoder_vp8.cc +++ b/remoting/base/encoder_vp8.cc @@ -34,7 +34,9 @@ EncoderVp8::EncoderVp8() image_(NULL), active_map_width_(0), active_map_height_(0), - last_timestamp_(0) { + last_timestamp_(0), + width_(0), + height_(0) { } EncoderVp8::~EncoderVp8() { @@ -45,6 +47,8 @@ EncoderVp8::~EncoderVp8() { } bool EncoderVp8::Init(int width, int height) { + width_ = width; + height_ = height; codec_.reset(new vpx_codec_ctx_t()); image_.reset(new vpx_image_t()); memset(image_.get(), 0, sizeof(vpx_image_t)); @@ -57,6 +61,30 @@ bool EncoderVp8::Init(int width, int height) { image_->d_h = height; image_->h = height; + // YUV image size is 1.5 times of a plane. Multiplication is performed first + // to avoid rounding error. + const int plane_size = width * height; + const int size = plane_size * 3 / 2; + + yuv_image_.reset(new uint8[size]); + + // Reset image value to 128 so we just need to fill in the y plane. + memset(yuv_image_.get(), 128, size); + + // Fill in the information for |image_|. + unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get()); + image_->planes[0] = image; + image_->planes[1] = image + plane_size; + + // The V plane starts from 1.25 of the plane size. + image_->planes[2] = image + plane_size + plane_size / 4; + + // In YV12 Y plane has full width, UV plane has half width because of + // subsampling. + image_->stride[0] = image_->w; + image_->stride[1] = image_->w / 2; + image_->stride[2] = image_->w / 2; + vpx_codec_enc_cfg_t config; const vpx_codec_iface_t* algo = vpx_codec_vp8_cx(); CHECK(algo); @@ -112,34 +140,6 @@ static gfx::Rect AlignRect(const gfx::Rect& rect, int width, int height) { bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data, std::vector<gfx::Rect>* updated_rects) { - const int plane_size = capture_data->width() * capture_data->height(); - - if (!yuv_image_.get()) { - - // YUV image size is 1.5 times of a plane. Multiplication is performed first - // to avoid rounding error. - const int size = plane_size * 3 / 2; - - yuv_image_.reset(new uint8[size]); - - // Reset image value to 128 so we just need to fill in the y plane. - memset(yuv_image_.get(), 128, size); - - // Fill in the information for |image_|. - unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get()); - image_->planes[0] = image; - image_->planes[1] = image + plane_size; - - // The V plane starts from 1.25 of the plane size. - image_->planes[2] = image + plane_size + plane_size / 4; - - // In YV12 Y plane has full width, UV plane has half width because of - // subsampling. - image_->stride[0] = image_->w; - image_->stride[1] = image_->w / 2; - image_->stride[2] = image_->w / 2; - } - // Perform RGB->YUV conversion. if (capture_data->pixel_format() != media::VideoFrame::RGB32) { LOG(ERROR) << "Only RGB32 is supported"; @@ -149,6 +149,7 @@ bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data, const InvalidRects& rects = capture_data->dirty_rects(); const uint8* in = capture_data->data_planes().data[0]; const int in_stride = capture_data->data_planes().strides[0]; + const int plane_size = capture_data->width() * capture_data->height(); uint8* y_out = yuv_image_.get(); uint8* u_out = yuv_image_.get() + plane_size; uint8* v_out = yuv_image_.get() + plane_size + plane_size / 4; @@ -205,7 +206,8 @@ void EncoderVp8::PrepareActiveMap( void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, bool key_frame, DataAvailableCallback* data_available_callback) { - if (!initialized_) { + if (!initialized_ || (capture_data->width() != width_) || + (capture_data->height() != height_)) { bool ret = Init(capture_data->width(), capture_data->height()); // TODO(hclam): Handle error better. DCHECK(ret) << "Initialization of encoder failed"; @@ -269,6 +271,8 @@ void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, message->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); message->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET | VideoPacket::LAST_PARTITION); + message->mutable_format()->set_screen_width(capture_data->width()); + message->mutable_format()->set_screen_height(capture_data->height()); for (size_t i = 0; i < updated_rects.size(); ++i) { Rect* rect = message->add_dirty_rects(); rect->set_x(updated_rects[i].x()); diff --git a/remoting/base/encoder_vp8.h b/remoting/base/encoder_vp8.h index e283d16..2bf73dc 100644 --- a/remoting/base/encoder_vp8.h +++ b/remoting/base/encoder_vp8.h @@ -51,6 +51,10 @@ class EncoderVp8 : public Encoder { // Buffer for storing the yuv image. scoped_array<uint8> yuv_image_; + // The current frame dimensions. + int width_; + int height_; + DISALLOW_COPY_AND_ASSIGN(EncoderVp8); }; diff --git a/remoting/client/appengine/static_files/chromoting_session.js b/remoting/client/appengine/static_files/chromoting_session.js index 9d4c481..3e22c85 100644 --- a/remoting/client/appengine/static_files/chromoting_session.js +++ b/remoting/client/appengine/static_files/chromoting_session.js @@ -24,6 +24,7 @@ function init() { // a 'callback' property that contains the callback function. plugin.connectionInfoUpdate = connectionInfoUpdateCallback; plugin.debugInfo = debugInfoCallback; + plugin.desktopSizeUpdate = desktopSizeChanged; plugin.loginChallenge = loginChallengeCallback; console.log('connect request received: ' + chromoting.hostname + ' by ' + diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index eacd49e..0cdfe62 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -73,9 +73,8 @@ bool ChromotingInstance::Init(uint32_t argc, context_.jingle_thread())); view_.reset(new PepperView(this, &context_)); view_proxy_ = new PepperViewProxy(this, view_.get()); - rectangle_decoder_.reset( - new RectangleUpdateDecoder(context_.decode_message_loop(), - view_proxy_)); + rectangle_decoder_ = new RectangleUpdateDecoder( + context_.decode_message_loop(), view_proxy_); input_handler_.reset(new PepperInputHandler(&context_, host_connection_.get(), view_proxy_)); diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index 9e1c2d3..554760b 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -101,7 +101,7 @@ class ChromotingInstance : public pp::Instance { // both ChromotingInstance and PepperView are destroyed and there will be // outstanding tasks on the pepper message loo. scoped_refptr<PepperViewProxy> view_proxy_; - scoped_ptr<RectangleUpdateDecoder> rectangle_decoder_; + scoped_refptr<RectangleUpdateDecoder> rectangle_decoder_; scoped_ptr<InputHandler> input_handler_; scoped_ptr<ChromotingClient> client_; diff --git a/remoting/client/plugin/chromoting_scriptable_object.cc b/remoting/client/plugin/chromoting_scriptable_object.cc index b45b822..d1b7eec 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.cc +++ b/remoting/client/plugin/chromoting_scriptable_object.cc @@ -19,6 +19,7 @@ const char kConnectionInfoUpdate[] = "connectionInfoUpdate"; const char kDebugInfo[] = "debugInfo"; const char kDesktopHeight[] = "desktopHeight"; const char kDesktopWidth[] = "desktopWidth"; +const char kDesktopSizeUpdate[] = "desktopSizeUpdate"; const char kLoginChallenge[] = "loginChallenge"; const char kQualityAttribute[] = "quality"; const char kStatusAttribute[] = "status"; @@ -57,6 +58,7 @@ void ChromotingScriptableObject::Init() { // Debug info to display. AddAttribute(kConnectionInfoUpdate, Var()); AddAttribute(kDebugInfo, Var()); + AddAttribute(kDesktopSizeUpdate, Var()); AddAttribute(kLoginChallenge, Var()); AddAttribute(kDesktopWidth, Var(0)); AddAttribute(kDesktopHeight, Var(0)); @@ -142,6 +144,7 @@ void ChromotingScriptableObject::SetProperty(const Var& name, std::string property_name = name.AsString(); if (property_name != kConnectionInfoUpdate && property_name != kDebugInfo && + property_name != kDesktopSizeUpdate && property_name != kLoginChallenge && property_name != kDesktopWidth && property_name != kDesktopHeight) { @@ -208,6 +211,7 @@ void ChromotingScriptableObject::SetDesktopSize(int width, int height) { properties_[height_index].attribute.AsInt() != height) { properties_[width_index].attribute = Var(width); properties_[height_index].attribute = Var(height); + SignalDesktopSizeChange(); } LogDebugInfo(base::StringPrintf("Update desktop size to: %d x %d.", @@ -239,6 +243,23 @@ void ChromotingScriptableObject::SignalConnectionInfoChange() { "Exception when invoking connectionInfoUpdate JS callback."); } +void ChromotingScriptableObject::SignalDesktopSizeChange() { + Var exception; + + // The JavaScript callback function is the 'callback' property on the + // 'desktopSizeUpdate' object. + Var cb = GetProperty(Var(kDesktopSizeUpdate), &exception); + + // Var() means call the object directly as a function rather than calling + // a method in the object. + cb.Call(Var(), 0, NULL, &exception); + + if (!exception.is_undefined()) { + LOG(WARNING) << "Exception when invoking JS callback" + << exception.AsString(); + } +} + void ChromotingScriptableObject::SignalLoginChallenge() { Var exception; Var cb = GetProperty(Var(kLoginChallenge), &exception); diff --git a/remoting/client/plugin/chromoting_scriptable_object.h b/remoting/client/plugin/chromoting_scriptable_object.h index b8b848e62..9ebc8fa 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.h +++ b/remoting/client/plugin/chromoting_scriptable_object.h @@ -160,6 +160,9 @@ class ChromotingScriptableObject // changed. void SignalConnectionInfoChange(); + // Signal the JS code that the desktop size has changed. + void SignalDesktopSizeChange(); + pp::Var DoConnect(const std::vector<pp::Var>& args, pp::Var* exception); pp::Var DoDisconnect(const std::vector<pp::Var>& args, pp::Var* exception); diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc index 5d154ba..6bc4508 100644 --- a/remoting/client/plugin/pepper_view.cc +++ b/remoting/client/plugin/pepper_view.cc @@ -20,8 +20,6 @@ namespace remoting { PepperView::PepperView(ChromotingInstance* instance, ClientContext* context) : instance_(instance), context_(context), - viewport_x_(0), - viewport_y_(0), viewport_width_(0), viewport_height_(0), is_static_fill_(false), @@ -83,6 +81,8 @@ void PepperView::PaintFrame(media::VideoFrame* frame, UpdatedRects* rects) { TraceContext::tracer()->PrintString("Start Paint Frame."); + SetViewport(0, 0, frame->width(), frame->height()); + uint8* frame_data = frame->data(media::VideoFrame::kRGBPlane); const int kFrameStride = frame->stride(media::VideoFrame::kRGBPlane); const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32); @@ -178,11 +178,9 @@ void PepperView::UpdateLoginStatus(bool success, const std::string& info) { void PepperView::SetViewport(int x, int y, int width, int height) { DCHECK(instance_->CurrentlyOnPluginThread()); - // TODO(ajwong): Should we ignore x & y updates? What do those even mean? + if ((width == viewport_width_) && (height == viewport_height_)) + return; - // TODO(ajwong): What does viewport x, y mean to a plugin anyways? - viewport_x_ = x; - viewport_y_ = y; viewport_width_ = width; viewport_height_ = height; diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h index f7822f3..119d1ca 100644 --- a/remoting/client/plugin/pepper_view.h +++ b/remoting/client/plugin/pepper_view.h @@ -16,7 +16,6 @@ #include "ppapi/cpp/graphics_2d.h" #include "remoting/client/chromoting_view.h" #include "remoting/client/frame_consumer.h" -#include "remoting/client/rectangle_update_decoder.h" namespace remoting { @@ -68,8 +67,6 @@ class PepperView : public ChromotingView, pp::Graphics2D graphics2d_; - int viewport_x_; - int viewport_y_; int viewport_width_; int viewport_height_; diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc index 2690e14..5659fc5 100644 --- a/remoting/client/rectangle_update_decoder.cc +++ b/remoting/client/rectangle_update_decoder.cc @@ -44,15 +44,16 @@ class PartialFrameCleanup : public Task { RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop, FrameConsumer* consumer) : message_loop_(message_loop), - consumer_(consumer) { + consumer_(consumer), + frame_is_new_(false) { } RectangleUpdateDecoder::~RectangleUpdateDecoder() { } void RectangleUpdateDecoder::Initialize(const SessionConfig* config) { - screen_size_ = gfx::Size(config->initial_resolution().width, - config->initial_resolution().height); + initial_screen_size_ = gfx::Size(config->initial_resolution().width, + config->initial_resolution().height); // Initialize decoder based on the selected codec. ChannelConfig::Codec codec = config->video_config().codec; @@ -87,77 +88,90 @@ void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet, TraceContext::tracer()->PrintString("Decode Packet called."); - if (!decoder_->IsReadyForData()) { - InitializeDecoder( - NewTracedMethod(this, - &RectangleUpdateDecoder::ProcessPacketData, - packet, done_runner.release())); - } else { - ProcessPacketData(packet, done_runner.release()); - } + AllocateFrame(packet, done_runner.release()); } -void RectangleUpdateDecoder::ProcessPacketData( - const VideoPacket* packet, Task* done) { - AutoTaskRunner done_runner(done); - - if (!decoder_->IsReadyForData()) { - // TODO(ajwong): This whole thing should move into an invalid state. - LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; - return; - } - - TraceContext::tracer()->PrintString("Executing Decode."); - - Decoder::DecodeResult result = decoder_->DecodePacket(packet); - - if (result == Decoder::DECODE_DONE) { - UpdatedRects* rects = new UpdatedRects(); - decoder_->GetUpdatedRects(rects); - consumer_->OnPartialFrameOutput(frame_, rects, - new PartialFrameCleanup(frame_, rects)); - } -} - -void RectangleUpdateDecoder::InitializeDecoder(Task* done) { +void RectangleUpdateDecoder::AllocateFrame(const VideoPacket* packet, + Task* done) { if (message_loop_ != MessageLoop::current()) { message_loop_->PostTask( FROM_HERE, NewTracedMethod(this, - &RectangleUpdateDecoder::InitializeDecoder, done)); + &RectangleUpdateDecoder::AllocateFrame, packet, done)); return; } AutoTaskRunner done_runner(done); - // Check if we need to request a new frame. - if (!frame_ || - frame_->width() != static_cast<size_t>(screen_size_.width()) || - frame_->height() != static_cast<size_t>(screen_size_.height())) { + TraceContext::tracer()->PrintString("AllocateFrame called."); + + // Find the required frame size. + bool has_screen_size = packet->format().has_screen_width() && + packet->format().has_screen_height(); + gfx::Size screen_size(packet->format().screen_width(), + packet->format().screen_height()); + if (!has_screen_size) + screen_size = initial_screen_size_; + + // Find the current frame size. + gfx::Size frame_size(0, 0); + if (frame_) + frame_size = gfx::Size(static_cast<int>(frame_->width()), + static_cast<int>(frame_->height())); + + // Allocate a new frame, if necessary. + if ((!frame_) || (has_screen_size && (screen_size != frame_size))) { if (frame_) { TraceContext::tracer()->PrintString("Releasing old frame."); consumer_->ReleaseFrame(frame_); frame_ = NULL; } TraceContext::tracer()->PrintString("Requesting new frame."); + consumer_->AllocateFrame(media::VideoFrame::RGB32, - screen_size_.width(), screen_size_.height(), + screen_size.width(), screen_size.height(), base::TimeDelta(), base::TimeDelta(), &frame_, - NewTracedMethod( - this, - &RectangleUpdateDecoder::InitializeDecoder, - done_runner.release())); + NewRunnableMethod(this, + &RectangleUpdateDecoder::ProcessPacketData, + packet, done_runner.release())); + frame_is_new_ = true; return; } + ProcessPacketData(packet, done_runner.release()); +} + +void RectangleUpdateDecoder::ProcessPacketData( + const VideoPacket* packet, Task* done) { + if (message_loop_ != MessageLoop::current()) { + message_loop_->PostTask( + FROM_HERE, + NewTracedMethod(this, + &RectangleUpdateDecoder::ProcessPacketData, packet, + done)); + return; + } + AutoTaskRunner done_runner(done); + + if (frame_is_new_) { + decoder_->Reset(); + decoder_->Initialize(frame_); + frame_is_new_ = false; + } - // TODO(ajwong): We need to handle the allocator failing to create a frame - // and properly disable this class. - CHECK(frame_); + if (!decoder_->IsReadyForData()) { + // TODO(ajwong): This whole thing should move into an invalid state. + LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; + return; + } - decoder_->Reset(); - decoder_->Initialize(frame_); + TraceContext::tracer()->PrintString("Executing Decode."); - TraceContext::tracer()->PrintString("Decoder is Initialized"); + if (decoder_->DecodePacket(packet) == Decoder::DECODE_DONE) { + UpdatedRects* rects = new UpdatedRects(); + decoder_->GetUpdatedRects(rects); + consumer_->OnPartialFrameOutput(frame_, rects, + new PartialFrameCleanup(frame_, rects)); + } } } // namespace remoting diff --git a/remoting/client/rectangle_update_decoder.h b/remoting/client/rectangle_update_decoder.h index bcd521a..fbcc69b 100644 --- a/remoting/client/rectangle_update_decoder.h +++ b/remoting/client/rectangle_update_decoder.h @@ -8,13 +8,13 @@ #include "base/scoped_ptr.h" #include "base/task.h" #include "media/base/video_frame.h" +#include "remoting/base/decoder.h" #include "ui/gfx/size.h" class MessageLoop; namespace remoting { -class Decoder; class FrameConsumer; class VideoPacketFormat; class VideoPacket; @@ -26,11 +26,11 @@ class SessionConfig; // TODO(ajwong): Re-examine this API, especially with regards to how error // conditions on each step are reported. Should they be CHECKs? Logs? Other? // TODO(sergeyu): Rename this class. -class RectangleUpdateDecoder { +class RectangleUpdateDecoder : + public base::RefCountedThreadSafe<RectangleUpdateDecoder> { public: RectangleUpdateDecoder(MessageLoop* message_loop, FrameConsumer* consumer); - ~RectangleUpdateDecoder(); // Initializes decoder with the infromation from the protocol config. void Initialize(const protocol::SessionConfig* config); @@ -45,20 +45,23 @@ class RectangleUpdateDecoder { void DecodePacket(const VideoPacket* packet, Task* done); private: - void InitializeDecoder(Task* done); + friend class base::RefCountedThreadSafe<RectangleUpdateDecoder>; + ~RectangleUpdateDecoder(); + void AllocateFrame(const VideoPacket* packet, Task* done); void ProcessPacketData(const VideoPacket* packet, Task* done); // Pointers to infrastructure objects. Not owned. MessageLoop* message_loop_; FrameConsumer* consumer_; - gfx::Size screen_size_; + gfx::Size initial_screen_size_; scoped_ptr<Decoder> decoder_; - // Framebuffer for the decoder. + // The video frame that the decoder writes to. scoped_refptr<media::VideoFrame> frame_; + bool frame_is_new_; }; } // namespace remoting diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc index 7bfd3b4..5d0867f 100644 --- a/remoting/client/x11_client.cc +++ b/remoting/client/x11_client.cc @@ -34,11 +34,12 @@ int main(int argc, char** argv) { remoting::ClientContext context; remoting::protocol::ConnectionToHost connection(context.jingle_thread()); remoting::X11View view; - remoting::RectangleUpdateDecoder rectangle_decoder( - context.decode_message_loop(), &view); + scoped_refptr<remoting::RectangleUpdateDecoder> rectangle_decoder = + new remoting::RectangleUpdateDecoder(context.decode_message_loop(), + &view); remoting::X11InputHandler input_handler(&context, &connection, &view); remoting::ChromotingClient client( - config, &context, &connection, &view, &rectangle_decoder, &input_handler, + config, &context, &connection, &view, rectangle_decoder, &input_handler, NewRunnableFunction(&ClientQuit, &ui_loop)); // Run the client on a new MessageLoop until diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h index 9ba7fc1..d3e9623 100644 --- a/remoting/client/x11_view.h +++ b/remoting/client/x11_view.h @@ -9,8 +9,8 @@ #include "base/task.h" #include "media/base/video_frame.h" #include "remoting/base/decoder.h" // For UpdatedRects -#include "remoting/client/frame_consumer.h" #include "remoting/client/chromoting_view.h" +#include "remoting/client/frame_consumer.h" typedef unsigned long XID; typedef struct _XDisplay Display; diff --git a/remoting/host/capturer.cc b/remoting/host/capturer.cc index 59854f1..0dd09ab 100644 --- a/remoting/host/capturer.cc +++ b/remoting/host/capturer.cc @@ -11,27 +11,16 @@ namespace remoting { Capturer::Capturer(MessageLoop* message_loop) - : width_(0), - height_(0), - pixel_format_(media::VideoFrame::INVALID), - bytes_per_row_(0), + : pixel_format_(media::VideoFrame::INVALID), current_buffer_(0), - message_loop_(message_loop) { + message_loop_(message_loop), + width_most_recent_(0), + height_most_recent_(0) { } Capturer::~Capturer() { } -// Return the width of the screen. -int Capturer::width() const { - return width_; -} - -// Return the height of the screen. -int Capturer::height() const { - return height_; -} - // Return the pixel format of the screen. media::VideoFrame::Format Capturer::pixel_format() const { return pixel_format_; @@ -53,10 +42,14 @@ void Capturer::InvalidateRects(const InvalidRects& inval_rects) { } } -void Capturer::InvalidateFullScreen() { +void Capturer::InvalidateFullScreen(int width, int height) { base::AutoLock auto_inval_rects_lock(inval_rects_lock_); inval_rects_.clear(); - inval_rects_.insert(gfx::Rect(0, 0, width_, height_)); + inval_rects_.insert(gfx::Rect(0, 0, width, height)); +} + +void Capturer::InvalidateFullScreen() { + InvalidateFullScreen(width_most_recent_, height_most_recent_); } void Capturer::CaptureInvalidRects(CaptureCompletedCallback* callback) { @@ -81,16 +74,19 @@ void Capturer::FinishCapture(scoped_refptr<CaptureData> data, // Select the next buffer to be the current buffer. current_buffer_ = (current_buffer_ + 1) % kNumBuffers; + width_most_recent_ = data->width(); + height_most_recent_ = data->height(); + callback->Run(data); delete callback; } -bool Capturer::IsCaptureFullScreen() { +bool Capturer::IsCaptureFullScreen(int width, int height) { base::AutoLock auto_inval_rects_lock(inval_rects_lock_); return inval_rects_.size() == 1u && inval_rects_.begin()->x() == 0 && inval_rects_.begin()->y() == 0 && - inval_rects_.begin()->width() == width_ && - inval_rects_.begin()->height() == height_; + inval_rects_.begin()->width() == width && + inval_rects_.begin()->height() == height; } } // namespace remoting diff --git a/remoting/host/capturer.h b/remoting/host/capturer.h index a769abc..a91dcd7 100644 --- a/remoting/host/capturer.h +++ b/remoting/host/capturer.h @@ -52,12 +52,6 @@ class Capturer { // Called when the screen configuration is changed. virtual void ScreenConfigurationChanged() = 0; - // Return the width of the screen. - virtual int width() const; - - // Return the height of the screen. - virtual int height() const; - // Return the pixel format of the screen. virtual media::VideoFrame::Format pixel_format() const; @@ -67,7 +61,11 @@ class Capturer { // Invalidate the specified screen rects. void InvalidateRects(const InvalidRects& inval_rects); - // Invalidate the entire screen. + // Invalidate the entire screen, of a given size. + void InvalidateFullScreen(int width, int height); + + // Invalidate the entire screen, using the size of the most recently + // captured screen. virtual void InvalidateFullScreen(); // Capture the screen data associated with each of the accumulated @@ -85,6 +83,10 @@ class Capturer { // method is called. virtual void CaptureInvalidRects(CaptureCompletedCallback* callback); + // Get the width/height of the most recently captured screen. + int width_most_recent() { return width_most_recent_; } + int height_most_recent() { return height_most_recent_; } + protected: explicit Capturer(MessageLoop* message_loop); @@ -116,18 +118,13 @@ class Capturer { // Called by subclasses' CalculateInvalidRects() method to check if // InvalidateFullScreen() was called by user. - bool IsCaptureFullScreen(); + bool IsCaptureFullScreen(int width, int height); // Number of screen buffers. static const int kNumBuffers = 2; - // Capture screen dimensions. - int width_; - int height_; - // Format of pixels returned in buffer. media::VideoFrame::Format pixel_format_; - int bytes_per_row_; // The current buffer with valid data for reading. int current_buffer_; @@ -143,6 +140,10 @@ class Capturer { // A lock protecting |inval_rects_| across threads. base::Lock inval_rects_lock_; + + // The width and height of the most recently captured screen. + int width_most_recent_; + int height_most_recent_; }; } // namespace remoting diff --git a/remoting/host/capturer_fake.cc b/remoting/host/capturer_fake.cc index 0cc0f37..a574457 100644 --- a/remoting/host/capturer_fake.cc +++ b/remoting/host/capturer_fake.cc @@ -26,6 +26,7 @@ static const int kBytesPerPixel = 4; // 32 bit RGB is 4 bytes per pixel. CapturerFake::CapturerFake(MessageLoop* message_loop) : Capturer(message_loop), + bytes_per_row_(0), box_pos_x_(0), box_pos_y_(0), box_speed_x_(kSpeed), @@ -39,8 +40,8 @@ CapturerFake::~CapturerFake() { void CapturerFake::ScreenConfigurationChanged() { width_ = kWidth; height_ = kHeight; - pixel_format_ = media::VideoFrame::RGB32; bytes_per_row_ = width_ * kBytesPerPixel; + pixel_format_ = media::VideoFrame::RGB32; // Create memory for the buffers. int buffer_size = height_ * bytes_per_row_; @@ -51,7 +52,7 @@ void CapturerFake::ScreenConfigurationChanged() { void CapturerFake::CalculateInvalidRects() { GenerateImage(); - InvalidateFullScreen(); + InvalidateFullScreen(width_, height_); } void CapturerFake::CaptureRects(const InvalidRects& rects, diff --git a/remoting/host/capturer_fake.h b/remoting/host/capturer_fake.h index a6279b7..420d30e 100644 --- a/remoting/host/capturer_fake.h +++ b/remoting/host/capturer_fake.h @@ -29,6 +29,9 @@ class CapturerFake : public Capturer { // Generates an image in the front buffer. void GenerateImage(); + int width_; + int height_; + int bytes_per_row_; int box_pos_x_; int box_pos_y_; int box_speed_x_; diff --git a/remoting/host/capturer_fake_ascii.cc b/remoting/host/capturer_fake_ascii.cc index 1729fb6..8ea73c4 100644 --- a/remoting/host/capturer_fake_ascii.cc +++ b/remoting/host/capturer_fake_ascii.cc @@ -22,8 +22,8 @@ CapturerFakeAscii::~CapturerFakeAscii() { void CapturerFakeAscii::ScreenConfigurationChanged() { width_ = kWidth; height_ = kHeight; - pixel_format_ = media::VideoFrame::ASCII; bytes_per_row_ = width_ * kBytesPerPixel; + pixel_format_ = media::VideoFrame::ASCII; // Create memory for the buffers. int buffer_size = height_ * bytes_per_row_; diff --git a/remoting/host/capturer_fake_ascii.h b/remoting/host/capturer_fake_ascii.h index cd02bc4..e68b8b4 100644 --- a/remoting/host/capturer_fake_ascii.h +++ b/remoting/host/capturer_fake_ascii.h @@ -30,6 +30,11 @@ class CapturerFakeAscii : public Capturer { // Generates an image in the front buffer. void GenerateImage(); + // The screen dimensions. + int width_; + int height_; + int bytes_per_row_; + // We have two buffers for the screen images as required by Capturer. scoped_array<uint8> buffers_[kNumBuffers]; diff --git a/remoting/host/capturer_gdi.cc b/remoting/host/capturer_gdi.cc index bdaf756..36c8945 100644 --- a/remoting/host/capturer_gdi.cc +++ b/remoting/host/capturer_gdi.cc @@ -18,6 +18,7 @@ CapturerGdi::CapturerGdi(MessageLoop* message_loop) : Capturer(message_loop), desktop_dc_(NULL), memory_dc_(NULL), + dc_size_(0, 0), capture_fullscreen_(true) { memset(target_bitmap_, 0, sizeof(target_bitmap_)); memset(buffers_, 0, sizeof(buffers_)); @@ -34,9 +35,9 @@ void CapturerGdi::ReleaseBuffers() { DeleteObject(target_bitmap_[i]); target_bitmap_[i] = NULL; } - if (buffers_[i]) { - DeleteObject(buffers_[i]); - buffers_[i] = NULL; + if (buffers_[i].data) { + DeleteObject(buffers_[i].data); + buffers_[i].data = NULL; } } @@ -48,96 +49,149 @@ void CapturerGdi::ReleaseBuffers() { } void CapturerGdi::ScreenConfigurationChanged() { - ReleaseBuffers(); + // We poll for screen configuration changes, so ignore notifications. +} - desktop_dc_ = GetDC(GetDesktopWindow()); - memory_dc_ = CreateCompatibleDC(desktop_dc_); +void CapturerGdi::UpdateBufferCapture(const gfx::Size& size) { + // Make sure the DCs have the correct dimensions. + if (size != dc_size_) { + // TODO(simonmorris): screen dimensions changing isn't equivalent to needing + // a new DC, but it's good enough for now. + desktop_dc_ = GetDC(GetDesktopWindow()); + if (memory_dc_) + DeleteDC(memory_dc_); + memory_dc_ = CreateCompatibleDC(desktop_dc_); + dc_size_ = size; + } + + // Make sure the current bitmap has the correct dimensions. + if (size != buffers_[current_buffer_].size) { + ReallocateBuffer(current_buffer_, size); + capture_fullscreen_ = true; + } +} + +void CapturerGdi::ReallocateBuffer(int buffer_index, const gfx::Size& size) { + // Delete any previously constructed bitmap. + if (target_bitmap_[buffer_index]) { + DeleteObject(target_bitmap_[buffer_index]); + target_bitmap_[buffer_index] = NULL; + } + if (buffers_[buffer_index].data) { + DeleteObject(buffers_[buffer_index].data); + buffers_[buffer_index].data = NULL; + } // Create a bitmap to keep the desktop image. - width_ = GetSystemMetrics(SM_CXSCREEN); - height_ = GetSystemMetrics(SM_CYSCREEN); - int rounded_width = (width_ + 3) & (~3); + int rounded_width = (size.width() + 3) & (~3); // Dimensions of screen. pixel_format_ = media::VideoFrame::RGB32; - bytes_per_row_ = rounded_width * kBytesPerPixel; - - // Create a differ for this screen size. - differ_.reset(new Differ(width_, height_, 4, bytes_per_row_)); + int bytes_per_row = rounded_width * kBytesPerPixel; - // Create a device independant bitmap (DIB) that is the same size. + // Create a device independent bitmap (DIB) that is the same size. BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); - bmi.bmiHeader.biHeight = -height_; - bmi.bmiHeader.biWidth = width_; + bmi.bmiHeader.biHeight = -size.height(); + bmi.bmiHeader.biWidth = size.width(); bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biSizeImage = bytes_per_row_ * height_; + bmi.bmiHeader.biSizeImage = bytes_per_row * size.height(); bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; // Create memory for the buffers. - for (int i = 0; i < kNumBuffers; i++) { - target_bitmap_[i] = CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS, - static_cast<void**>(&buffers_[i]), - NULL, 0); - } - - capture_fullscreen_ = true; + target_bitmap_[buffer_index] = + CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS, + static_cast<void**>(&buffers_[buffer_index].data), + NULL, 0); + buffers_[buffer_index].size = gfx::Size(bmi.bmiHeader.biWidth, + std::abs(bmi.bmiHeader.biHeight)); + buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8; + buffers_[buffer_index].bytes_per_row = + bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight); } void CapturerGdi::CalculateInvalidRects() { CaptureImage(); - if (IsCaptureFullScreen()) + const VideoFrameBuffer& current = buffers_[current_buffer_]; + if (IsCaptureFullScreen(current.size.width(), current.size.height())) capture_fullscreen_ = true; if (capture_fullscreen_) { - InvalidateFullScreen(); - } else { - // Calculate the difference between the previous and current screen. - int prev_buffer_id = current_buffer_ - 1; - if (prev_buffer_id < 0) { - prev_buffer_id = kNumBuffers - 1; - } - - void* prev_buffer = buffers_[prev_buffer_id]; - void* curr_buffer = buffers_[current_buffer_]; + InvalidateFullScreen(current.size.width(), current.size.height()); + capture_fullscreen_ = false; + return; + } - InvalidRects rects; - differ_->CalcDirtyRects(prev_buffer, curr_buffer, &rects); + // Find the previous and current screens. + int prev_buffer_id = current_buffer_ - 1; + if (prev_buffer_id < 0) { + prev_buffer_id = kNumBuffers - 1; + } + const VideoFrameBuffer& prev = buffers_[prev_buffer_id]; + + // Maybe the previous and current screens can't be differenced. + if ((current.size != prev.size) || + (current.bytes_per_pixel != prev.bytes_per_pixel) || + (current.bytes_per_row != prev.bytes_per_row)) { + InvalidateFullScreen(current.size.width(), current.size.height()); + return; + } - InvalidateRects(rects); + // Make sure the differencer is set up correctly for these previous and + // current screens. + if (!differ_.get() || + (differ_->width() != current.size.width()) || + (differ_->height() != current.size.height()) || + (differ_->bytes_per_pixel() != current.bytes_per_pixel) || + (differ_->bytes_per_row() != current.bytes_per_row)) { + differ_.reset(new Differ(current.size.width(), current.size.height(), + current.bytes_per_pixel, current.bytes_per_row)); } - capture_fullscreen_ = false; + InvalidRects rects; + differ_->CalcDirtyRects(prev.data, current.data, &rects); + + InvalidateRects(rects); } void CapturerGdi::CaptureRects(const InvalidRects& rects, CaptureCompletedCallback* callback) { + const VideoFrameBuffer& buffer = buffers_[current_buffer_]; DataPlanes planes; - planes.data[0] = static_cast<uint8*>(buffers_[current_buffer_]); - planes.strides[0] = bytes_per_row_; + planes.data[0] = static_cast<uint8*>(buffer.data); + planes.strides[0] = buffer.bytes_per_row; scoped_refptr<CaptureData> data(new CaptureData(planes, - width(), - height(), - pixel_format())); + buffer.size.width(), + buffer.size.height(), + pixel_format_)); data->mutable_dirty_rects() = rects; FinishCapture(data, callback); } void CapturerGdi::CaptureImage() { + // Make sure the structures we use to capture the image have the correct size. + UpdateBufferCapture(GetScreenSize()); + // Select the target bitmap into the memory dc. SelectObject(memory_dc_, target_bitmap_[current_buffer_]); // And then copy the rect from desktop to memory. - BitBlt(memory_dc_, 0, 0, width_, height_, desktop_dc_, 0, 0, + BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), + buffers_[current_buffer_].size.height(), desktop_dc_, 0, 0, SRCCOPY | CAPTUREBLT); } +gfx::Size CapturerGdi::GetScreenSize() { + return gfx::Size(GetSystemMetrics(SM_CXSCREEN), + GetSystemMetrics(SM_CYSCREEN)); +} + // static Capturer* Capturer::Create(MessageLoop* message_loop) { return new CapturerGdi(message_loop); diff --git a/remoting/host/capturer_gdi.h b/remoting/host/capturer_gdi.h index 72aa261..634520e 100644 --- a/remoting/host/capturer_gdi.h +++ b/remoting/host/capturer_gdi.h @@ -26,6 +26,31 @@ class CapturerGdi : public Capturer { virtual void ScreenConfigurationChanged(); private: + struct VideoFrameBuffer { + VideoFrameBuffer(void* data, const gfx::Size& size, int bytes_per_pixel, + int bytes_per_row) + : data(data), size(size), bytes_per_pixel(bytes_per_pixel), + bytes_per_row(bytes_per_row) { + } + VideoFrameBuffer() { + data = 0; + size = gfx::Size(0, 0); + bytes_per_pixel = 0; + bytes_per_row = 0; + } + void* data; + gfx::Size size; + int bytes_per_pixel; + int bytes_per_row; + }; + + // Make sure that the current buffer has the same size as the screen. + void UpdateBufferCapture(const gfx::Size& size); + + // Allocate memory for a buffer of a given size, freeing any memory previously + // allocated for that buffer. + void ReallocateBuffer(int buffer_index, const gfx::Size& size); + virtual void CalculateInvalidRects(); virtual void CaptureRects(const InvalidRects& rects, CaptureCompletedCallback* callback); @@ -34,13 +59,24 @@ class CapturerGdi : public Capturer { // Generates an image in the current buffer. void CaptureImage(); + // Gets the current screen size and calls ScreenConfigurationChanged + // if the screen size has changed. + void MaybeChangeScreenConfiguration(); + + // Gets the screen size. + gfx::Size GetScreenSize(); + // Gdi specific information about screen. HDC desktop_dc_; HDC memory_dc_; HBITMAP target_bitmap_[kNumBuffers]; + // The screen size attached to the device contexts through which the screen + // is captured. + gfx::Size dc_size_; + // We have two buffers for the screen images as required by Capturer. - void* buffers_[kNumBuffers]; + VideoFrameBuffer buffers_[kNumBuffers]; // Class to calculate the difference between two screen bitmaps. scoped_ptr<Differ> differ_; diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc index f8e3fc6..408eda0 100644 --- a/remoting/host/capturer_linux.cc +++ b/remoting/host/capturer_linux.cc @@ -68,6 +68,8 @@ class CapturerLinuxPimpl { Display* display_; GC gc_; Window root_window_; + int width_; + int height_; // XDamage information. Damage damage_handle_; @@ -117,6 +119,8 @@ CapturerLinuxPimpl::CapturerLinuxPimpl(CapturerLinux* capturer) display_(NULL), gc_(NULL), root_window_(BadValue), + width_(0), + height_(0), damage_handle_(BadValue), damage_event_base_(-1), damage_error_base_(-1), @@ -182,23 +186,21 @@ bool CapturerLinuxPimpl::Init() { // Set up the dimensions of the catpure framebuffer. XWindowAttributes root_attr; XGetWindowAttributes(display_, root_window_, &root_attr); - capturer_->width_ = root_attr.width; - capturer_->height_ = root_attr.height; - stride_ = capturer_->width() * kBytesPerPixel; - VLOG(1) << "Initialized with Geometry: " << capturer_->width() - << "x" << capturer_->height(); + width_ = root_attr.width; + height_ = root_attr.height; + stride_ = width_ * kBytesPerPixel; + VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_; // Allocate the screen buffers. for (int i = 0; i < CapturerLinux::kNumBuffers; i++) { - buffers_[i] = - new uint8[capturer_->width() * capturer_->height() * kBytesPerPixel]; + buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel]; } return true; } void CapturerLinuxPimpl::CalculateInvalidRects() { - if (capturer_->IsCaptureFullScreen()) + if (capturer_->IsCaptureFullScreen(width_, height_)) capture_fullscreen_ = true; // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor. @@ -243,8 +245,7 @@ void CapturerLinuxPimpl::CaptureRects( planes.strides[0] = stride_; scoped_refptr<CaptureData> capture_data( - new CaptureData(planes, capturer_->width(), capturer_->height(), - media::VideoFrame::RGB32)); + new CaptureData(planes, width_, height_, media::VideoFrame::RGB32)); // Synchronize the current buffer with the last one since we do not capture // the entire desktop. Note that encoder may be reading from the previous @@ -259,7 +260,7 @@ void CapturerLinuxPimpl::CaptureRects( for (int i = 0; i < it->height(); ++i) { memcpy(buffer + offset, last_buffer_ + offset, it->width() * kBytesPerPixel); - offset += capturer_->width() * kBytesPerPixel; + offset += width_ * kBytesPerPixel; } } @@ -343,7 +344,7 @@ void CapturerLinuxPimpl::SlowBlit(XImage* image, int dest_x, int dest_y, unsigned int max_green = image->green_mask >> green_shift; // Produce an upside-down image. - uint8* dst_pos = dst_buffer + dst_stride * (capturer_->height() - dest_y - 1); + uint8* dst_pos = dst_buffer + dst_stride * (height_ - dest_y - 1); dst_pos += dest_x * kBytesPerPixel; // TODO(jamiewalch): Optimize, perhaps using MMX code or by converting to // YUV directly diff --git a/remoting/host/capturer_mac.cc b/remoting/host/capturer_mac.cc index dd94649..614386f 100644 --- a/remoting/host/capturer_mac.cc +++ b/remoting/host/capturer_mac.cc @@ -12,7 +12,10 @@ namespace remoting { CapturerMac::CapturerMac(MessageLoop* message_loop) : Capturer(message_loop), - cgl_context_(NULL) { + cgl_context_(NULL), + width_(0), + height_(0), + bytes_per_row_(0) { // TODO(dmaclach): move this initialization out into session_manager, // or at least have session_manager call into here to initialize it. CGError err = @@ -51,7 +54,7 @@ void CapturerMac::ScreenConfigurationChanged() { height_ = CGDisplayPixelsHigh(mainDevice); pixel_format_ = media::VideoFrame::RGB32; bytes_per_row_ = width_ * sizeof(uint32_t); - size_t buffer_size = height() * bytes_per_row_; + size_t buffer_size = height_ * bytes_per_row_; for (int i = 0; i < kNumBuffers; ++i) { buffers_[i].reset(new uint8[buffer_size]); } @@ -93,19 +96,17 @@ void CapturerMac::CaptureRects(const InvalidRects& rects, glPixelStorei(GL_PACK_SKIP_PIXELS, 0); // Read a block of pixels from the frame buffer. - int h = height(); - int w = width(); uint8* flip_buffer = flip_buffer_.get(); uint8* current_buffer = buffers_[current_buffer_].get(); - - glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, flip_buffer); + glReadPixels(0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, flip_buffer); glPopClientAttrib(); // OpenGL reads with a vertical flip, and sadly there is no optimized // way to get it flipped automatically. - for (int y = 0; y < h; ++y) { + for (int y = 0; y < height_; ++y) { uint8* flip_row = &(flip_buffer[y * bytes_per_row_]); - uint8* current_row = &(current_buffer[(h - (y + 1)) * bytes_per_row_]); + uint8* current_row = + &(current_buffer[(height_ - (y + 1)) * bytes_per_row_]); memcpy(current_row, flip_row, bytes_per_row_); } @@ -114,7 +115,7 @@ void CapturerMac::CaptureRects(const InvalidRects& rects, planes.strides[0] = bytes_per_row_; scoped_refptr<CaptureData> data( - new CaptureData(planes, w, h, pixel_format())); + new CaptureData(planes, width_, height_, pixel_format())); data->mutable_dirty_rects() = rects; FinishCapture(data, callback); } @@ -123,7 +124,7 @@ void CapturerMac::ScreenRefresh(CGRectCount count, const CGRect *rect_array) { InvalidRects rects; for (CGRectCount i = 0; i < count; ++i) { CGRect rect = rect_array[i]; - rect.origin.y = height() - rect.size.height; + rect.origin.y = height_ - rect.size.height; rects.insert(gfx::Rect(rect)); } InvalidateRects(rects); @@ -135,7 +136,7 @@ void CapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta, InvalidRects rects; for (CGRectCount i = 0; i < count; ++i) { CGRect rect = rect_array[i]; - rect.origin.y = height() - rect.size.height; + rect.origin.y = height_ - rect.size.height; rects.insert(gfx::Rect(rect)); rect = CGRectOffset(rect, delta.dX, delta.dY); rects.insert(gfx::Rect(rect)); diff --git a/remoting/host/capturer_mac.h b/remoting/host/capturer_mac.h index eaedb47..7ea8d99 100644 --- a/remoting/host/capturer_mac.h +++ b/remoting/host/capturer_mac.h @@ -44,6 +44,13 @@ class CapturerMac : public Capturer { CGLContextObj cgl_context_; scoped_array<uint8> buffers_[kNumBuffers]; scoped_array<uint8> flip_buffer_; + + // Screen size. + int width_; + int height_; + + int bytes_per_row_; + DISALLOW_COPY_AND_ASSIGN(CapturerMac); }; diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index 9522b5c..5b2a21d 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -299,9 +299,10 @@ void ChromotingHost::OnNewClientSession( return; } + // TODO(simonmorris): The resolution is set in the video stream now, + // so it doesn't need to be set here. *protocol_config_->mutable_initial_resolution() = - protocol::ScreenResolution(desktop_environment_->capturer()->width(), - desktop_environment_->capturer()->height()); + protocol::ScreenResolution(2048, 2048); // TODO(sergeyu): Respect resolution requested by the client if supported. protocol::SessionConfig* config = protocol_config_->Select( session->candidate_config(), true /* force_host_resolution */); diff --git a/remoting/host/differ.h b/remoting/host/differ.h index 8386e7a..a5af54f 100644 --- a/remoting/host/differ.h +++ b/remoting/host/differ.h @@ -27,6 +27,11 @@ class Differ { Differ(int width, int height, int bytes_per_pixel, int stride); ~Differ(); + int width() { return width_; } + int height() { return height_; } + int bytes_per_pixel() { return bytes_per_pixel_; } + int bytes_per_row() { return bytes_per_row_; } + // Given the previous and current screen buffer, calculate the set of // rectangles that enclose all the changed pixels in the new screen. void CalcDirtyRects(const void* prev_buffer, const void* curr_buffer, diff --git a/remoting/host/event_executor_win.cc b/remoting/host/event_executor_win.cc index e9229b2..95da76b 100644 --- a/remoting/host/event_executor_win.cc +++ b/remoting/host/event_executor_win.cc @@ -97,10 +97,14 @@ void EventExecutorWin::HandleMouse(const MouseEvent* event) { INPUT input; input.type = INPUT_MOUSE; input.mi.time = 0; - input.mi.dx = static_cast<int>((x * 65535) / capturer_->width()); - input.mi.dy = static_cast<int>((y * 65535) / capturer_->height()); - input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; - SendInput(1, &input, sizeof(INPUT)); + int screen_width = capturer_->width_most_recent(); + int screen_height = capturer_->height_most_recent(); + if ((screen_width > 0) && (screen_height > 0)) { + input.mi.dx = static_cast<int>((x * 65535) / screen_width); + input.mi.dy = static_cast<int>((y * 65535) / screen_height); + input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; + SendInput(1, &input, sizeof(INPUT)); + } } if (event->has_wheel_offset_x() && event->has_wheel_offset_y()) { diff --git a/remoting/proto/video.proto b/remoting/proto/video.proto index 2dadcb8..245db10 100644 --- a/remoting/proto/video.proto +++ b/remoting/proto/video.proto @@ -30,6 +30,10 @@ message VideoPacketFormat { // The encoding used for this image update. optional Encoding encoding = 5 [default = ENCODING_INVALID]; + + // Width and height of the whole screen. + optional int32 screen_width = 6; + optional int32 screen_height = 7; } // TODO(hclam): Remove this message once we can obtain dirty rects from libvpx. |