| Index: content/browser/renderer_host/media/web_contents_video_capture_device.cc
|
| diff --git a/content/browser/renderer_host/media/web_contents_video_capture_device.cc b/content/browser/renderer_host/media/web_contents_video_capture_device.cc
|
| index 5a5efc2f055f9be290a09d0a35c754a6b2e0251b..15258a04523cf7d9b8c9e31cdd7e818b793d1802 100644
|
| --- a/content/browser/renderer_host/media/web_contents_video_capture_device.cc
|
| +++ b/content/browser/renderer_host/media/web_contents_video_capture_device.cc
|
| @@ -50,26 +50,18 @@
|
|
|
| #include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
|
|
|
| -#include <algorithm>
|
| -#include <list>
|
| -#include <string>
|
| -
|
| #include "base/basictypes.h"
|
| #include "base/bind.h"
|
| -#include "base/callback_forward.h"
|
| #include "base/callback_helpers.h"
|
| -#include "base/debug/trace_event.h"
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_ptr.h"
|
| -#include "base/memory/weak_ptr.h"
|
| #include "base/message_loop/message_loop_proxy.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/sequenced_task_runner.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/synchronization/lock.h"
|
| #include "base/threading/thread.h"
|
| #include "base/threading/thread_checker.h"
|
| #include "base/time/time.h"
|
| +#include "content/browser/renderer_host/media/video_capture_device_impl.h"
|
| #include "content/browser/renderer_host/media/video_capture_oracle.h"
|
| #include "content/browser/renderer_host/media/web_contents_capture_util.h"
|
| #include "content/browser/renderer_host/render_widget_host_impl.h"
|
| @@ -79,43 +71,19 @@
|
| #include "content/public/browser/browser_thread.h"
|
| #include "content/public/browser/notification_source.h"
|
| #include "content/public/browser/notification_types.h"
|
| -#include "content/public/browser/render_process_host.h"
|
| #include "content/public/browser/render_view_host.h"
|
| #include "content/public/browser/render_widget_host_view.h"
|
| -#include "content/public/browser/web_contents.h"
|
| #include "content/public/browser/web_contents_observer.h"
|
| -#include "media/base/bind_to_loop.h"
|
| -#include "media/base/video_frame.h"
|
| #include "media/base/video_util.h"
|
| -#include "media/base/yuv_convert.h"
|
| #include "media/video/capture/video_capture_types.h"
|
| #include "skia/ext/image_operations.h"
|
| #include "third_party/skia/include/core/SkBitmap.h"
|
| #include "third_party/skia/include/core/SkColor.h"
|
| -#include "ui/gfx/rect.h"
|
| -#include "ui/gfx/skia_util.h"
|
|
|
| namespace content {
|
|
|
| namespace {
|
|
|
| -const int kMinFrameWidth = 2;
|
| -const int kMinFrameHeight = 2;
|
| -
|
| -// TODO(nick): Remove this once frame subscription is supported on Aura and
|
| -// Linux.
|
| -#if (defined(OS_WIN) || defined(OS_MACOSX)) || defined(USE_AURA)
|
| -const bool kAcceleratedSubscriberIsSupported = true;
|
| -#else
|
| -const bool kAcceleratedSubscriberIsSupported = false;
|
| -#endif
|
| -
|
| -// Returns the nearest even integer closer to zero.
|
| -template<typename IntType>
|
| -IntType MakeEven(IntType x) {
|
| - return x & static_cast<IntType>(-2);
|
| -}
|
| -
|
| // Compute a letterbox region, aligned to even coordinates.
|
| gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
|
| const gfx::Size& content_size) {
|
| @@ -131,57 +99,14 @@ gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
|
| return result;
|
| }
|
|
|
| -// Thread-safe, refcounted proxy to the VideoCaptureOracle. This proxy wraps
|
| -// the VideoCaptureOracle, which decides which frames to capture, and a
|
| -// VideoCaptureDevice::Client, which allocates and receives the captured
|
| -// frames, in a lock to synchronize state between the two.
|
| -class ThreadSafeCaptureOracle
|
| - : public base::RefCountedThreadSafe<ThreadSafeCaptureOracle> {
|
| - public:
|
| - ThreadSafeCaptureOracle(scoped_ptr<media::VideoCaptureDevice::Client> client,
|
| - scoped_ptr<VideoCaptureOracle> oracle,
|
| - const gfx::Size& capture_size,
|
| - int frame_rate);
|
| -
|
| - bool ObserveEventAndDecideCapture(
|
| - VideoCaptureOracle::Event event,
|
| - base::Time event_time,
|
| - scoped_refptr<media::VideoFrame>* storage,
|
| - RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback);
|
| -
|
| - base::TimeDelta capture_period() const {
|
| - return oracle_->capture_period();
|
| - }
|
| -
|
| - // Stop new captures from happening (but doesn't forget the client).
|
| - void Stop();
|
| -
|
| - // Signal an error to the client.
|
| - void ReportError();
|
| -
|
| - private:
|
| - friend class base::RefCountedThreadSafe<ThreadSafeCaptureOracle>;
|
| - virtual ~ThreadSafeCaptureOracle() {}
|
| -
|
| - // Callback invoked on completion of all captures.
|
| - void DidCaptureFrame(
|
| - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
|
| - int frame_number,
|
| - base::Time timestamp,
|
| - bool success);
|
| - // Protects everything below it.
|
| - base::Lock lock_;
|
| -
|
| - // Recipient of our capture activity.
|
| - scoped_ptr<media::VideoCaptureDevice::Client> client_;
|
| -
|
| - // Makes the decision to capture a frame.
|
| - const scoped_ptr<VideoCaptureOracle> oracle_;
|
| -
|
| - // The current capturing resolution and frame rate.
|
| - const gfx::Size capture_size_;
|
| - const int frame_rate_;
|
| -};
|
| +// Wrapper function to invoke ThreadSafeCaptureOracle::CaptureFrameCallback, is
|
| +// compatible with RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback.
|
| +void InvokeCaptureFrameCallback(
|
| + const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
|
| + base::Time timestamp,
|
| + bool frame_captured) {
|
| + capture_frame_cb.Run(timestamp, frame_captured);
|
| +}
|
|
|
| // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
|
| // with RenderWidgetHostViewFrameSubscriber. We create one per event type.
|
| @@ -275,18 +200,18 @@ void RenderVideoFrame(const SkBitmap& input,
|
| // implementation is currently asynchronous -- in our case, the "rvh changed"
|
| // notification would get posted back to the UI thread and processed later, and
|
| // this seems disadvantageous.
|
| -class CaptureMachine : public WebContentsObserver,
|
| - public base::SupportsWeakPtr<CaptureMachine> {
|
| +class WebContentsCaptureMachine
|
| + : public VideoCaptureMachine,
|
| + public WebContentsObserver,
|
| + public base::SupportsWeakPtr<WebContentsCaptureMachine> {
|
| public:
|
| - virtual ~CaptureMachine();
|
| + WebContentsCaptureMachine(int render_process_id, int render_view_id);
|
| + virtual ~WebContentsCaptureMachine();
|
|
|
| - // Creates a CaptureMachine. Must be run on the UI BrowserThread. Returns
|
| - // NULL if the indicated render view cannot be found.
|
| - static scoped_ptr<CaptureMachine> Create(
|
| - int render_process_id,
|
| - int render_view_id,
|
| - const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
|
| - const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy);
|
| + // VideoCaptureMachine overrides.
|
| + virtual bool Start(
|
| + const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
|
| + virtual void Stop() OVERRIDE;
|
|
|
| // Starts a copy from the backing store or the composited surface. Must be run
|
| // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
|
| @@ -328,13 +253,8 @@ class CaptureMachine : public WebContentsObserver,
|
| virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
|
|
|
| private:
|
| - CaptureMachine(
|
| - const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
|
| - const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy);
|
| -
|
| // Starts observing the web contents, returning false if lookup fails.
|
| - bool StartObservingWebContents(int initial_render_process_id,
|
| - int initial_render_view_id);
|
| + bool StartObservingWebContents();
|
|
|
| // Helper function to determine the view that we are currently tracking.
|
| RenderWidgetHost* GetTarget();
|
| @@ -360,12 +280,16 @@ class CaptureMachine : public WebContentsObserver,
|
| // attached views.
|
| void RenewFrameSubscription();
|
|
|
| - // The task runner of the thread on which SkBitmap->VideoFrame conversion will
|
| + // Parameters saved in constructor.
|
| + const int initial_render_process_id_;
|
| + const int initial_render_view_id_;
|
| +
|
| + // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
|
| // occur. Only used when this activity cannot be done on the GPU.
|
| - const scoped_refptr<base::SequencedTaskRunner> render_task_runner_;
|
| + base::Thread render_thread_;
|
|
|
| // Makes all the decisions about which frames to copy, and how.
|
| - const scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
|
| + scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
|
|
|
| // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
|
| // otherwise.
|
| @@ -378,7 +302,7 @@ class CaptureMachine : public WebContentsObserver,
|
| // oracle, and initiating captures accordingly.
|
| scoped_ptr<ContentCaptureSubscription> subscription_;
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(CaptureMachine);
|
| + DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
|
| };
|
|
|
| // Responsible for logging the effective frame rate.
|
| @@ -401,121 +325,6 @@ class VideoFrameDeliveryLog {
|
| DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
|
| };
|
|
|
| -ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
|
| - scoped_ptr<media::VideoCaptureDevice::Client> client,
|
| - scoped_ptr<VideoCaptureOracle> oracle,
|
| - const gfx::Size& capture_size,
|
| - int frame_rate)
|
| - : client_(client.Pass()),
|
| - oracle_(oracle.Pass()),
|
| - capture_size_(capture_size),
|
| - frame_rate_(frame_rate) {}
|
| -
|
| -bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
|
| - VideoCaptureOracle::Event event,
|
| - base::Time event_time,
|
| - scoped_refptr<media::VideoFrame>* storage,
|
| - RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback) {
|
| - base::AutoLock guard(lock_);
|
| -
|
| - if (!client_)
|
| - return false; // Capture is stopped.
|
| -
|
| - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
|
| - client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_);
|
| - const bool should_capture =
|
| - oracle_->ObserveEventAndDecideCapture(event, event_time);
|
| - const bool content_is_dirty =
|
| - (event == VideoCaptureOracle::kCompositorUpdate ||
|
| - event == VideoCaptureOracle::kSoftwarePaint);
|
| - const char* event_name =
|
| - (event == VideoCaptureOracle::kTimerPoll ? "poll" :
|
| - (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
|
| - "paint"));
|
| -
|
| - // Consider the various reasons not to initiate a capture.
|
| - if (should_capture && !output_buffer) {
|
| - TRACE_EVENT_INSTANT1("mirroring",
|
| - "EncodeLimited",
|
| - TRACE_EVENT_SCOPE_THREAD,
|
| - "trigger",
|
| - event_name);
|
| - return false;
|
| - } else if (!should_capture && output_buffer) {
|
| - if (content_is_dirty) {
|
| - // This is a normal and acceptable way to drop a frame. We've hit our
|
| - // capture rate limit: for example, the content is animating at 60fps but
|
| - // we're capturing at 30fps.
|
| - TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
|
| - TRACE_EVENT_SCOPE_THREAD,
|
| - "trigger", event_name);
|
| - }
|
| - return false;
|
| - } else if (!should_capture && !output_buffer) {
|
| - // We decided not to capture, but we wouldn't have been able to if we wanted
|
| - // to because no output buffer was available.
|
| - TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
|
| - TRACE_EVENT_SCOPE_THREAD,
|
| - "trigger", event_name);
|
| - return false;
|
| - }
|
| - int frame_number = oracle_->RecordCapture();
|
| - TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
|
| - "frame_number", frame_number,
|
| - "trigger", event_name);
|
| -
|
| - *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
|
| - this,
|
| - output_buffer,
|
| - frame_number);
|
| - *storage = media::VideoFrame::WrapExternalPackedMemory(
|
| - media::VideoFrame::I420,
|
| - capture_size_,
|
| - gfx::Rect(capture_size_),
|
| - capture_size_,
|
| - static_cast<uint8*>(output_buffer->data()),
|
| - output_buffer->size(),
|
| - base::SharedMemory::NULLHandle(),
|
| - base::TimeDelta(),
|
| - base::Closure());
|
| - return true;
|
| -}
|
| -
|
| -void ThreadSafeCaptureOracle::Stop() {
|
| - base::AutoLock guard(lock_);
|
| - client_.reset();
|
| -}
|
| -
|
| -void ThreadSafeCaptureOracle::ReportError() {
|
| - base::AutoLock guard(lock_);
|
| - if (client_)
|
| - client_->OnError();
|
| -}
|
| -
|
| -void ThreadSafeCaptureOracle::DidCaptureFrame(
|
| - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
|
| - int frame_number,
|
| - base::Time timestamp,
|
| - bool success) {
|
| - base::AutoLock guard(lock_);
|
| - TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
|
| - "success", success,
|
| - "timestamp", timestamp.ToInternalValue());
|
| -
|
| - if (!client_)
|
| - return; // Capture is stopped.
|
| -
|
| - if (success) {
|
| - if (oracle_->CompleteCapture(frame_number, timestamp)) {
|
| - client_->OnIncomingCapturedBuffer(buffer,
|
| - media::VideoFrame::I420,
|
| - capture_size_,
|
| - timestamp,
|
| - frame_rate_);
|
| - }
|
| - }
|
| -}
|
| -
|
| bool FrameSubscriber::ShouldCaptureFrame(
|
| base::Time present_time,
|
| scoped_refptr<media::VideoFrame>* storage,
|
| @@ -523,8 +332,12 @@ bool FrameSubscriber::ShouldCaptureFrame(
|
| TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
|
| "instance", this);
|
|
|
| - return oracle_proxy_->ObserveEventAndDecideCapture(event_type_, present_time,
|
| - storage, deliver_frame_cb);
|
| + ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
|
| + bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
|
| + event_type_, present_time, storage, &capture_frame_cb);
|
| +
|
| + *deliver_frame_cb = base::Bind(&InvokeCaptureFrameCallback, capture_frame_cb);
|
| + return oracle_decision;
|
| }
|
|
|
| ContentCaptureSubscription::ContentCaptureSubscription(
|
| @@ -552,7 +365,8 @@ ContentCaptureSubscription::ContentCaptureSubscription(
|
| }
|
|
|
| // Subscribe to software paint events. This instance will service these by
|
| - // reflecting them back to the CaptureMachine via |capture_callback|.
|
| + // reflecting them back to the WebContentsCaptureMachine via
|
| + // |capture_callback|.
|
| registrar_.Add(
|
| this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
|
| Source<RenderWidgetHost>(&source));
|
| @@ -734,44 +548,47 @@ void VideoFrameDeliveryLog::ChronicleFrameDelivery(int frame_number) {
|
| }
|
| }
|
|
|
| -// static
|
| -scoped_ptr<CaptureMachine> CaptureMachine::Create(
|
| - int render_process_id,
|
| - int render_view_id,
|
| - const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
|
| +WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
|
| + int render_view_id)
|
| + : initial_render_process_id_(render_process_id),
|
| + initial_render_view_id_(render_view_id),
|
| + render_thread_("WebContentsVideo_RenderThread"),
|
| + fullscreen_widget_id_(MSG_ROUTING_NONE) {}
|
| +
|
| +WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
|
| +
|
| +bool WebContentsCaptureMachine::Start(
|
| const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(render_task_runner.get());
|
| - DCHECK(oracle_proxy.get());
|
| - scoped_ptr<CaptureMachine> machine(
|
| - new CaptureMachine(render_task_runner, oracle_proxy));
|
| + DCHECK(!started_);
|
|
|
| - if (!machine->StartObservingWebContents(render_process_id, render_view_id))
|
| - machine.reset();
|
| + DCHECK(oracle_proxy.get());
|
| + oracle_proxy_ = oracle_proxy;
|
|
|
| - return machine.Pass();
|
| -}
|
| + if (!render_thread_.Start()) {
|
| + DVLOG(1) << "Failed to spawn render thread.";
|
| + return false;
|
| + }
|
|
|
| -CaptureMachine::CaptureMachine(
|
| - const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
|
| - const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy)
|
| - : render_task_runner_(render_task_runner),
|
| - oracle_proxy_(oracle_proxy),
|
| - fullscreen_widget_id_(MSG_ROUTING_NONE) {}
|
| + if (!StartObservingWebContents())
|
| + return false;
|
|
|
| -CaptureMachine::~CaptureMachine() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
|
| - !BrowserThread::IsMessageLoopValid(BrowserThread::UI));
|
| + started_ = true;
|
| + return true;
|
| +}
|
|
|
| - // Stop observing the web contents.
|
| +void WebContentsCaptureMachine::Stop() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| subscription_.reset();
|
| if (web_contents()) {
|
| web_contents()->DecrementCapturerCount();
|
| Observe(NULL);
|
| }
|
| + render_thread_.Stop();
|
| + started_ = false;
|
| }
|
|
|
| -void CaptureMachine::Capture(
|
| +void WebContentsCaptureMachine::Capture(
|
| const base::Time& start_time,
|
| const scoped_refptr<media::VideoFrame>& target,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| @@ -806,27 +623,25 @@ void CaptureMachine::Capture(
|
| // backing store are not accessible.
|
| rwh->GetSnapshotFromRenderer(
|
| gfx::Rect(),
|
| - base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(),
|
| - start_time, target, deliver_frame_cb));
|
| + base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
|
| + this->AsWeakPtr(), start_time, target, deliver_frame_cb));
|
| } else if (view->CanCopyToVideoFrame()) {
|
| view->CopyFromCompositingSurfaceToVideoFrame(
|
| gfx::Rect(view_size),
|
| target,
|
| - base::Bind(&CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame,
|
| + base::Bind(&WebContentsCaptureMachine::
|
| + DidCopyFromCompositingSurfaceToVideoFrame,
|
| this->AsWeakPtr(), start_time, deliver_frame_cb));
|
| } else {
|
| rwh->CopyFromBackingStore(
|
| gfx::Rect(),
|
| fitted_size, // Size here is a request not always honored.
|
| - base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(),
|
| - start_time, target, deliver_frame_cb));
|
| + base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
|
| + this->AsWeakPtr(), start_time, target, deliver_frame_cb));
|
| }
|
| }
|
|
|
| -bool CaptureMachine::StartObservingWebContents(int initial_render_process_id,
|
| - int initial_render_view_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| +bool WebContentsCaptureMachine::StartObservingWebContents() {
|
| // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
|
| // If successful, begin observing the WebContents instance.
|
| //
|
| @@ -837,11 +652,11 @@ bool CaptureMachine::StartObservingWebContents(int initial_render_process_id,
|
| // a bit of indirection across threads. It's easily possible that, in the
|
| // meantime, the original RenderView may have gone away.
|
| RenderViewHost* const rvh =
|
| - RenderViewHost::FromID(initial_render_process_id,
|
| - initial_render_view_id);
|
| + RenderViewHost::FromID(initial_render_process_id_,
|
| + initial_render_view_id_);
|
| DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
|
| - << initial_render_process_id << ", "
|
| - << initial_render_view_id << ") returned NULL.";
|
| + << initial_render_process_id_ << ", "
|
| + << initial_render_view_id_ << ") returned NULL.";
|
| Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
|
|
|
| WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
|
| @@ -856,7 +671,8 @@ bool CaptureMachine::StartObservingWebContents(int initial_render_process_id,
|
| return false;
|
| }
|
|
|
| -void CaptureMachine::WebContentsDestroyed(WebContents* web_contents) {
|
| +void WebContentsCaptureMachine::WebContentsDestroyed(
|
| + WebContents* web_contents) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| subscription_.reset();
|
| @@ -864,7 +680,7 @@ void CaptureMachine::WebContentsDestroyed(WebContents* web_contents) {
|
| oracle_proxy_->ReportError();
|
| }
|
|
|
| -RenderWidgetHost* CaptureMachine::GetTarget() {
|
| +RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| if (!web_contents())
|
| return NULL;
|
| @@ -881,7 +697,7 @@ RenderWidgetHost* CaptureMachine::GetTarget() {
|
| return rwh;
|
| }
|
|
|
| -void CaptureMachine::DidCopyFromBackingStore(
|
| +void WebContentsCaptureMachine::DidCopyFromBackingStore(
|
| const base::Time& start_time,
|
| const scoped_refptr<media::VideoFrame>& target,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| @@ -895,7 +711,7 @@ void CaptureMachine::DidCopyFromBackingStore(
|
| UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
|
| TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(),
|
| "Render");
|
| - render_task_runner_->PostTask(FROM_HERE, base::Bind(
|
| + render_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
|
| &RenderVideoFrame, bitmap, target,
|
| base::Bind(deliver_frame_cb, start_time)));
|
| } else {
|
| @@ -905,7 +721,7 @@ void CaptureMachine::DidCopyFromBackingStore(
|
| }
|
| }
|
|
|
| -void CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
|
| +void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
|
| const base::Time& start_time,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| deliver_frame_cb,
|
| @@ -922,7 +738,7 @@ void CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
|
| deliver_frame_cb.Run(start_time, success);
|
| }
|
|
|
| -void CaptureMachine::RenewFrameSubscription() {
|
| +void WebContentsCaptureMachine::RenewFrameSubscription() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| // Always destroy the old subscription before creating a new one.
|
| @@ -933,240 +749,15 @@ void CaptureMachine::RenewFrameSubscription() {
|
| return;
|
|
|
| subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
|
| - base::Bind(&CaptureMachine::Capture, this->AsWeakPtr())));
|
| -}
|
| -
|
| -void DeleteCaptureMachineOnUIThread(
|
| - scoped_ptr<CaptureMachine> capture_machine) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - capture_machine.reset();
|
| + base::Bind(&WebContentsCaptureMachine::Capture, this->AsWeakPtr())));
|
| }
|
|
|
| } // namespace
|
|
|
| -// The "meat" of the video capture implementation, which is a ref-counted class.
|
| -// Separating this from the "shell class" WebContentsVideoCaptureDevice allows
|
| -// safe destruction without needing to block any threads (e.g., the IO
|
| -// BrowserThread).
|
| -//
|
| -// WebContentsVideoCaptureDevice::Impl manages a simple state machine and the
|
| -// pipeline (see notes at top of this file). It times the start of successive
|
| -// captures and facilitates the processing of each through the stages of the
|
| -// pipeline.
|
| -class WebContentsVideoCaptureDevice::Impl : public base::SupportsWeakPtr<Impl> {
|
| - public:
|
| -
|
| - Impl(int render_process_id, int render_view_id);
|
| - virtual ~Impl();
|
| -
|
| - // Asynchronous requests to change WebContentsVideoCaptureDevice::Impl state.
|
| - void AllocateAndStart(const media::VideoCaptureParams& params,
|
| - scoped_ptr<media::VideoCaptureDevice::Client> client);
|
| - void StopAndDeAllocate();
|
| -
|
| - private:
|
| -
|
| - // Flag indicating current state.
|
| - enum State {
|
| - kIdle,
|
| - kCapturing,
|
| - kError
|
| - };
|
| -
|
| - void TransitionStateTo(State next_state);
|
| -
|
| - // Stops capturing and notifies client_ of an error state.
|
| - void Error();
|
| -
|
| - // Called in response to CaptureMachine::Create that runs on the UI thread.
|
| - // It will assign the capture machine to the Impl class if it still exists
|
| - // otherwise it will post a task to delete CaptureMachine on the UI thread.
|
| - static void AssignCaptureMachine(
|
| - base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl,
|
| - scoped_ptr<CaptureMachine> capture_machine);
|
| -
|
| - // Tracks that all activity occurs on the media stream manager's thread.
|
| - base::ThreadChecker thread_checker_;
|
| -
|
| - // These values identify the starting view that will be captured. After
|
| - // capture starts, the target view IDs will change as navigation occurs, and
|
| - // so these values are not relevant after the initial bootstrapping.
|
| - const int initial_render_process_id_;
|
| - const int initial_render_view_id_;
|
| -
|
| - // Current lifecycle state.
|
| - State state_;
|
| -
|
| - // A dedicated worker thread for doing image operations. Started/joined here,
|
| - // but used by the CaptureMachine.
|
| - base::Thread render_thread_;
|
| -
|
| - // Tracks the CaptureMachine that's doing work on our behalf on the UI thread.
|
| - // This value should never be dereferenced by this class, other than to
|
| - // create and destroy it on the UI thread.
|
| - scoped_ptr<CaptureMachine> capture_machine_;
|
| -
|
| - // Our thread-safe capture oracle which serves as the gateway to the video
|
| - // capture pipeline. Besides the WCVCD itself, it is the only component of the
|
| - // system with direct access to |client_|.
|
| - scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Impl);
|
| -};
|
| -
|
| -WebContentsVideoCaptureDevice::Impl::Impl(int render_process_id,
|
| - int render_view_id)
|
| - : initial_render_process_id_(render_process_id),
|
| - initial_render_view_id_(render_view_id),
|
| - state_(kIdle),
|
| - render_thread_("WebContentsVideo_RenderThread") {}
|
| -
|
| -void WebContentsVideoCaptureDevice::Impl::AllocateAndStart(
|
| - const media::VideoCaptureParams& params,
|
| - scoped_ptr<VideoCaptureDevice::Client> client) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - if (state_ != kIdle) {
|
| - DVLOG(1) << "Allocate() invoked when not in state Idle.";
|
| - return;
|
| - }
|
| -
|
| - if (params.requested_format.frame_rate <= 0) {
|
| - DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate;
|
| - client->OnError();
|
| - return;
|
| - }
|
| -
|
| - if (!render_thread_.Start()) {
|
| - DVLOG(1) << "Failed to spawn render thread.";
|
| - client->OnError();
|
| - return;
|
| - }
|
| -
|
| - // Frame dimensions must each be a positive, even integer, since the client
|
| - // wants (or will convert to) YUV420.
|
| - gfx::Size frame_size(MakeEven(params.requested_format.frame_size.width()),
|
| - MakeEven(params.requested_format.frame_size.height()));
|
| - if (frame_size.width() < kMinFrameWidth ||
|
| - frame_size.height() < kMinFrameHeight) {
|
| - DVLOG(1) << "invalid frame size: " << frame_size.ToString();
|
| - client->OnError();
|
| - return;
|
| - }
|
| -
|
| - base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
|
| - 1000000.0 / params.requested_format.frame_rate + 0.5);
|
| -
|
| - scoped_ptr<VideoCaptureOracle> oracle(
|
| - new VideoCaptureOracle(capture_period,
|
| - kAcceleratedSubscriberIsSupported));
|
| - oracle_proxy_ =
|
| - new ThreadSafeCaptureOracle(client.Pass(),
|
| - oracle.Pass(),
|
| - frame_size,
|
| - params.requested_format.frame_rate);
|
| -
|
| - // Allocates the CaptureMachine. The CaptureMachine will be tracking render
|
| - // view swapping over its lifetime, and we don't want to lose our reference to
|
| - // the current render view by starting over with the stale
|
| - // |initial_render_view_id_|.
|
| - DCHECK(!capture_machine_.get());
|
| - BrowserThread::PostTaskAndReplyWithResult(
|
| - BrowserThread::UI, FROM_HERE,
|
| - base::Bind(&CaptureMachine::Create,
|
| - initial_render_process_id_,
|
| - initial_render_view_id_,
|
| - render_thread_.message_loop_proxy(), oracle_proxy_),
|
| - base::Bind(&Impl::AssignCaptureMachine, AsWeakPtr()));
|
| -
|
| - TransitionStateTo(kCapturing);
|
| -}
|
| -
|
| -// static
|
| -void WebContentsVideoCaptureDevice::Impl::AssignCaptureMachine(
|
| - base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl,
|
| - scoped_ptr<CaptureMachine> capture_machine) {
|
| - DCHECK(!impl.get() || impl->thread_checker_.CalledOnValidThread());
|
| -
|
| - if (!impl.get()) {
|
| - // If WCVD::Impl was destroyed before we got back on it's thread and
|
| - // capture_machine is not NULL, then we need to return to the UI thread to
|
| - // safely cleanup the CaptureMachine.
|
| - if (capture_machine) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI, FROM_HERE, base::Bind(
|
| - &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine)));
|
| - return;
|
| - }
|
| - } else if (!capture_machine) {
|
| - impl->Error();
|
| - } else {
|
| - impl->capture_machine_ = capture_machine.Pass();
|
| - }
|
| -}
|
| -
|
| -void WebContentsVideoCaptureDevice::Impl::StopAndDeAllocate() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - if (state_ != kCapturing) {
|
| - return;
|
| - }
|
| - oracle_proxy_->Stop();
|
| - oracle_proxy_ = NULL;
|
| - render_thread_.Stop();
|
| -
|
| - TransitionStateTo(kIdle);
|
| -
|
| - // There is still a capture pipeline running that is checking in with the
|
| - // oracle, and processing captures that are already started in flight. That
|
| - // pipeline must be shut down asynchronously, on the UI thread.
|
| - if (capture_machine_) {
|
| - // The task that is posted to the UI thread might not run if we are shutting
|
| - // down, so we transfer ownership of CaptureMachine to the closure so that
|
| - // it is still cleaned up when the closure is deleted.
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI, FROM_HERE, base::Bind(
|
| - &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
|
| - }
|
| -}
|
| -
|
| -WebContentsVideoCaptureDevice::Impl::~Impl() {
|
| - DCHECK(!capture_machine_) << "Cleanup on UI thread did not happen.";
|
| - DVLOG(1) << "WebContentsVideoCaptureDevice::Impl@" << this << " destroying.";
|
| -}
|
| -
|
| -void WebContentsVideoCaptureDevice::Impl::TransitionStateTo(State next_state) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| -#ifndef NDEBUG
|
| - static const char* kStateNames[] = {
|
| - "Idle", "Allocated", "Capturing", "Error"
|
| - };
|
| - DVLOG(1) << "State change: " << kStateNames[state_]
|
| - << " --> " << kStateNames[next_state];
|
| -#endif
|
| -
|
| - state_ = next_state;
|
| -}
|
| -
|
| -void WebContentsVideoCaptureDevice::Impl::Error() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - if (state_ == kIdle)
|
| - return;
|
| -
|
| - if (oracle_proxy_)
|
| - oracle_proxy_->ReportError();
|
| -
|
| - StopAndDeAllocate();
|
| - TransitionStateTo(kError);
|
| -}
|
| -
|
| WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
|
| - int render_process_id,
|
| - int render_view_id)
|
| - : impl_(new WebContentsVideoCaptureDevice::Impl(render_process_id,
|
| - render_view_id)) {}
|
| + int render_process_id, int render_view_id)
|
| + : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
|
| + new WebContentsCaptureMachine(render_process_id, render_view_id)))) {}
|
|
|
| WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
|
| DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
|
|
|