Commit b2afe031 authored by Yuri Wiitala's avatar Yuri Wiitala Committed by Commit Bot

New Desktop/Browser Window screen capture impl, based on VIZ services.

Adds content::AuraWindowVideoCaptureDevice, which provides screen
capture of aura::Windows that have compositor frame sinks associated
with them. Then, for Chrome OS browser window capture, where a CFS is
not available, use a fall-back window capturer impl instead. The
intention is for the LameWindowCapturerChromeOS to be a temporary
solution until more work is complete on services/ui/ws (the new Window
Service).

Note: This change does not activate the new impl yet. A follow-up
change will switch from the legacy impl to the new impl.

Bug: 806366
Change-Id: Ia6aa81e4addde603b120a6ab61c5e35a0041b418
Reviewed-on: https://chromium-review.googlesource.com/1006366
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: default avatarAntoine Labour <piman@chromium.org>
Reviewed-by: default avatarAdam Parker <amp@chromium.org>
Reviewed-by: default avatarXiangjun Zhang <xjz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#559371}
parent b63383e4
...@@ -1867,12 +1867,20 @@ jumbo_source_set("browser") { ...@@ -1867,12 +1867,20 @@ jumbo_source_set("browser") {
sources += [ sources += [
"media/capture/aura_window_capture_machine.cc", "media/capture/aura_window_capture_machine.cc",
"media/capture/aura_window_capture_machine.h", "media/capture/aura_window_capture_machine.h",
"media/capture/aura_window_video_capture_device.cc",
"media/capture/aura_window_video_capture_device.h",
"media/capture/cursor_renderer_aura.cc", "media/capture/cursor_renderer_aura.cc",
"media/capture/cursor_renderer_aura.h", "media/capture/cursor_renderer_aura.h",
"media/capture/desktop_capture_device_aura.cc", "media/capture/desktop_capture_device_aura.cc",
"media/capture/desktop_capture_device_aura.h", "media/capture/desktop_capture_device_aura.h",
] ]
} }
if (is_chromeos) {
sources += [
"media/capture/lame_window_capturer_chromeos.cc",
"media/capture/lame_window_capturer_chromeos.h",
]
}
if (is_mac) { if (is_mac) {
sources += [ sources += [
"media/capture/cursor_renderer_mac.h", "media/capture/cursor_renderer_mac.h",
......
// Copyright 2018 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 "content/browser/media/capture/aura_window_video_capture_device.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "media/base/bind_to_current_loop.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#if defined(OS_CHROMEOS)
#include "content/browser/media/capture/lame_window_capturer_chromeos.h"
#endif
namespace content {
// Threading note: This is constructed on the device thread, while the
// destructor and the rest of the class will run exclusively on the UI thread.
class AuraWindowVideoCaptureDevice::WindowTracker
: public aura::WindowObserver,
public base::SupportsWeakPtr<
AuraWindowVideoCaptureDevice::WindowTracker> {
public:
WindowTracker(base::WeakPtr<AuraWindowVideoCaptureDevice> device,
CursorRenderer* cursor_renderer,
const DesktopMediaID& source_id)
: device_(std::move(device)),
device_task_runner_(base::ThreadTaskRunnerHandle::Get()),
cursor_renderer_(cursor_renderer),
target_type_(source_id.type) {
DCHECK(device_task_runner_);
DCHECK(cursor_renderer_);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&WindowTracker::ResolveTarget, AsWeakPtr(), source_id));
}
~WindowTracker() final {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (target_window_) {
target_window_->RemoveObserver(this);
}
}
DesktopMediaID::Type target_type() const { return target_type_; }
aura::Window* target_window() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return target_window_;
}
private:
// Determines which frame sink and aura::Window should be targeted for capture
// and notifies the device.
void ResolveTarget(const DesktopMediaID& source_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Since ResolveTarget() should only ever be called once, expect
// |target_window_| to be null at this point.
DCHECK(!target_window_);
target_window_ = DesktopMediaID::GetAuraWindowById(source_id);
if (target_window_ &&
#if defined(OS_CHROMEOS)
// See class comments for LameWindowCapturerChromeOS.
(source_id.type == DesktopMediaID::TYPE_WINDOW ||
target_window_->GetFrameSinkId().is_valid()) &&
#else
target_window_->GetFrameSinkId().is_valid() &&
#endif
true) {
target_window_->AddObserver(this);
device_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FrameSinkVideoCaptureDevice::OnTargetChanged, device_,
target_window_->GetFrameSinkId()));
// Note: CursorRenderer runs on the UI thread. It's also important that
// SetTargetView() be called in the current stack while |target_window_|
// is known to be a valid pointer. http://crbug.com/818679
cursor_renderer_->SetTargetView(target_window_);
} else {
device_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FrameSinkVideoCaptureDevice::OnTargetPermanentlyLost,
device_));
}
}
// aura::WindowObserver override.
void OnWindowDestroying(aura::Window* window) final {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(window, target_window_);
target_window_->RemoveObserver(this);
target_window_ = nullptr;
device_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FrameSinkVideoCaptureDevice::OnTargetPermanentlyLost,
device_));
cursor_renderer_->SetTargetView(nullptr);
}
private:
// |device_| may be dereferenced only by tasks run by |device_task_runner_|.
const base::WeakPtr<FrameSinkVideoCaptureDevice> device_;
const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
// Owned by FrameSinkVideoCaptureDevice. This will be valid for the life of
// WindowTracker because the WindowTracker deleter task will be posted to the
// UI thread before the CursorRenderer deleter task.
CursorRenderer* const cursor_renderer_;
const DesktopMediaID::Type target_type_;
aura::Window* target_window_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(WindowTracker);
};
AuraWindowVideoCaptureDevice::AuraWindowVideoCaptureDevice(
const DesktopMediaID& source_id)
: tracker_(new WindowTracker(AsWeakPtr(), cursor_renderer(), source_id)) {}
AuraWindowVideoCaptureDevice::~AuraWindowVideoCaptureDevice() = default;
#if defined(OS_CHROMEOS)
void AuraWindowVideoCaptureDevice::CreateCapturer(
CreatedCapturerCallback callback) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
[](base::WeakPtr<WindowTracker> tracker_ptr,
CreatedCapturerCallback callback) {
WindowTracker* const tracker = tracker_ptr.get();
if (!tracker) {
// WindowTracker was destroyed in the meantime, due to early
// shutdown.
std::move(callback).Run(nullptr);
return;
}
if (tracker->target_type() == DesktopMediaID::TYPE_WINDOW) {
VLOG(1) << "AuraWindowVideoCaptureDevice is using the LAME "
"capturer. :(";
viz::mojom::FrameSinkVideoCapturerPtr capturer;
mojo::StrongBinding<viz::mojom::FrameSinkVideoCapturer>::Create(
std::make_unique<LameWindowCapturerChromeOS>(
tracker->target_window()),
mojo::MakeRequest(&capturer));
std::move(callback).Run(capturer.PassInterface());
} else {
VLOG(1) << "AuraWindowVideoCaptureDevice is using the frame "
"sink capturer. :)";
CreateCapturerViaGlobalManager(std::move(callback));
}
},
tracker_->AsWeakPtr(),
media::BindToCurrentLoop(std::move(callback))));
}
#endif
} // namespace content
// Copyright 2018 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 CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_VIDEO_CAPTURE_DEVICE_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_VIDEO_CAPTURE_DEVICE_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "content/browser/media/capture/frame_sink_video_capture_device.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
namespace aura {
class Window;
} // namespace aura
namespace content {
struct DesktopMediaID;
// Captures the displayed contents of an aura::Window, producing a stream of
// video frames.
class CONTENT_EXPORT AuraWindowVideoCaptureDevice
: public FrameSinkVideoCaptureDevice,
public base::SupportsWeakPtr<AuraWindowVideoCaptureDevice> {
public:
explicit AuraWindowVideoCaptureDevice(const DesktopMediaID& source_id);
~AuraWindowVideoCaptureDevice() final;
#if defined(OS_CHROMEOS)
protected:
// Overrides FrameSinkVideoCaptureDevice::CreateCapturer() to create a
// LameWindowCapturerChromeOS for window capture where compositor frame sinks
// are not present. See class comments for LameWindowCapturerChromeOS for
// further details.
void CreateCapturer(CreatedCapturerCallback callback) final;
#endif
private:
// Monitors the target Window and notifies the base class if it is destroyed.
class WindowTracker;
// A helper that runs on the UI thread to monitor the target aura::Window, and
// post a notification if it is destroyed.
const std::unique_ptr<WindowTracker, BrowserThread::DeleteOnUIThread>
tracker_;
DISALLOW_COPY_AND_ASSIGN(AuraWindowVideoCaptureDevice);
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_VIDEO_CAPTURE_DEVICE_H_
...@@ -38,7 +38,7 @@ ContentCaptureDeviceBrowserTestBase::~ContentCaptureDeviceBrowserTestBase() = ...@@ -38,7 +38,7 @@ ContentCaptureDeviceBrowserTestBase::~ContentCaptureDeviceBrowserTestBase() =
default; default;
void ContentCaptureDeviceBrowserTestBase::ChangePageContentColor( void ContentCaptureDeviceBrowserTestBase::ChangePageContentColor(
std::string css_color_hex) { SkColor color) {
// See the HandleRequest() method for the original documents being modified // See the HandleRequest() method for the original documents being modified
// here. // here.
std::string script; std::string script;
...@@ -51,7 +51,9 @@ void ContentCaptureDeviceBrowserTestBase::ChangePageContentColor( ...@@ -51,7 +51,9 @@ void ContentCaptureDeviceBrowserTestBase::ChangePageContentColor(
} else { } else {
script = "document.body.style.backgroundColor = '#123456';"; script = "document.body.style.backgroundColor = '#123456';";
} }
script.replace(script.find("123456"), 6, css_color_hex); script.replace(script.find("123456"), 6,
base::StringPrintf("%02x%02x%02x", SkColorGetR(color),
SkColorGetG(color), SkColorGetB(color)));
CHECK(ExecuteScript(shell()->web_contents(), script)); CHECK(ExecuteScript(shell()->web_contents(), script));
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "content/browser/media/capture/fake_video_capture_stack.h" #include "content/browser/media/capture/fake_video_capture_stack.h"
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
#include "media/capture/video_capture_types.h" #include "media/capture/video_capture_types.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
namespace net { namespace net {
...@@ -41,7 +42,7 @@ class ContentCaptureDeviceBrowserTestBase : public ContentBrowserTest { ...@@ -41,7 +42,7 @@ class ContentCaptureDeviceBrowserTestBase : public ContentBrowserTest {
// Alters the solid fill color making up the page content. This will trigger a // Alters the solid fill color making up the page content. This will trigger a
// compositor update, which will trigger a frame capture. // compositor update, which will trigger a frame capture.
void ChangePageContentColor(std::string css_color_hex); void ChangePageContentColor(SkColor color);
// Returns the captured source size, but also sanity-checks that it is not // Returns the captured source size, but also sanity-checks that it is not
// changing during the test. Prefer to use this method instead of // changing during the test. Prefer to use this method instead of
......
...@@ -33,18 +33,6 @@ std::unique_ptr<T, BrowserThread::DeleteOnUIThread> RescopeToUIThread( ...@@ -33,18 +33,6 @@ std::unique_ptr<T, BrowserThread::DeleteOnUIThread> RescopeToUIThread(
return std::unique_ptr<T, BrowserThread::DeleteOnUIThread>(ptr.release()); return std::unique_ptr<T, BrowserThread::DeleteOnUIThread>(ptr.release());
} }
// Sets up a mojo message pipe and requests the HostFrameSinkManager create a
// new capturer instance bound to it. Returns the client-side interface.
viz::mojom::FrameSinkVideoCapturerPtrInfo CreateCapturer() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
viz::HostFrameSinkManager* const manager = GetHostFrameSinkManager();
DCHECK(manager);
viz::mojom::FrameSinkVideoCapturerPtr capturer;
manager->CreateVideoCapturer(mojo::MakeRequest(&capturer));
return capturer.PassInterface();
}
// Adapter for a VideoFrameReceiver to notify once frame consumption is // Adapter for a VideoFrameReceiver to notify once frame consumption is
// complete. VideoFrameReceiver requires owning an object that it will destroy // complete. VideoFrameReceiver requires owning an object that it will destroy
// once consumption is complete. This class adapts between that scheme and // once consumption is complete. This class adapts between that scheme and
...@@ -61,8 +49,7 @@ class ScopedFrameDoneHelper ...@@ -61,8 +49,7 @@ class ScopedFrameDoneHelper
} // namespace } // namespace
FrameSinkVideoCaptureDevice::FrameSinkVideoCaptureDevice() FrameSinkVideoCaptureDevice::FrameSinkVideoCaptureDevice()
: capturer_creator_(base::BindRepeating(&CreateCapturer)), : binding_(this),
binding_(this),
cursor_renderer_(RescopeToUIThread(CursorRenderer::Create( cursor_renderer_(RescopeToUIThread(CursorRenderer::Create(
CursorRenderer::CURSOR_DISPLAYED_ON_MOUSE_MOVEMENT))), CursorRenderer::CURSOR_DISPLAYED_ON_MOUSE_MOVEMENT))),
weak_factory_(this) { weak_factory_(this) {
...@@ -104,12 +91,8 @@ void FrameSinkVideoCaptureDevice::AllocateAndStartWithReceiver( ...@@ -104,12 +91,8 @@ void FrameSinkVideoCaptureDevice::AllocateAndStartWithReceiver(
&FrameSinkVideoCaptureDevice::RequestRefreshFrame, &FrameSinkVideoCaptureDevice::RequestRefreshFrame,
weak_factory_.GetWeakPtr())))); weak_factory_.GetWeakPtr()))));
// Hop to the UI thread to request a Mojo connection to a new capturer CreateCapturer(base::BindOnce(&FrameSinkVideoCaptureDevice::OnCapturerCreated,
// instance, and then hop back to the device thread to start using it. weak_factory_.GetWeakPtr()));
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::UI, FROM_HERE, base::BindOnce(capturer_creator_),
base::BindOnce(&FrameSinkVideoCaptureDevice::OnCapturerCreated,
weak_factory_.GetWeakPtr()));
} }
void FrameSinkVideoCaptureDevice::AllocateAndStart( void FrameSinkVideoCaptureDevice::AllocateAndStart(
...@@ -288,15 +271,32 @@ void FrameSinkVideoCaptureDevice::OnTargetPermanentlyLost() { ...@@ -288,15 +271,32 @@ void FrameSinkVideoCaptureDevice::OnTargetPermanentlyLost() {
OnFatalError("Capture target has been permanently lost."); OnFatalError("Capture target has been permanently lost.");
} }
void FrameSinkVideoCaptureDevice::SetCapturerCreatorForTesting(
CapturerCreatorCallback creator) {
capturer_creator_ = std::move(creator);
}
void FrameSinkVideoCaptureDevice::WillStart() {} void FrameSinkVideoCaptureDevice::WillStart() {}
void FrameSinkVideoCaptureDevice::DidStop() {} void FrameSinkVideoCaptureDevice::DidStop() {}
void FrameSinkVideoCaptureDevice::CreateCapturer(
CreatedCapturerCallback callback) {
CreateCapturerViaGlobalManager(std::move(callback));
}
// static
void FrameSinkVideoCaptureDevice::CreateCapturerViaGlobalManager(
CreatedCapturerCallback callback) {
// Hop to the UI thread to request a Mojo connection to a new capturer
// instance, then hop back to the device thread to deliver the client-side
// interface via |callback|.
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::UI, FROM_HERE, base::BindOnce([]() {
viz::HostFrameSinkManager* const manager = GetHostFrameSinkManager();
DCHECK(manager);
viz::mojom::FrameSinkVideoCapturerPtr capturer;
manager->CreateVideoCapturer(mojo::MakeRequest(&capturer));
return capturer.PassInterface();
}),
std::move(callback));
}
void FrameSinkVideoCaptureDevice::OnCapturerCreated( void FrameSinkVideoCaptureDevice::OnCapturerCreated(
viz::mojom::FrameSinkVideoCapturerPtrInfo info) { viz::mojom::FrameSinkVideoCapturerPtrInfo info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -309,6 +309,11 @@ void FrameSinkVideoCaptureDevice::OnCapturerCreated( ...@@ -309,6 +309,11 @@ void FrameSinkVideoCaptureDevice::OnCapturerCreated(
MaybeStopConsuming(); MaybeStopConsuming();
capturer_.reset(); capturer_.reset();
if (!info) {
OnFatalError("Failed to create/connect to the replacement capturer.");
return;
}
// Bind and configure the new capturer. // Bind and configure the new capturer.
capturer_.Bind(std::move(info)); capturer_.Bind(std::move(info));
// TODO(miu): Remove this once HostFrameSinkManager will notify this // TODO(miu): Remove this once HostFrameSinkManager will notify this
......
...@@ -43,9 +43,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice ...@@ -43,9 +43,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice
: public media::VideoCaptureDevice, : public media::VideoCaptureDevice,
public viz::mojom::FrameSinkVideoConsumer { public viz::mojom::FrameSinkVideoConsumer {
public: public:
using CapturerCreatorCallback =
base::RepeatingCallback<viz::mojom::FrameSinkVideoCapturerPtrInfo()>;
FrameSinkVideoCaptureDevice(); FrameSinkVideoCaptureDevice();
~FrameSinkVideoCaptureDevice() override; ~FrameSinkVideoCaptureDevice() override;
...@@ -87,9 +84,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice ...@@ -87,9 +84,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice
void OnTargetChanged(const viz::FrameSinkId& frame_sink_id); void OnTargetChanged(const viz::FrameSinkId& frame_sink_id);
void OnTargetPermanentlyLost(); void OnTargetPermanentlyLost();
// Overrides the callback that is run to create the capturer.
void SetCapturerCreatorForTesting(CapturerCreatorCallback creator);
protected: protected:
CursorRenderer* cursor_renderer() const { return cursor_renderer_.get(); } CursorRenderer* cursor_renderer() const { return cursor_renderer_.get(); }
...@@ -97,6 +91,17 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice ...@@ -97,6 +91,17 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice
virtual void WillStart(); virtual void WillStart();
virtual void DidStop(); virtual void DidStop();
// Creates a capturer and then runs the given |callback| to deliver the
// client-side interface. The default implementation calls
// CreateCapturerViaGlobalManager(), but subclasses and/or tests may provide
// alternatives.
using CreatedCapturerCallback =
base::OnceCallback<void(viz::mojom::FrameSinkVideoCapturerPtrInfo)>;
virtual void CreateCapturer(CreatedCapturerCallback callback);
// Creates a capturer using the global viz::HostFrameSinkManager.
static void CreateCapturerViaGlobalManager(CreatedCapturerCallback callback);
private: private:
using BufferId = decltype(media::VideoCaptureDevice::Client::Buffer::id); using BufferId = decltype(media::VideoCaptureDevice::Client::Buffer::id);
...@@ -135,11 +140,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice ...@@ -135,11 +140,6 @@ class CONTENT_EXPORT FrameSinkVideoCaptureDevice
// cleared by StopAndDeAllocate(). // cleared by StopAndDeAllocate().
std::unique_ptr<media::VideoFrameReceiver> receiver_; std::unique_ptr<media::VideoFrameReceiver> receiver_;
// Callback that is run to request a capturer be created and returns the
// client-side interface. This callback will be run on the UI BrowserThread.
// The constructor provides a default, but unit tests can override this.
CapturerCreatorCallback capturer_creator_;
// Mojo pointer to the capturer instance in VIZ. // Mojo pointer to the capturer instance in VIZ.
viz::mojom::FrameSinkVideoCapturerPtr capturer_; viz::mojom::FrameSinkVideoCapturerPtr capturer_;
......
...@@ -236,6 +236,31 @@ class MockVideoFrameReceiver : public media::VideoFrameReceiver { ...@@ -236,6 +236,31 @@ class MockVideoFrameReceiver : public media::VideoFrameReceiver {
base::flat_map<int, media::mojom::VideoFrameInfoPtr> frame_infos_; base::flat_map<int, media::mojom::VideoFrameInfoPtr> frame_infos_;
}; };
// A FrameSinkVideoCaptureDevice, but with CreateCapturer() overridden to bind
// to a MockFrameSinkVideoCapturer instead of the real thing.
class FrameSinkVideoCaptureDeviceForTest : public FrameSinkVideoCaptureDevice {
public:
explicit FrameSinkVideoCaptureDeviceForTest(
MockFrameSinkVideoCapturer* capturer)
: capturer_(capturer) {}
protected:
void CreateCapturer(CreatedCapturerCallback callback) final {
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
[](FrameSinkVideoCaptureDeviceForTest* self) {
viz::mojom::FrameSinkVideoCapturerPtr capturer_ptr;
self->capturer_->Bind(mojo::MakeRequest(&capturer_ptr));
return capturer_ptr.PassInterface();
},
this),
std::move(callback));
}
MockFrameSinkVideoCapturer* const capturer_;
};
// Convenience macros to make a non-blocking FrameSinkVideoCaptureDevice method // Convenience macros to make a non-blocking FrameSinkVideoCaptureDevice method
// call on the device thread. // call on the device thread.
#define POST_DEVICE_METHOD_CALL0(method) \ #define POST_DEVICE_METHOD_CALL0(method) \
...@@ -258,21 +283,11 @@ class FrameSinkVideoCaptureDeviceTest : public testing::Test { ...@@ -258,21 +283,11 @@ class FrameSinkVideoCaptureDeviceTest : public testing::Test {
// until complete. // until complete.
POST_DEVICE_TASK(base::BindOnce( POST_DEVICE_TASK(base::BindOnce(
[](FrameSinkVideoCaptureDeviceTest* test) { [](FrameSinkVideoCaptureDeviceTest* test) {
test->device_ = std::make_unique<FrameSinkVideoCaptureDevice>(); test->device_ = std::make_unique<FrameSinkVideoCaptureDeviceForTest>(
&test->capturer_);
}, },
this)); this));
WAIT_FOR_DEVICE_TASKS(); WAIT_FOR_DEVICE_TASKS();
// Set an override to "create" the mock capturer instance instead of the
// real thing.
device_->SetCapturerCreatorForTesting(base::BindRepeating(
[](MockFrameSinkVideoCapturer* capturer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
viz::mojom::FrameSinkVideoCapturerPtr capturer_ptr;
capturer->Bind(mojo::MakeRequest(&capturer_ptr));
return capturer_ptr.PassInterface();
},
&capturer_));
} }
void TearDown() override { void TearDown() override {
......
This diff is collapsed.
// Copyright 2018 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 CONTENT_BROWSER_MEDIA_CAPTURE_LAME_WINDOW_CAPTURER_CHROMEOS_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_LAME_WINDOW_CAPTURER_CHROMEOS_H_
#include <utility>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
#include "media/base/video_frame.h"
#include "mojo/public/cpp/system/buffer.h"
#include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/gfx/geometry/size.h"
namespace viz {
class CopyOutputResult;
}
namespace content {
// A minimal FrameSinkVideoCapturer implementation for aura::Window video
// capture on ChromeOS (i.e., not desktop capture, and not WebContents capture),
// in cases where a Window does not host a compositor frame sink. This is far
// less efficient than, and far under-performs, the normal
// FrameSinkVideoCapturer provided by the VIZ service, as it lacks multiple
// design features that would be required for low CPU use and high
// pixels-per-sec throughput. It is a placeholder, until the necessary
// infrastructure exists to provide VIZ FrameSinkVideoCapturer the compositor
// frame sink it needs for aura::Windows in the middle of the window tree
// hierarchy.
//
// As this is not meant to be a full-fledged, long-term implementation, it only
// supports the production of I420-format video (Rec. 709 color space) at a
// maximum rate of 5 FPS, and only a maximum of 3 frames can be in-flight at any
// one time. In addition, since source content changes cannot be detected, this
// capturer indefinitely produces frames at a constant framerate while it is
// running.
//
// TODO(crbug/806366): The goal is to remove this code by 2019.
class LameWindowCapturerChromeOS : public viz::mojom::FrameSinkVideoCapturer,
public aura::WindowObserver {
public:
explicit LameWindowCapturerChromeOS(aura::Window* target);
~LameWindowCapturerChromeOS() final;
// viz::mojom::FrameSinkVideoCapturer implementation.
void SetFormat(media::VideoPixelFormat format,
media::ColorSpace color_space) final;
void SetMinCapturePeriod(base::TimeDelta min_capture_period) final;
void SetMinSizeChangePeriod(base::TimeDelta min_period) final;
void SetResolutionConstraints(const gfx::Size& min_size,
const gfx::Size& max_size,
bool use_fixed_aspect_ratio) final;
void SetAutoThrottlingEnabled(bool enabled) final;
void ChangeTarget(const viz::FrameSinkId& frame_sink_id) final;
void Start(viz::mojom::FrameSinkVideoConsumerPtr consumer) final;
void Stop() final;
void RequestRefreshFrame() final;
private:
// Represents an in-flight frame, being populated by this capturer and then
// delivered to the consumer. When the consumer is done with the frame, this
// returns the buffer back to the pool.
class InFlightFrame;
// Initiates capture of the next frame. This is called periodically by the
// |timer_|.
void CaptureNextFrame();
// Populates the frame from the CopyOutputResult and then calls DeliverFrame.
void DidCopyFrame(std::unique_ptr<InFlightFrame> in_flight_frame,
std::unique_ptr<viz::CopyOutputResult> result);
// Delivers the frame to the consumer, and sets up the notification path for
// when the consumer is done with the frame.
void DeliverFrame(std::unique_ptr<InFlightFrame> in_flight_frame);
// aura::WindowObserver override.
void OnWindowDestroying(aura::Window* window) final;
// The window being captured. If the window is destroyed, this is set to null
// and only blank black frames will be produced thereafter.
aura::Window* target_;
// Capture parameters. The defaults are according to the mojo interface
// definition comments for viz::mojom::FrameSinkVideoCapturer.
base::TimeDelta capture_period_ = kAbsoluteMinCapturePeriod;
gfx::Size capture_size_ = gfx::Size(640, 360);
// The current consumer. This is set by Start() and cleared by Stop().
viz::mojom::FrameSinkVideoConsumerPtr consumer_;
// A timer that calls CaptureNextFrame() periodically, according to the
// currently-set |capture_period_|. This timer is only running while a
// consumer is present.
base::RepeatingTimer timer_;
// A pool of shared memory buffers for re-use.
using BufferAndSize = std::pair<mojo::ScopedSharedBufferHandle, size_t>;
std::vector<BufferAndSize> buffer_pool_;
// The current number of frames in-flight. If incrementing this would be
// exceed kMaxInFlightFrames, frame capture is not attempted.
int in_flight_count_ = 0;
// Tick clock time of the first frame since Start() was called. This is used
// for generating "media offset" VideoFrame timestamps.
base::TimeTicks first_frame_reference_time_;
// A value provided in the copy requests to enable VIZ to optimize around
// video capture.
const base::UnguessableToken copy_request_source_;
// Used for cancelling any outstanding activities' results, once Stop() is
// called and there is no longer a consumer to receive another frame.
base::WeakPtrFactory<LameWindowCapturerChromeOS> weak_factory_;
// Enforce a very low maximum frame rate (5 FPS), due to the lack of
// design optimizations. See top-level class comments.
static constexpr base::TimeDelta kAbsoluteMinCapturePeriod =
base::TimeDelta::FromMilliseconds(200);
// The maximum number of frames in-flight at any one time.
static constexpr int kMaxFramesInFlight = 3;
DISALLOW_COPY_AND_ASSIGN(LameWindowCapturerChromeOS);
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CAPTURE_LAME_WINDOW_CAPTURER_CHROMEOS_H_
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "media/base/video_util.h" #include "media/base/video_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/rect_f.h"
...@@ -207,7 +206,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, ...@@ -207,7 +206,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
AllocateAndStartAndWaitForFirstFrame(); AllocateAndStartAndWaitForFirstFrame();
// Initially, the device captures any content changes normally. // Initially, the device captures any content changes normally.
ChangePageContentColor("ff0000"); ChangePageContentColor(SK_ColorRED);
WaitForFrameWithColor(SK_ColorRED); WaitForFrameWithColor(SK_ColorRED);
// Delete the WebContents instance and the Shell, and allow the the "target // Delete the WebContents instance and the Shell, and allow the the "target
...@@ -229,7 +228,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, ...@@ -229,7 +228,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
AllocateAndStartAndWaitForFirstFrame(); AllocateAndStartAndWaitForFirstFrame();
// Initially, the device captures any content changes normally. // Initially, the device captures any content changes normally.
ChangePageContentColor("ff0000"); ChangePageContentColor(SK_ColorRED);
WaitForFrameWithColor(SK_ColorRED); WaitForFrameWithColor(SK_ColorRED);
// Suspend the device. // Suspend the device.
...@@ -239,7 +238,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, ...@@ -239,7 +238,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
// Change the page content and run the browser for five seconds. Expect no // Change the page content and run the browser for five seconds. Expect no
// frames were queued because the device should be suspended. // frames were queued because the device should be suspended.
ChangePageContentColor("00ff00"); ChangePageContentColor(SK_ColorGREEN);
base::RunLoop run_loop; base::RunLoop run_loop;
BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
run_loop.QuitClosure(), run_loop.QuitClosure(),
...@@ -263,7 +262,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest, ...@@ -263,7 +262,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
AllocateAndStartAndWaitForFirstFrame(); AllocateAndStartAndWaitForFirstFrame();
// Set the page content to a known color. // Set the page content to a known color.
ChangePageContentColor("ff0000"); ChangePageContentColor(SK_ColorRED);
WaitForFrameWithColor(SK_ColorRED); WaitForFrameWithColor(SK_ColorRED);
// Without making any further changes to the source (which would trigger // Without making any further changes to the source (which would trigger
...@@ -297,23 +296,23 @@ INSTANTIATE_TEST_CASE_P( ...@@ -297,23 +296,23 @@ INSTANTIATE_TEST_CASE_P(
, ,
WebContentsVideoCaptureDeviceBrowserTestP, WebContentsVideoCaptureDeviceBrowserTestP,
testing::Combine( testing::Combine(
// On ChromeOS, software compositing is not an option. // Note: On ChromeOS, software compositing is not an option.
testing::Values(false), testing::Values(false /* GPU-accelerated compositing */),
// Force video frame resolutions to have a fixed aspect ratio? testing::Values(false /* variable aspect ratio */,
testing::Values(false, true), true /* fixed aspect ratio */),
// Test with a document that contains a cross-site iframe? testing::Values(false /* page has only a main frame */,
testing::Values(false, true))); true /* page contains a cross-site iframe */)));
#else #else
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
, ,
WebContentsVideoCaptureDeviceBrowserTestP, WebContentsVideoCaptureDeviceBrowserTestP,
testing::Combine( testing::Combine(
// Use software compositing instead of GPU-accelerated compositing? testing::Values(false /* GPU-accelerated compositing */,
testing::Values(false, true), true /* software compositing */),
// Force video frame resolutions to have a fixed aspect ratio? testing::Values(false /* variable aspect ratio */,
testing::Values(false, true), true /* fixed aspect ratio */),
// Test with a document that contains a cross-site iframe? testing::Values(false /* page has only a main frame */,
testing::Values(false, true))); true /* page contains a cross-site iframe */)));
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
// Tests that the device successfully captures a series of content changes, // Tests that the device successfully captures a series of content changes,
...@@ -368,18 +367,13 @@ IN_PROC_BROWSER_TEST_P(WebContentsVideoCaptureDeviceBrowserTestP, ...@@ -368,18 +367,13 @@ IN_PROC_BROWSER_TEST_P(WebContentsVideoCaptureDeviceBrowserTestP,
} }
} }
static const struct { static constexpr SkColor kColorsToCycleThrough[] = {
const char* const css_hex; SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorYELLOW,
SkColor skia; SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorWHITE,
} kColorsToCycleThrough[] = {
{"ff0000", SK_ColorRED}, {"00ff00", SK_ColorGREEN},
{"0000ff", SK_ColorBLUE}, {"ffff00", SK_ColorYELLOW},
{"00ffff", SK_ColorCYAN}, {"ff00ff", SK_ColorMAGENTA},
{"ffffff", SK_ColorWHITE},
}; };
for (const auto color : kColorsToCycleThrough) { for (SkColor color : kColorsToCycleThrough) {
ChangePageContentColor(color.css_hex); ChangePageContentColor(color);
WaitForFrameWithColor(color.skia); WaitForFrameWithColor(color);
} }
} }
......
...@@ -1003,6 +1003,11 @@ test("content_browsertests") { ...@@ -1003,6 +1003,11 @@ test("content_browsertests") {
"../browser/media/capture/web_contents_video_capture_device_browsertest.cc", "../browser/media/capture/web_contents_video_capture_device_browsertest.cc",
] ]
# For chromecast, content_browsertests does not provide a shell window.
if (use_aura && !is_chromecast) {
sources += [ "../browser/media/capture/aura_window_video_capture_device_browsertest.cc" ]
}
deps += [ "//third_party/libyuv" ] deps += [ "//third_party/libyuv" ]
data += [ data += [
"//net/tools/testserver/", "//net/tools/testserver/",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment