Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1335)

Unified Diff: content/browser/renderer_host/media/web_contents_video_capture_device.cc

Issue 12090109: Tab Capture: Backing store readbacks to YV12 VideoFrames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Style fix per wjia Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 aed5d37cfbabb6e88e3cd760ec5efae3c5c5afd5..8b30cb18d8b4aa4995101dadc658e0537be84d18 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
@@ -57,6 +57,7 @@
#include "base/time.h"
#include "content/browser/renderer_host/media/web_contents_capture_util.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
@@ -64,11 +65,13 @@
#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/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"
// Used to self-trampoline invocation of methods to the approprate thread. This
// should be used sparingly, only when it's not clear which thread is invoking a
@@ -139,16 +142,6 @@ void CalculateFittedSize(int source_width, int source_height,
// backing store on the UI BrowserThread.
class BackingStoreCopier : public WebContentsObserver {
public:
- // Result status and done callback used with StartCopy().
- enum Result {
- OK,
- TRANSIENT_ERROR,
- NO_SOURCE,
- };
- typedef base::Callback<void(Result result,
- const SkBitmap& capture,
- const base::Time& capture_time)> DoneCB;
-
BackingStoreCopier(int render_process_id, int render_view_id);
virtual ~BackingStoreCopier();
@@ -157,18 +150,19 @@ class BackingStoreCopier : public WebContentsObserver {
// This is used for unit testing.
void SetRenderWidgetHostForTesting(RenderWidgetHost* override);
- // Starts the copy from the backing store. Must be run on the UI
- // BrowserThread. |done_cb| is invoked with result status. When successful
- // (OK), the bitmap of the capture is transferred to the callback along with
- // the timestamp at which the capture was completed.
- void StartCopy(int frame_number, int desired_width, int desired_height,
- const DoneCB& done_cb);
+ // Starts the copy from the backing store. Must be run on the UI
+ // BrowserThread. Resulting frame is conveyed back to |consumer|.
+ void StartCopy(const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ int desired_width,
+ int desired_height);
// Stops observing an existing WebContents instance, if any. This must be
// called before BackingStoreCopier is destroyed. Must be run on the UI
// BrowserThread.
void StopObservingWebContents();
+ // content::WebContentsObserver implementation.
virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
fullscreen_widget_id_ = routing_id;
@@ -185,10 +179,20 @@ class BackingStoreCopier : public WebContentsObserver {
void LookUpAndObserveWebContents();
- void CopyFromBackingStoreComplete(int frame_number,
- const DoneCB& done_cb,
- bool success,
- const SkBitmap& result);
+ // Response callback for RenderWidgetHost::CopyFromBackingStore.
+ void DidCopyFromBackingStore(const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ const base::Time& start_time,
+ bool success,
+ const SkBitmap& frame);
+
+ // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
+ void DidCopyFromBackingStoreToVideoFrame(
+ const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ const base::Time& start_time,
+ const scoped_refptr<media::VideoFrame>& frame,
+ bool success);
// The "starting point" to find the capture source.
const int render_process_id_;
@@ -258,6 +262,9 @@ class SynchronizedConsumer {
void OnError();
void OnIncomingCapturedFrame(const uint8* pixels, int size,
const base::Time& timestamp);
+ void OnIncomingCapturedVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::Time& timestamp);
private:
base::Lock consumer_lock_;
@@ -272,15 +279,31 @@ class VideoFrameDeliverer {
public:
explicit VideoFrameDeliverer(SynchronizedConsumer* consumer);
+ // Deliver a fully rendered ARGB frame, using SkBitmap as a container.
+ // |done_cb| will be invoked after delivery is complete.
void Deliver(int frame_number,
- const SkBitmap& frame_buffer, const base::Time& frame_timestamp,
+ const SkBitmap& frame_buffer,
+ const base::Time& frame_timestamp,
const base::Closure& done_cb);
+ // Deliver a fully rendered frame YV12 frame, using VideoFrame as a container.
+ // A refcount is taken on |frame| until delivery is complete.
+ void Deliver(int frame_number,
+ const scoped_refptr<media::VideoFrame>& frame,
+ const base::Time& frame_timestamp);
private:
void DeliverOnDeliverThread(int frame_number,
- const SkBitmap& frame_buffer,
+ const SkBitmap& frame,
const base::Time& frame_timestamp,
const base::Closure& done_cb);
+ void DeliverVideoFrameOnDeliverThread(
+ int frame_number,
+ const scoped_refptr<media::VideoFrame>& frame,
+ const base::Time& frame_timestamp);
+
+ // Treat |frame_number| as having been delivered, and update the
+ // frame rate statistics accordingly.
+ void ChronicleFrameDelivery(int frame_number);
base::Thread deliver_thread_;
SynchronizedConsumer* const consumer_;
@@ -351,88 +374,6 @@ void BackingStoreCopier::SetRenderWidgetHostForTesting(
rwh_for_testing_ = override;
}
-void BackingStoreCopier::StartCopy(int frame_number,
- int desired_width, int desired_height,
- const DoneCB& done_cb) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this,
- "frame_number", frame_number);
-
- RenderWidgetHost* rwh;
- if (rwh_for_testing_) {
- rwh = rwh_for_testing_;
- } else {
- if (!web_contents()) { // No source yet.
- LookUpAndObserveWebContents();
- if (!web_contents()) { // No source ever.
- done_cb.Run(NO_SOURCE, SkBitmap(), base::Time());
- return;
- }
- }
-
- if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
- RenderProcessHost* process = web_contents()->GetRenderProcessHost();
- rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_)
- : NULL;
- } else {
- rwh = web_contents()->GetRenderViewHost();
- }
-
- if (!rwh) {
- // Transient failure state (e.g., a RenderView is being replaced).
- done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time());
- return;
- }
- }
-
- gfx::Size fitted_size;
- if (RenderWidgetHostView* const view = rwh->GetView()) {
- const gfx::Size& view_size = view->GetViewBounds().size();
- if (!view_size.IsEmpty()) {
- CalculateFittedSize(view_size.width(), view_size.height(),
- desired_width, desired_height,
- &fitted_size);
- }
- if (view_size != last_view_size_) {
- last_view_size_ = view_size;
-
- // Measure the number of kilopixels.
- UMA_HISTOGRAM_COUNTS_10000(
- "TabCapture.ViewChangeKiloPixels",
- view_size.width() * view_size.height() / 1024);
- }
- }
-
- rwh->CopyFromBackingStore(
- gfx::Rect(),
- fitted_size,
- base::Bind(&BackingStoreCopier::CopyFromBackingStoreComplete,
- base::Unretained(this),
- frame_number, done_cb));
-
- // TODO(miu): When a tab is not visible to the user, rendering stops. For
- // mirroring, however, it's important that rendering continues to happen.
-}
-
-void BackingStoreCopier::CopyFromBackingStoreComplete(
- int frame_number,
- const DoneCB& done_cb,
- bool success,
- const SkBitmap& frame) {
- // Note: No restriction on which thread invokes this method but, currently,
- // it's always the UI BrowserThread.
- TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
- "frame_number", frame_number);
- if (success) {
- done_cb.Run(OK, frame, base::Time::Now());
- } else {
- // Capture can fail due to transient issues, so just skip this frame.
- DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame.";
- done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time());
- }
-}
-
VideoFrameRenderer::VideoFrameRenderer()
: render_thread_("WebContentsVideo_RenderThread") {
output_[0].in_use = false;
@@ -602,6 +543,15 @@ void SynchronizedConsumer::OnIncomingCapturedFrame(
}
}
+void SynchronizedConsumer::OnIncomingCapturedVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ const base::Time& timestamp) {
+ base::AutoLock guard(consumer_lock_);
+ if (wrapped_consumer_) {
+ wrapped_consumer_->OnIncomingCapturedVideoFrame(video_frame, timestamp);
+ }
+}
+
VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer)
: deliver_thread_("WebContentsVideo_DeliverThread"),
consumer_(consumer),
@@ -622,9 +572,20 @@ void VideoFrameDeliverer::Deliver(
done_cb));
}
+void VideoFrameDeliverer::Deliver(
+ int frame_number,
+ const scoped_refptr<media::VideoFrame>& frame,
+ const base::Time& frame_timestamp) {
+ deliver_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread,
+ base::Unretained(this), frame_number, frame, frame_timestamp));
+}
+
void VideoFrameDeliverer::DeliverOnDeliverThread(
int frame_number,
- const SkBitmap& frame_buffer, const base::Time& frame_timestamp,
+ const SkBitmap& frame_buffer,
+ const base::Time& frame_timestamp,
const base::Closure& done_cb) {
DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current());
@@ -639,6 +600,27 @@ void VideoFrameDeliverer::DeliverOnDeliverThread(
frame_buffer.getSize(),
frame_timestamp);
+ ChronicleFrameDelivery(frame_number);
+
+ // All done.
+ done_cb.Run();
+}
+
+void VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread(
+ int frame_number,
+ const scoped_refptr<media::VideoFrame>& frame,
+ const base::Time& frame_timestamp) {
+ DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current());
+
+ TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number);
+
+ // Send the frame to the consumer.
+ consumer_->OnIncomingCapturedVideoFrame(frame, frame_timestamp);
+
+ ChronicleFrameDelivery(frame_number);
+}
+
+void VideoFrameDeliverer::ChronicleFrameDelivery(int frame_number) {
// Log frame rate, if verbose logging is turned on.
static const base::TimeDelta kFrameRateLogInterval =
base::TimeDelta::FromSeconds(10);
@@ -669,9 +651,6 @@ void VideoFrameDeliverer::DeliverOnDeliverThread(
last_frame_number_ = frame_number;
}
}
-
- // All done.
- done_cb.Run();
}
} // namespace
@@ -687,6 +666,11 @@ void VideoFrameDeliverer::DeliverOnDeliverThread(
class CaptureMachine
: public base::RefCountedThreadSafe<CaptureMachine, CaptureMachine> {
public:
+ enum SnapshotError {
+ NO_SOURCE,
+ TRANSIENT_ERROR
+ };
+
CaptureMachine(int render_process_id, int render_view_id);
// Sets the capture source to the given |override| for unit testing.
@@ -707,6 +691,18 @@ class CaptureMachine
void Stop();
void DeAllocate();
+ // Snapshot result events.
+ void OnSnapshotComplete(int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const SkBitmap& frame);
+ void OnSnapshotComplete(int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const scoped_refptr<media::VideoFrame>& frame);
+ void OnSnapshotFailed(SnapshotError error,
+ int frame_number);
+
private:
friend class base::RefCountedThreadSafe<CaptureMachine, CaptureMachine>;
@@ -732,11 +728,18 @@ class CaptureMachine
// The glue between the pipeline stages.
void StartSnapshot();
- void SnapshotComplete(int frame_number,
- const base::Time& start_time,
- BackingStoreCopier::Result result,
- const SkBitmap& capture,
- const base::Time& capture_time);
+ bool FinishSnapshot();
+ void SnapshotCompleteBitmap(int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const SkBitmap& frame);
+ void SnapshotCompleteVideoFrame(
+ int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const scoped_refptr<media::VideoFrame>& frame);
+ void SnapshotFailed(SnapshotError error, int frame_number);
+
void RenderComplete(int frame_number,
const base::Time& capture_time,
const SkBitmap* frame_buffer);
@@ -826,6 +829,8 @@ void CaptureMachine::Allocate(int width, int height, int frame_rate) {
settings_.width = width;
settings_.height = height;
settings_.frame_rate = frame_rate;
+ // Sets the color format used by OnIncomingCapturedFrame().
+ // Does not apply to OnIncomingCapturedVideoFrame().
settings_.color = media::VideoCaptureCapability::kARGB;
settings_.expected_capture_delay = 0;
settings_.interlaced = false;
@@ -974,60 +979,92 @@ void CaptureMachine::StartSnapshot() {
if (!is_snapshotting_) {
is_snapshotting_ = true;
- const BackingStoreCopier::DoneCB& done_cb =
- media::BindToLoop(manager_thread_.message_loop_proxy(),
- base::Bind(&CaptureMachine::SnapshotComplete, this,
- frame_number_, base::Time::Now()));
- const base::Closure& start_cb =
- base::Bind(&BackingStoreCopier::StartCopy,
- base::Unretained(&copier_),
- frame_number_, settings_.width, settings_.height, done_cb);
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BackingStoreCopier::StartCopy, base::Unretained(&copier_),
+ make_scoped_refptr(this), frame_number_, settings_.width,
+ settings_.height));
}
ScheduleNextFrameCapture();
}
-void CaptureMachine::SnapshotComplete(int frame_number,
- const base::Time& start_time,
- BackingStoreCopier::Result result,
- const SkBitmap& capture,
- const base::Time& capture_time) {
+bool CaptureMachine::FinishSnapshot() {
DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current());
DCHECK(is_snapshotting_);
is_snapshotting_ = false;
- if (state_ != kCapturing) {
+ return state_ == kCapturing;
+}
+
+void CaptureMachine::OnSnapshotComplete(int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const SkBitmap& frame) {
+ manager_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&CaptureMachine::SnapshotCompleteBitmap, this,
+ frame_number, start_time, duration, frame));
+}
+
+void CaptureMachine::SnapshotCompleteBitmap(int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const SkBitmap& capture) {
+ if (!FinishSnapshot())
return;
+
+ UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration);
+ if (num_renders_pending_ <= 1) {
+ ++num_renders_pending_;
+ renderer_.Render(
+ frame_number,
+ capture,
+ settings_.width, settings_.height,
+ media::BindToLoop(manager_thread_.message_loop_proxy(),
+ base::Bind(&CaptureMachine::RenderComplete, this,
+ frame_number, start_time + duration)));
}
+}
- switch (result) {
- case BackingStoreCopier::OK:
- UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime",
- base::Time::Now() - start_time);
- if (num_renders_pending_ <= 1) {
- ++num_renders_pending_;
- DCHECK(!capture_time.is_null());
- renderer_.Render(
- frame_number,
- capture,
- settings_.width, settings_.height,
- media::BindToLoop(manager_thread_.message_loop_proxy(),
- base::Bind(&CaptureMachine::RenderComplete, this,
- frame_number, capture_time)));
- }
- break;
+void CaptureMachine::OnSnapshotComplete(
+ int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const scoped_refptr<media::VideoFrame>& frame) {
+ manager_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&CaptureMachine::SnapshotCompleteVideoFrame, this,
+ frame_number, start_time, duration, frame));
+}
- case BackingStoreCopier::TRANSIENT_ERROR:
- // Skip this frame.
- break;
+void CaptureMachine::SnapshotCompleteVideoFrame(
+ int frame_number,
+ const base::Time& start_time,
+ const base::TimeDelta& duration,
+ const scoped_refptr<media::VideoFrame>& frame) {
+ if (!FinishSnapshot())
+ return;
- case BackingStoreCopier::NO_SOURCE:
- DVLOG(1) << "no capture source";
- Error();
- break;
- }
+ UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration);
+
+ deliverer_.Deliver(frame_number, frame, start_time + duration);
+}
+
+void CaptureMachine::OnSnapshotFailed(CaptureMachine::SnapshotError error,
+ int frame_number) {
+ manager_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&CaptureMachine::SnapshotFailed, this, error, frame_number));
+}
+
+void CaptureMachine::SnapshotFailed(CaptureMachine::SnapshotError error,
+ int frame_number) {
+ if (!FinishSnapshot())
+ return;
+
+ if (error == NO_SOURCE)
+ Error();
}
void CaptureMachine::RenderComplete(int frame_number,
@@ -1057,6 +1094,138 @@ void CaptureMachine::DoShutdownTasksOnUIThread() {
copier_.StopObservingWebContents();
}
+void BackingStoreCopier::StartCopy(
+ const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ int desired_width,
+ int desired_height) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RenderWidgetHost* rwh;
+ if (rwh_for_testing_) {
+ rwh = rwh_for_testing_;
+ } else {
+ if (!web_contents()) { // No source yet.
+ LookUpAndObserveWebContents();
+ if (!web_contents()) { // No source ever.
+ consumer->OnSnapshotFailed(CaptureMachine::NO_SOURCE, frame_number);
+ return;
+ }
+ }
+
+ if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
+ RenderProcessHost* process = web_contents()->GetRenderProcessHost();
+ rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_)
+ : NULL;
+ } else {
+ rwh = web_contents()->GetRenderViewHost();
+ }
+
+ if (!rwh) {
+ // Transient failure state (e.g., a RenderView is being replaced).
+ consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number);
+ return;
+ }
+ }
+
+ RenderWidgetHostViewPort* view =
+ RenderWidgetHostViewPort::FromRWHV(rwh->GetView());
+
+ gfx::Size fitted_size;
+ if (view) {
+ gfx::Size view_size = view->GetViewBounds().size();
+ if (!view_size.IsEmpty()) {
+ CalculateFittedSize(view_size.width(), view_size.height(),
+ desired_width, desired_height,
+ &fitted_size);
+ }
+ if (view_size != last_view_size_) {
+ last_view_size_ = view_size;
+
+ // Measure the number of kilopixels.
+ UMA_HISTOGRAM_COUNTS_10000(
+ "TabCapture.ViewChangeKiloPixels",
+ view_size.width() * view_size.height() / 1024);
+ }
+ }
+
+ TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this,
+ "frame_number", frame_number);
+
+ if (view && view->CanCopyToVideoFrame()) {
+ gfx::Size view_size = view->GetViewBounds().size();
+ gfx::Size dst_size(desired_width, desired_height);
+ scoped_refptr<media::VideoFrame> video_frame(
+ media::VideoFrame::CreateFrame(
+ media::VideoFrame::YV12,
+ dst_size,
+ gfx::Rect(dst_size),
+ dst_size,
+ base::TimeDelta()));
+
+ view->CopyFromCompositingSurfaceToVideoFrame(
+ gfx::Rect(view_size),
+ video_frame,
+ base::Bind(&BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame,
+ base::Unretained(this), consumer, frame_number,
+ base::Time::Now(), video_frame));
+ } else {
+ rwh->CopyFromBackingStore(
+ gfx::Rect(),
+ fitted_size,
+ base::Bind(&BackingStoreCopier::DidCopyFromBackingStore,
+ base::Unretained(this), consumer, frame_number,
+ base::Time::Now()));
+ }
+ // TODO(miu): When a tab is not visible to the user, rendering stops. For
+ // mirroring, however, it's important that rendering continues to happen.
+}
+
+void BackingStoreCopier::DidCopyFromBackingStore(
+ const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ const base::Time& start_time,
+ bool success,
+ const SkBitmap& frame) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Note: No restriction on which thread invokes this method but, currently,
+ // it's always the UI BrowserThread.
+ TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
+ "frame_number", frame_number);
+
+ if (success) {
+ consumer->OnSnapshotComplete(
+ frame_number, start_time, base::Time::Now() - start_time, frame);
+ } else {
+ // Capture can fail due to transient issues, so just skip this frame.
+ DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame.";
+ consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number);
+ }
+}
+
+void BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame(
+ const scoped_refptr<CaptureMachine>& consumer,
+ int frame_number,
+ const base::Time& start_time,
+ const scoped_refptr<media::VideoFrame>& frame,
+ bool success) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Note: No restriction on which thread invokes this method but, currently,
+ // it's always the UI BrowserThread.
+ TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
+ "frame_number", frame_number);
+
+ if (success) {
+ consumer->OnSnapshotComplete(
+ frame_number, start_time, base::Time::Now() - start_time, frame);
+ } else {
+ // Capture can fail due to transient issues, so just skip this frame.
+ DVLOG(1) << "CopyFromBackingStoreToVideoFrame failure; skipping frame.";
+ consumer->OnSnapshotFailed(
+ CaptureMachine::TRANSIENT_ERROR, frame_number);
+ }
+}
+
WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
const media::VideoCaptureDevice::Name& name,
int render_process_id, int render_view_id)

Powered by Google App Engine
This is Rietveld 408576698