diff options
author | kelvinp <kelvinp@chromium.org> | 2014-09-16 19:12:07 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-17 02:12:21 +0000 |
commit | 0215564c6c0225e22419c85e7854c5ce103414c2 (patch) | |
tree | 5179a16de74a74f0eb9d5338d1ab025f50caa89a /remoting | |
parent | a0d3f6781193aca6a48a3ff24577a724d05240e9 (diff) | |
download | chromium_src-0215564c6c0225e22419c85e7854c5ce103414c2.zip chromium_src-0215564c6c0225e22419c85e7854c5ce103414c2.tar.gz chromium_src-0215564c6c0225e22419c85e7854c5ce103414c2.tar.bz2 |
Remote Assistance on Chrome OS - Aura Desktop Capturer
This CL implements a WebRTC Desktop Capturer that captures from
the Aura Shell root window.
The capturer is used by the It2MeHost on Chrome OS.
|DesktopCaptureDeviceAura| cannot be re-used in our scenario
because is timer-driven as opposed to caller driven, which is
required by WebRTC.
The current implementation uses the layer API
desktop_window_->layer()->RequestCopyOfOutput(request.Pass())
to request the layer and its subtree to be rendered to a
|SkiaBitmap|. It then copies the pixels to a WebRTC |DesktopFrame|.
BUG=411530
Committed: https://crrev.com/278b065b519617b1e9231bd53e6502706d20787c
Cr-Commit-Position: refs/heads/master@{#295187}
Review URL: https://codereview.chromium.org/543243003
Cr-Commit-Position: refs/heads/master@{#295214}
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/DEPS | 3 | ||||
-rw-r--r-- | remoting/host/chromeos/aura_desktop_capturer.cc | 119 | ||||
-rw-r--r-- | remoting/host/chromeos/aura_desktop_capturer.h | 53 | ||||
-rw-r--r-- | remoting/host/chromeos/aura_desktop_capturer_unittest.cc | 85 | ||||
-rw-r--r-- | remoting/remoting_host.gypi | 182 | ||||
-rw-r--r-- | remoting/remoting_host_linux.gypi | 2 | ||||
-rw-r--r-- | remoting/remoting_test.gypi | 11 |
7 files changed, 389 insertions, 66 deletions
diff --git a/remoting/host/DEPS b/remoting/host/DEPS index eed4138..66e61fd 100644 --- a/remoting/host/DEPS +++ b/remoting/host/DEPS @@ -1,4 +1,6 @@ include_rules = [ + "+ash", + "+cc/output", "+jingle/glue", "+net", "+remoting/codec", @@ -6,6 +8,7 @@ include_rules = [ "+remoting/signaling", "+third_party/jsoncpp", "+third_party/modp_b64", + "+third_party/skia", "+third_party/webrtc", "+ui", ] diff --git a/remoting/host/chromeos/aura_desktop_capturer.cc b/remoting/host/chromeos/aura_desktop_capturer.cc new file mode 100644 index 0000000..caf6837 --- /dev/null +++ b/remoting/host/chromeos/aura_desktop_capturer.cc @@ -0,0 +1,119 @@ +// Copyright 2014 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/host/chromeos/aura_desktop_capturer.h" + +#include "ash/shell.h" +#include "base/bind.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" + +namespace remoting { + +namespace { + +// DesktopFrame implementation used by screen capture on ChromeOS. +// Frame data is stored in a SkBitmap. +class SkiaBitmapDesktopFrame : public webrtc::DesktopFrame { + public: + static SkiaBitmapDesktopFrame* Create(scoped_ptr<SkBitmap> bitmap); + virtual ~SkiaBitmapDesktopFrame(); + + private: + SkiaBitmapDesktopFrame(webrtc::DesktopSize size, + int stride, + uint8_t* data, + scoped_ptr<SkBitmap> bitmap); + + scoped_ptr<SkBitmap> bitmap_; + + DISALLOW_COPY_AND_ASSIGN(SkiaBitmapDesktopFrame); +}; + +// static +SkiaBitmapDesktopFrame* SkiaBitmapDesktopFrame::Create( + scoped_ptr<SkBitmap> bitmap) { + + webrtc::DesktopSize size(bitmap->width(), bitmap->height()); + DCHECK_EQ(kRGBA_8888_SkColorType, bitmap->info().colorType()) + << "DesktopFrame objects always hold RGBA data."; + + uint8_t* bitmap_data = reinterpret_cast<uint8_t*>(bitmap->getPixels()); + + SkiaBitmapDesktopFrame* result = new SkiaBitmapDesktopFrame( + size, bitmap->rowBytes(), bitmap_data, bitmap.Pass()); + + return result; +} + +SkiaBitmapDesktopFrame::SkiaBitmapDesktopFrame(webrtc::DesktopSize size, + int stride, + uint8_t* data, + scoped_ptr<SkBitmap> bitmap) + : DesktopFrame(size, stride, data, NULL), bitmap_(bitmap.Pass()) { +} + +SkiaBitmapDesktopFrame::~SkiaBitmapDesktopFrame() { +} + +} // namespace + +AuraDesktopCapturer::AuraDesktopCapturer() + : callback_(NULL), desktop_window_(NULL), weak_factory_(this) { +} + +AuraDesktopCapturer::~AuraDesktopCapturer() { +} + +void AuraDesktopCapturer::Start(webrtc::DesktopCapturer::Callback* callback) { + if (ash::Shell::HasInstance()) { + // TODO(kelvinp): Use ash::Shell::GetAllRootWindows() when multiple monitor + // support is implemented. + desktop_window_ = ash::Shell::GetPrimaryRootWindow(); + DCHECK(desktop_window_) << "Failed to retrieve the Aura Shell root window"; + } + + DCHECK(!callback_) << "Start() can only be called once"; + callback_ = callback; + DCHECK(callback_); +} + +void AuraDesktopCapturer::Capture(const webrtc::DesktopRegion&) { + scoped_ptr<cc::CopyOutputRequest> request = + cc::CopyOutputRequest::CreateBitmapRequest( + base::Bind( + &AuraDesktopCapturer::OnFrameCaptured, + weak_factory_.GetWeakPtr())); + + gfx::Rect window_rect(desktop_window_->bounds().size()); + + request->set_area(window_rect); + desktop_window_->layer()->RequestCopyOfOutput(request.Pass()); +} + +void AuraDesktopCapturer::OnFrameCaptured( + scoped_ptr<cc::CopyOutputResult> result) { + DCHECK(result->HasBitmap()); + + scoped_ptr<SkBitmap> bitmap = result->TakeBitmap(); + + scoped_ptr<webrtc::DesktopFrame> frame( + SkiaBitmapDesktopFrame::Create(bitmap.Pass())); + + // |VideoScheduler| will not encode the frame if |updated_region| is empty. + const webrtc::DesktopRect& rect = webrtc::DesktopRect::MakeWH( + frame->size().width(), frame->size().height()); + + // TODO(kelvinp): Set Frame DPI according to the screen resolution. + // See cc::Layer::contents_scale_(x|y)() and frame->set_depi(). + frame->mutable_updated_region()->SetRect(rect); + + callback_->OnCaptureCompleted(frame.release()); +} + +} // namespace remoting diff --git a/remoting/host/chromeos/aura_desktop_capturer.h b/remoting/host/chromeos/aura_desktop_capturer.h new file mode 100644 index 0000000..a76abce --- /dev/null +++ b/remoting/host/chromeos/aura_desktop_capturer.h @@ -0,0 +1,53 @@ +// Copyright 2014 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_HOST_CHROMEOS_AURA_DESKTOP_CAPTURER_H_ +#define REMOTING_HOST_CHROMEOS_AURA_DESKTOP_CAPTURER_H_ + +#include "base/memory/weak_ptr.h" +#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" + +namespace cc { +class CopyOutputResult; +} // namespace cc + +namespace aura { +class Window; +} // namespace aura + +namespace remoting { + +// A webrtc::DesktopCapturer that captures pixels from the root window of the +// Aura Shell. This is implemented by requesting the layer and its substree to +// be rendered to a given data structure. Start() and Capture() must be called +// on the Browser UI thread. +class AuraDesktopCapturer : public webrtc::DesktopCapturer { + public: + AuraDesktopCapturer(); + virtual ~AuraDesktopCapturer(); + + // webrtc::DesktopCapturer implementation. + virtual void Start(webrtc::DesktopCapturer::Callback* callback) OVERRIDE; + virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE; + + private: + friend class AuraDesktopCapturerTest; + + // Called when a copy of the layer is captured. + void OnFrameCaptured(scoped_ptr<cc::CopyOutputResult> result); + + // Points to the callback passed to webrtc::DesktopCapturer::Start(). + webrtc::DesktopCapturer::Callback* callback_; + + // The root window of the Aura Shell. + aura::Window* desktop_window_; + + base::WeakPtrFactory<AuraDesktopCapturer> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AuraDesktopCapturer); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_CHROMEOS_AURA_DESKTOP_CAPTURER_H_ diff --git a/remoting/host/chromeos/aura_desktop_capturer_unittest.cc b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc new file mode 100644 index 0000000..a665012 --- /dev/null +++ b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc @@ -0,0 +1,85 @@ +// Copyright 2014 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/host/chromeos/aura_desktop_capturer.h" + +#include "cc/output/copy_output_result.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" + +using testing::_; +using testing::SaveArg; + +namespace remoting { + +namespace { + +// Test frame data. +const unsigned char frame_data[] = { + 0x00, 0x00, 0x00, 0x9a, 0x65, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x90, + 0x24, 0x71, 0xf8, 0xf2, 0xe5, 0xdf, 0x7f, 0x81, 0xc7, 0x49, 0xc4, 0xa3, + 0x58, 0x5c, 0xf6, 0xcc, 0x40, 0x14, 0x28, 0x0c, 0xa0, 0xfa, 0x03, 0x18, + 0x38, 0xd8, 0x7d, 0x77, 0x2b, 0x3a, 0x00, 0x00, 0x00, 0x20, 0x64, 0x46, + 0x47, 0x2f, 0xdf, 0x6e, 0xed, 0x7b, 0xf3, 0xc3, 0x37, 0x20, 0xf2, 0x36, + 0x67, 0x6c, 0x36, 0xe1, 0xb4, 0x5e, 0xbe, 0x04, 0x85, 0xdb, 0x89, 0xa3, + 0xcd, 0xfd, 0xd2, 0x4b, 0xd6, 0x9f, 0x00, 0x00, 0x00, 0x40, 0x38, 0x35, + 0x05, 0x75, 0x1d, 0x13, 0x6e, 0xb3, 0x6b, 0x1d, 0x29, 0xae, 0xd3, 0x43, + 0xe6, 0x84, 0x8f, 0xa3, 0x9d, 0x65, 0x4e, 0x2f, 0x57, 0xe3, 0xf6, 0xe6, + 0x20, 0x3c, 0x00, 0xc6, 0xe1, 0x73, 0x34, 0xe2, 0x23, 0x99, 0xc4, 0xfa, + 0x91, 0xc2, 0xd5, 0x97, 0xc1, 0x8b, 0xd0, 0x3c, 0x13, 0xba, 0xf0, 0xd7 + }; +} // namespace + +class AuraDesktopCapturerTest : public testing::Test, + public webrtc::DesktopCapturer::Callback { + public: + AuraDesktopCapturerTest() {} + + virtual void SetUp() OVERRIDE; + + MOCK_METHOD1(CreateSharedMemory, webrtc::SharedMemory*(size_t size)); + MOCK_METHOD1(OnCaptureCompleted, void(webrtc::DesktopFrame* frame)); + + protected: + void SimulateFrameCapture() { + scoped_ptr<SkBitmap> bitmap(new SkBitmap()); + const SkImageInfo& info = + SkImageInfo::Make(3, 4, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + bitmap->installPixels(info, const_cast<unsigned char*>(frame_data), 12); + + scoped_ptr<cc::CopyOutputResult> output = + cc::CopyOutputResult::CreateBitmapResult(bitmap.Pass()); + capturer_->OnFrameCaptured(output.Pass()); + } + + scoped_ptr<AuraDesktopCapturer> capturer_; +}; + +void AuraDesktopCapturerTest::SetUp() { + capturer_.reset(new AuraDesktopCapturer()); +} + +TEST_F(AuraDesktopCapturerTest, ConvertSkBitmapToDesktopFrame) { + webrtc::DesktopFrame* captured_frame = NULL; + + EXPECT_CALL(*this, OnCaptureCompleted(_)).Times(1).WillOnce( + SaveArg<0>(&captured_frame)); + capturer_->Start(this); + + SimulateFrameCapture(); + + ASSERT_TRUE(captured_frame != NULL); + uint8_t* captured_data = captured_frame->data(); + EXPECT_EQ( + 0, + memcmp( + frame_data, captured_data, sizeof(frame_data))); + + delete captured_frame; +} + +} // namespace remoting diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi index d7f0e4d..25c1529 100644 --- a/remoting/remoting_host.gypi +++ b/remoting/remoting_host.gypi @@ -12,11 +12,19 @@ 'variables': { 'conditions': [ # Remoting host is supported only on Windows, OSX and Linux (with X11). - ['OS=="win" or OS=="mac" or (OS=="linux" and chromeos==0 and use_x11==1)', { + ['OS=="win" or OS=="mac" or (OS=="linux" and use_x11==1)', { + 'enable_me2me_host': 1, + 'enable_it2me_host': 1, 'enable_remoting_host': 1, }, { + 'enable_me2me_host': 0, + 'enable_it2me_host': 0, 'enable_remoting_host': 0, }], + ['chromeos==1', { + 'enable_me2me_host': 0, + 'enable_it2me_host': 0, + }], ], }, @@ -64,6 +72,8 @@ 'host/branding.h', 'host/capture_scheduler.cc', 'host/capture_scheduler.h', + 'host/chromeos/aura_desktop_capturer.cc', + 'host/chromeos/aura_desktop_capturer.h', 'host/chromium_port_allocator_factory.cc', 'host/chromium_port_allocator_factory.h', 'host/chromoting_host.cc', @@ -287,8 +297,6 @@ 'conditions': [ ['OS=="linux"', { 'dependencies': [ - # Always use GTK on Linux, even for Aura builds. - '../build/linux/system.gyp:gtk', '../build/linux/system.gyp:x11', '../build/linux/system.gyp:xext', '../build/linux/system.gyp:xfixes', @@ -302,6 +310,39 @@ ], }, }], + ['OS=="linux" and chromeos==0', { + 'dependencies' : [ + # Always use GTK on Linux, even for Aura builds. + '../build/linux/system.gyp:gtk', + ], + }], + ['chromeos==1', { + 'dependencies' : [ + '../ash/ash.gyp:ash', + '../cc/cc.gyp:cc', + '../content/content.gyp:content', + '../ppapi/ppapi_internal.gyp:ppapi_host', + '../skia/skia.gyp:skia', + '../ui/aura/aura.gyp:aura', + '../ui/compositor/compositor.gyp:compositor', + ], + 'include_dirs': [ + '../third_party/skia/include/utils', + ], + 'sources!' : [ + 'host/continue_window.cc', + 'host/continue_window.h', + 'host/continue_window_linux.cc', + 'host/disconnect_window.cc', + 'host/disconnect_window_linux.cc', + 'host/remoting_me2me_host.cc', + ] + }, { # chromeos==0 + 'sources!' : [ + 'host/chromeos/aura_desktop_capturer.cc', + 'host/chromeos/aura_desktop_capturer.h', + ], + }], ['OS=="mac"', { 'dependencies': [ '../third_party/google_toolbox_for_mac/google_toolbox_for_mac.gyp:google_toolbox_for_mac', @@ -398,40 +439,6 @@ }, # end of target 'remoting_native_messaging_base' { - 'target_name': 'remoting_me2me_host_static', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'dependencies': [ - '../base/base.gyp:base', - '../base/base.gyp:base_i18n', - '../net/net.gyp:net', - '../third_party/webrtc/modules/modules.gyp:desktop_capture', - 'remoting_base', - 'remoting_breakpad', - 'remoting_host', - 'remoting_protocol', - ], - 'defines': [ - 'VERSION=<(version_full)', - ], - 'sources': [ - 'host/curtain_mode.h', - 'host/curtain_mode_linux.cc', - 'host/curtain_mode_mac.cc', - 'host/curtain_mode_win.cc', - 'host/posix/signal_handler.cc', - 'host/posix/signal_handler.h', - ], - 'conditions': [ - ['os_posix != 1', { - 'sources/': [ - ['exclude', '^host/posix/'], - ], - }], - ], # end of 'conditions' - }, # end of target 'remoting_me2me_host_static' - - { 'target_name': 'remoting_host_setup_base', 'type': 'static_library', 'variables': { 'enable_wexit_time_destructors': 1, }, @@ -481,30 +488,6 @@ ], }, # end of target 'remoting_host_setup_base' - { - 'target_name': 'remoting_it2me_host_static', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'dependencies': [ - '../base/base.gyp:base_i18n', - '../net/net.gyp:net', - 'remoting_base', - 'remoting_host', - 'remoting_infoplist_strings', - 'remoting_protocol', - 'remoting_resources', - ], - 'defines': [ - 'VERSION=<(version_full)', - ], - 'sources': [ - 'host/it2me/it2me_host.cc', - 'host/it2me/it2me_host.h', - 'host/it2me/it2me_native_messaging_host.cc', - 'host/it2me/it2me_native_messaging_host.h', - ], - }, # end of target 'remoting_it2me_host_static' - # Generates native messaging manifest files. { 'target_name': 'remoting_native_messaging_manifests', @@ -617,7 +600,73 @@ ], # end of 'targets' }], # 'enable_remoting_host==1' - ['OS!="win" and enable_remoting_host==1', { + ['enable_me2me_host==1', { + 'targets': [ + { + 'target_name': 'remoting_me2me_host_static', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'dependencies': [ + '../base/base.gyp:base', + '../base/base.gyp:base_i18n', + '../net/net.gyp:net', + '../third_party/webrtc/modules/modules.gyp:desktop_capture', + 'remoting_base', + 'remoting_breakpad', + 'remoting_host', + 'remoting_protocol', + ], + 'defines': [ + 'VERSION=<(version_full)', + ], + 'sources': [ + 'host/curtain_mode.h', + 'host/curtain_mode_linux.cc', + 'host/curtain_mode_mac.cc', + 'host/curtain_mode_win.cc', + 'host/posix/signal_handler.cc', + 'host/posix/signal_handler.h', + ], + 'conditions': [ + ['os_posix != 1', { + 'sources/': [ + ['exclude', '^host/posix/'], + ], + }], + ], # end of 'conditions' + }, # end of target 'remoting_me2me_host_static' + ] # end of targets + }], # end of enable_me2me_host==1 + + ['enable_it2me_host==1', { + 'targets': [ + { + 'target_name': 'remoting_it2me_host_static', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'dependencies': [ + '../base/base.gyp:base_i18n', + '../net/net.gyp:net', + 'remoting_base', + 'remoting_host', + 'remoting_infoplist_strings', + 'remoting_protocol', + 'remoting_resources', + ], + 'defines': [ + 'VERSION=<(version_full)', + ], + 'sources': [ + 'host/it2me/it2me_host.cc', + 'host/it2me/it2me_host.h', + 'host/it2me/it2me_native_messaging_host.cc', + 'host/it2me/it2me_native_messaging_host.h', + ], + }, # end of target 'remoting_it2me_host_static' + ] # end of targets + }], # end of 'enable_it2me_host==1' + + ['OS!="win" and enable_me2me_host==1', { 'targets': [ { 'target_name': 'remoting_me2me_host', @@ -795,6 +844,11 @@ }], # OS=mac ], }, # end of target 'remoting_me2me_native_messaging_host' + ], # targets + }], # end of OS!="win" and enable_me2me_host==1 + + ['OS!="win" and enable_it2me_host==1', { + 'targets': [ { 'target_name': 'remoting_it2me_native_messaging_host', 'type': 'executable', @@ -818,7 +872,7 @@ 'host/it2me/it2me_native_messaging_host_main.h', ], 'conditions': [ - ['OS=="linux"', { + ['OS=="linux" and chromeos==0', { 'dependencies': [ # Always use GTK on Linux, even for Aura builds. '../build/linux/system.gyp:gtk', @@ -890,7 +944,7 @@ ], }, # end of target 'remoting_it2me_native_messaging_host' ], # end of 'targets' - }], # OS!="win" + }], # # end of OS!="win" and enable_it2me_host==1 ], # end of 'conditions' } diff --git a/remoting/remoting_host_linux.gypi b/remoting/remoting_host_linux.gypi index 843b5bc..31bbc94 100644 --- a/remoting/remoting_host_linux.gypi +++ b/remoting/remoting_host_linux.gypi @@ -4,7 +4,7 @@ { 'conditions': [ - ['OS=="linux" and branding=="Chrome" and enable_remoting_host==1', { + ['OS=="linux" and branding=="Chrome" and enable_remoting_host==1 and chromeos==0', { 'variables': { 'build_deb_script': 'host/installer/linux/build-deb.sh', 'deb_filename': 'host/installer/<!(["<(build_deb_script)", "-p", "-s", "<(DEPTH)"])', diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index 0b90f47..3bd537a 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi @@ -131,6 +131,7 @@ 'host/branding.cc', 'host/branding.h', 'host/capture_scheduler_unittest.cc', + 'host/chromeos/aura_desktop_capturer_unittest.cc', 'host/chromoting_host_context_unittest.cc', 'host/chromoting_host_unittest.cc', 'host/client_session_unittest.cc', @@ -242,13 +243,13 @@ [ 'chromeos==0', { 'sources!': [ 'client/plugin/normalizing_input_filter_cros_unittest.cc', + 'host/chromeos/aura_desktop_capturer_unittest.cc', ], }], ['enable_remoting_host == 0', { 'dependencies!': [ 'remoting_host', 'remoting_host_setup_base', - 'remoting_it2me_host_static', 'remoting_native_messaging_base', ], 'sources/': [ @@ -257,6 +258,14 @@ ['exclude', '^base/resources_unittest\\.cc$'], ] }], + ['enable_it2me_host == 0', { + 'dependencies!': [ + 'remoting_it2me_host_static', + ], + 'sources/': [ + ['exclude', '^host/it2me/'], + ] + }], [ 'OS == "linux" and use_allocator!="none"', { 'dependencies': [ '../base/allocator/allocator.gyp:allocator', |