| Index: content/browser/renderer_host/media/desktop_capture_device_ash.cc
|
| diff --git a/content/browser/renderer_host/media/desktop_capture_device_ash.cc b/content/browser/renderer_host/media/desktop_capture_device_ash.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..08a74826b49b24b79544f3eecc5b3f33ddc60a88
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/media/desktop_capture_device_ash.cc
|
| @@ -0,0 +1,299 @@
|
| +// Copyright 2013 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/renderer_host/media/desktop_capture_device_ash.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/timer/timer.h"
|
| +#include "cc/output/copy_output_request.h"
|
| +#include "cc/output/copy_output_result.h"
|
| +#include "content/browser/aura/image_transport_factory.h"
|
| +#include "content/browser/renderer_host/media/video_capture_device_impl.h"
|
| +#include "content/common/gpu/client/gl_helper.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "media/base/video_util.h"
|
| +#include "media/video/capture/video_capture_types.h"
|
| +#include "third_party/skia/include/core/SkBitmap.h"
|
| +#include "ui/aura/window.h"
|
| +#include "ui/aura/window_observer.h"
|
| +#include "ui/compositor/compositor.h"
|
| +#include "ui/compositor/dip_util.h"
|
| +#include "ui/compositor/layer.h"
|
| +#include "ui/gfx/screen.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +class DesktopVideoCaptureMachine
|
| + : public VideoCaptureMachine,
|
| + public aura::WindowObserver,
|
| + public ui::CompositorObserver,
|
| + public base::SupportsWeakPtr<DesktopVideoCaptureMachine> {
|
| + public:
|
| + DesktopVideoCaptureMachine(const DesktopMediaID& source);
|
| + virtual ~DesktopVideoCaptureMachine();
|
| +
|
| + // VideoCaptureFrameSource overrides.
|
| + virtual bool Start(
|
| + const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
|
| + virtual void Stop() OVERRIDE;
|
| +
|
| + // Implements aura::WindowObserver.
|
| + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
|
| +
|
| + // Implements ui::CompositorObserver.
|
| + virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
|
| + virtual void OnCompositingStarted(ui::Compositor* compositor,
|
| + base::TimeTicks start_time) OVERRIDE {}
|
| + virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
|
| + virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {}
|
| + virtual void OnCompositingLockStateChanged(
|
| + ui::Compositor* compositor) OVERRIDE {}
|
| + virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
|
| + base::TimeTicks timebase,
|
| + base::TimeDelta interval) OVERRIDE {}
|
| +
|
| + private:
|
| + // Captures a frame.
|
| + // |dirty| is false for timer polls and true for compositor updates.
|
| + void Capture(bool dirty);
|
| +
|
| + // Response callback for cc::Layer::RequestCopyOfOutput().
|
| + void DidCopyOutput(
|
| + scoped_refptr<media::VideoFrame> video_frame,
|
| + base::Time start_time,
|
| + const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
|
| + scoped_ptr<cc::CopyOutputResult> result);
|
| +
|
| + // The window associated with the desktop.
|
| + aura::Window* desktop_window_;
|
| +
|
| + // The layer associated with the desktop.
|
| + ui::Layer* desktop_layer_;
|
| +
|
| + // The timer that kicks off period captures.
|
| + base::Timer timer_;
|
| +
|
| + // The desktop id.
|
| + DesktopMediaID desktop_id_;
|
| +
|
| + // Makes all the decisions about which frames to copy, and how.
|
| + scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
|
| +
|
| + // YUV readback pipeline.
|
| + scoped_ptr<content::ReadbackYUVInterface> yuv_readback_pipeline_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine);
|
| +};
|
| +
|
| +DesktopVideoCaptureMachine::DesktopVideoCaptureMachine(
|
| + const DesktopMediaID& source)
|
| + : desktop_window_(NULL),
|
| + desktop_layer_(NULL),
|
| + timer_(true, true),
|
| + desktop_id_(source) {}
|
| +
|
| +DesktopVideoCaptureMachine::~DesktopVideoCaptureMachine() {}
|
| +
|
| +bool DesktopVideoCaptureMachine::Start(
|
| + const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + // TODO(hshi): get the correct display specified by |desktop_id_|.
|
| + const gfx::Display& primary_display =
|
| + gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
|
| + desktop_window_ = gfx::Screen::GetNativeScreen()->GetWindowAtScreenPoint(
|
| + primary_display.bounds().CenterPoint())->GetRootWindow();
|
| + if (!desktop_window_)
|
| + return false;
|
| +
|
| + // If the desktop layer is already destroyed then return failure.
|
| + desktop_layer_ = desktop_window_->layer();
|
| + if (!desktop_layer_)
|
| + return false;
|
| +
|
| + DCHECK(oracle_proxy.get());
|
| + oracle_proxy_ = oracle_proxy;
|
| +
|
| + // Start observing window events.
|
| + desktop_window_->AddObserver(this);
|
| +
|
| + // Start observing compositor updates.
|
| + ui::Compositor* compositor = desktop_layer_->GetCompositor();
|
| + if (!compositor)
|
| + return false;
|
| +
|
| + compositor->AddObserver(this);
|
| +
|
| + // Starts timer.
|
| + timer_.Start(FROM_HERE, oracle_proxy_->capture_period(),
|
| + base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(),
|
| + false));
|
| +
|
| + started_ = true;
|
| + return true;
|
| +}
|
| +
|
| +void DesktopVideoCaptureMachine::Stop() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + // Stop observing window events.
|
| + if (desktop_window_)
|
| + desktop_window_->RemoveObserver(this);
|
| +
|
| + // Stop observing compositor updates.
|
| + if (desktop_layer_) {
|
| + ui::Compositor* compositor = desktop_layer_->GetCompositor();
|
| + if (compositor)
|
| + compositor->RemoveObserver(this);
|
| + }
|
| +
|
| + // Stop timer.
|
| + timer_.Stop();
|
| +
|
| + started_ = false;
|
| +}
|
| +
|
| +void DesktopVideoCaptureMachine::Capture(bool dirty) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + // Do not capture if the desktop layer is already destroyed.
|
| + if (!desktop_layer_)
|
| + return;
|
| +
|
| + scoped_refptr<media::VideoFrame> frame;
|
| + ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
|
| +
|
| + const base::Time start_time = base::Time::Now();
|
| + const VideoCaptureOracle::Event event =
|
| + dirty ? VideoCaptureOracle::kCompositorUpdate
|
| + : VideoCaptureOracle::kTimerPoll;
|
| + if (oracle_proxy_->ObserveEventAndDecideCapture(
|
| + event, start_time, &frame, &capture_frame_cb)) {
|
| + scoped_ptr<cc::CopyOutputRequest> request =
|
| + cc::CopyOutputRequest::CreateRequest(
|
| + base::Bind(&DesktopVideoCaptureMachine::DidCopyOutput,
|
| + AsWeakPtr(), frame, start_time, capture_frame_cb));
|
| + gfx::Rect desktop_size = ui::ConvertRectToPixel(
|
| + desktop_layer_, desktop_layer_->bounds());
|
| + request->set_area(desktop_size);
|
| + desktop_layer_->RequestCopyOfOutput(request.Pass());
|
| + }
|
| +}
|
| +
|
| +static void CopyOutputFinishedForVideo(
|
| + base::Time start_time,
|
| + const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
|
| + scoped_ptr<cc::SingleReleaseCallback> release_callback,
|
| + bool result) {
|
| + release_callback->Run(0, false);
|
| + capture_frame_cb.Run(start_time, result);
|
| +}
|
| +
|
| +void DesktopVideoCaptureMachine::DidCopyOutput(
|
| + scoped_refptr<media::VideoFrame> video_frame,
|
| + base::Time start_time,
|
| + const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
|
| + scoped_ptr<cc::CopyOutputResult> result) {
|
| + if (result->IsEmpty() || result->size().IsEmpty())
|
| + return;
|
| +
|
| + // Compute the dest size we want after the letterboxing resize. Make the
|
| + // coordinates and sizes even because we letterbox in YUV space
|
| + // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
|
| + // line up correctly.
|
| + // The video frame's coded_size() and the result's size() are both physical
|
| + // pixels.
|
| + gfx::Rect region_in_frame =
|
| + media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
|
| + result->size());
|
| + region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
|
| + region_in_frame.y() & ~1,
|
| + region_in_frame.width() & ~1,
|
| + region_in_frame.height() & ~1);
|
| + if (region_in_frame.IsEmpty())
|
| + return;
|
| +
|
| + ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
|
| + GLHelper* gl_helper = factory->GetGLHelper();
|
| + if (!gl_helper)
|
| + return;
|
| +
|
| + cc::TextureMailbox texture_mailbox;
|
| + scoped_ptr<cc::SingleReleaseCallback> release_callback;
|
| + result->TakeTexture(&texture_mailbox, &release_callback);
|
| + DCHECK(texture_mailbox.IsTexture());
|
| + if (!texture_mailbox.IsTexture())
|
| + return;
|
| +
|
| + gfx::Rect result_rect(result->size());
|
| + if (!yuv_readback_pipeline_ ||
|
| + yuv_readback_pipeline_->scaler()->SrcSize() != result_rect.size() ||
|
| + yuv_readback_pipeline_->scaler()->SrcSubrect() != result_rect ||
|
| + yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) {
|
| + yuv_readback_pipeline_.reset(
|
| + gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST,
|
| + result_rect.size(),
|
| + result_rect,
|
| + video_frame->coded_size(),
|
| + region_in_frame,
|
| + true,
|
| + true));
|
| + }
|
| + yuv_readback_pipeline_->ReadbackYUV(
|
| + texture_mailbox.name(), texture_mailbox.sync_point(), video_frame.get(),
|
| + base::Bind(&CopyOutputFinishedForVideo, start_time, capture_frame_cb,
|
| + base::Passed(&release_callback)));
|
| +}
|
| +
|
| +void DesktopVideoCaptureMachine::OnWindowDestroyed(aura::Window* window) {
|
| + DCHECK(desktop_window_ && window == desktop_window_);
|
| + desktop_window_ = NULL;
|
| + desktop_layer_ = NULL;
|
| +
|
| + // Post task to stop capture on UI thread.
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
|
| + &DesktopVideoCaptureMachine::Stop, AsWeakPtr()));
|
| +}
|
| +
|
| +void DesktopVideoCaptureMachine::OnCompositingEnded(
|
| + ui::Compositor* compositor) {
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
|
| + &DesktopVideoCaptureMachine::Capture, AsWeakPtr(), true));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +DesktopCaptureDeviceAsh::DesktopCaptureDeviceAsh(
|
| + const DesktopMediaID& source)
|
| + : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
|
| + new DesktopVideoCaptureMachine(source)))) {}
|
| +
|
| +DesktopCaptureDeviceAsh::~DesktopCaptureDeviceAsh() {
|
| + DVLOG(2) << "DesktopCaptureDeviceAsh@" << this << " destroying.";
|
| +}
|
| +
|
| +// static
|
| +media::VideoCaptureDevice* DesktopCaptureDeviceAsh::Create(
|
| + const DesktopMediaID& source) {
|
| + // This implementation only supports screen capture.
|
| + if (source.type != DesktopMediaID::TYPE_SCREEN)
|
| + return NULL;
|
| +
|
| + return new DesktopCaptureDeviceAsh(source);
|
| +}
|
| +
|
| +void DesktopCaptureDeviceAsh::AllocateAndStart(
|
| + const media::VideoCaptureParams& params,
|
| + scoped_ptr<Client> client) {
|
| + DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
|
| + impl_->AllocateAndStart(params, client.Pass());
|
| +}
|
| +
|
| +void DesktopCaptureDeviceAsh::StopAndDeAllocate() {
|
| + impl_->StopAndDeAllocate();
|
| +}
|
| +
|
| +} // namespace content
|
|
|