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

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

Issue 11680002: Implement screen capturer for MediaStream API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years 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/screen_capturer.cc
diff --git a/content/browser/renderer_host/media/screen_capturer.cc b/content/browser/renderer_host/media/screen_capturer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3293917658bc1edca8b71090ff6a168fd160b3fd
--- /dev/null
+++ b/content/browser/renderer_host/media/screen_capturer.cc
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 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/screen_capturer.h"
+
+#include "remoting/capturer/capture_data.h"
+#include "remoting/capturer/mouse_cursor_shape.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace content {
+
+namespace {
+const int kBytesPerPixel = 4;
+} // namespace
+
+ScreenCapturer::ScreenCapturer()
+ : capture_thread_("Screen Capture Thread"),
+ event_handler_(NULL),
+ capture_pending_(false),
+ waiting_frame_size_(false),
+ started_(false) {
+ name_.device_name = "Screen";
+}
+
+ScreenCapturer::~ScreenCapturer() {
Wez 2013/01/10 00:58:14 If the caller hasn't called DeAllocate then we sti
Sergey Ulanov 2013/01/11 23:46:20 Added DeAllocate.
+}
+
+void ScreenCapturer::Allocate(int width, int height,
+ int frame_rate,
+ EventHandler* event_handler) {
Wez 2013/01/10 00:58:14 nit: Consider [D]CHECKing the parameters. e.g. fra
Sergey Ulanov 2013/01/11 23:46:20 Done.
+ capture_thread_.Start();
Wez 2013/01/10 00:58:14 Is there no other thread or thread pool we can all
Sergey Ulanov 2013/01/11 23:46:20 Changed this code to use blocking IO pool.
+
+ // base::Unretained is safe because we control lifetime of the thread.
+ capture_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&ScreenCapturer::DoAllocate, base::Unretained(this),
+ frame_rate, event_handler));
+}
+
+void ScreenCapturer::Start() {
wjia(left Chromium) 2013/01/07 22:15:26 check capture_thread_.IsRunning() here too?
Sergey Ulanov 2013/01/09 20:35:30 Done.
+ capture_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&ScreenCapturer::DoStart, base::Unretained(this)));
+}
+
+void ScreenCapturer::Stop() {
+ capture_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&ScreenCapturer::DoStop, base::Unretained(this)));
+}
+
+void ScreenCapturer::DeAllocate() {
+ if (capture_thread_.IsRunning()) {
+ capture_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&ScreenCapturer::DoDeAllocate,
+ base::Unretained(this)));
+ capture_thread_.Stop();
Wez 2013/01/10 00:58:14 Does it matter that this might block e.g. for half
Sergey Ulanov 2013/01/11 23:46:20 Removed capture_thread_. thread pool is used inste
+ }
+}
+
+const media::VideoCaptureDevice::Name& ScreenCapturer::device_name() {
+ return name_;
+}
+
+void ScreenCapturer::OnCaptureCompleted(
+ scoped_refptr<remoting::CaptureData> capture_data) {
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ DCHECK(capture_pending_);
+ capture_pending_ = false;
+
+ if (waiting_frame_size_) {
+ frame_size_ = capture_data->size();
+ waiting_frame_size_ = false;
+
+ // Invoke OnFrameInfo().
Wez 2013/01/10 00:58:14 nit: Please make this more descriptive, e.g. "Info
Sergey Ulanov 2013/01/11 23:46:20 Done.
+ media::VideoCaptureCapability caps;
+ caps.width = frame_size_.width();
+ caps.height = frame_size_.height();
+ caps.frame_rate = frame_rate_;
+ caps.color = media::VideoCaptureCapability::kARGB;
+ caps.expected_capture_delay =
+ base::Time::kMillisecondsPerSecond / frame_rate_;
Wez 2013/01/10 00:58:14 Does it matter that the host platform might not be
Sergey Ulanov 2013/01/11 23:46:20 I don't think so. In either case we have no way to
+ caps.interlaced = false;
+ event_handler_->OnFrameInfo(caps);
Wez 2013/01/10 00:58:14 This calls the EventHandler on the capture thread
Sergey Ulanov 2013/01/11 23:46:20 That's the same behavior that we have in device vi
+ }
+
+ if (!started_)
Wez 2013/01/10 00:58:14 This is happening on the same thread that DoStop()
Sergey Ulanov 2013/01/11 23:46:20 OnFrameInfo() needs to be called in response to Al
+ return;
Wez 2013/01/10 00:58:14 Check this before the |waiting_frame_size_| check
Sergey Ulanov 2013/01/11 23:46:20 See my previous comment. OnFrameInfo() doesn't dep
+ size_t buffer_size =
+ frame_size_.width() * frame_size_.height() *
+ remoting::CaptureData::kBytesPerPixel;
+
+ if (capture_data->size() == frame_size_) {
+ // Invalidate resized frame buffer if we previously allocated it.
Wez 2013/01/10 00:58:14 Reword this comment and place it before the if, e.
Sergey Ulanov 2013/01/11 23:46:20 Done.
+ resized_bitmap_.reset();
+ event_handler_->OnIncomingCapturedFrame(
+ capture_data->data(), buffer_size, base::Time::Now());
+ return;
+ }
+
+ // In case screen size has changed we need to resize the image. Resized image
Wez 2013/01/10 00:58:14 Do we really have no way to inform the caller of t
Sergey Ulanov 2013/01/11 23:46:20 No. The interface was designed for webcams and the
+ // is stored to |resized_bitmap_|. Only regions of the screen that are
+ // changing are copied.
+
+ bool redraw_all = false;
Wez 2013/01/10 00:58:14 Why not re-set the CaptureData's rects to contain
Sergey Ulanov 2013/01/11 23:46:20 Done.
+
+ if (resized_bitmap_.width() != frame_size_.width() ||
+ resized_bitmap_.height() != frame_size_.height()) {
+ resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
+ frame_size_.width(), frame_size_.height());
+ resized_bitmap_.setIsOpaque(true);
+ resized_bitmap_.allocPixels();
+ redraw_all = true;
+ }
+
+ float scale_x = static_cast<float>(frame_size_.width()) /
+ capture_data->size().width();
Wez 2013/01/10 00:58:14 Our capturers won't produce 0x0 frames, but if the
Sergey Ulanov 2013/01/11 23:46:20 Added DCHECK on top of this method.
+ float scale_y = static_cast<float>(frame_size_.height()) /
+ capture_data->size().height();
+ float scale;
+ float x, y;
+ // Center the image in case aspect ratio is different.
+ if (scale_x > scale_y) {
+ scale = scale_y;
+ x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0;
+ y = 0;
Wez 2013/01/10 00:58:14 nit: 0.0f
Sergey Ulanov 2013/01/11 23:46:20 Done.
+ } else {
+ scale = scale_x;
+ x = 0;
+ y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0;
+ }
+
+ SkDevice device(resized_bitmap_);
+ SkCanvas canvas(&device);
+ canvas.scale(scale, scale);
Wez 2013/01/10 00:58:14 Add a comment before this block to summarize the p
Sergey Ulanov 2013/01/11 23:46:20 Done.
+
+ if (redraw_all) {
+ SkBitmap source_bitmap;
+ source_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ capture_data->size().width(),
+ capture_data->size().height());
+ source_bitmap.setIsOpaque(true);
+ source_bitmap.setPixels(capture_data->data());
+ canvas.drawBitmap(source_bitmap, x / scale, y / scale, NULL);
+ } else {
+ int source_stride = capture_data->stride();
+ for (SkRegion::Iterator i(capture_data->dirty_region());
+ !i.done(); i.next()) {
+ SkBitmap source_bitmap;
+ source_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ i.rect().width(), i.rect().height(),
+ source_stride);
+ source_bitmap.setIsOpaque(true);
+ source_bitmap.setPixels(
+ capture_data->data() + i.rect().top() * source_stride +
+ i.rect().left() * remoting::CaptureData::kBytesPerPixel);
+ canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale,
+ i.rect().top() + y / scale, NULL);
Wez 2013/01/10 00:58:14 Does this do the right thing wrt sub-pixel destina
Sergey Ulanov 2013/01/11 23:46:20 Yes, as far as I know, but it uses simple nearest
+ }
+ }
+
+ event_handler_->OnIncomingCapturedFrame(
+ reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size,
+ base::Time::Now());
+}
+
+void ScreenCapturer::OnCursorShapeChanged(
+ scoped_ptr<remoting::MouseCursorShape> cursor_shape) {
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+}
+
+void ScreenCapturer::DoAllocate(int frame_rate,
+ EventHandler* event_handler) {
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ frame_rate_ = frame_rate;
+ event_handler_ = event_handler;
+ if (!video_frame_capturer_)
+ video_frame_capturer_ = remoting::VideoFrameCapturer::Create();
+ if (video_frame_capturer_)
+ video_frame_capturer_->Start(this);
+ waiting_frame_size_ = true;
Wez 2013/01/10 00:58:14 nit: If you need this, set it before Start()ing th
Wez 2013/01/10 00:58:14 nit: Add blank lines around frame_rate and event_h
Sergey Ulanov 2013/01/11 23:46:20 Start() doesn't really start anything. DoCapture()
Sergey Ulanov 2013/01/11 23:46:20 Done.
+ DoCapture();
+}
+
+void ScreenCapturer::DoStart() {
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ started_ = true;
+ if (video_frame_capturer_) {
+ timer_.reset(new base::RepeatingTimer<ScreenCapturer>());
+ timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / frame_rate_,
+ base::Bind(&ScreenCapturer::DoCapture,
+ base::Unretained(this)));
+ }
+}
+
+void ScreenCapturer::DoStop() {
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ started_ = false;
+ if (video_frame_capturer_) {
+ timer_.reset();
+ video_frame_capturer_->Stop();
wjia(left Chromium) 2013/01/07 22:15:26 Is this an synchronous API? ScreenCapturer wouldn'
Sergey Ulanov 2013/01/09 20:35:30 Yes.
+ }
+}
+
+void ScreenCapturer::DoDeAllocate() {
Wez 2013/01/10 00:58:14 ALso deallocate the resize buffer here.
Sergey Ulanov 2013/01/11 23:46:20 Added it in DoStop.
+ DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ video_frame_capturer_.reset();
+ event_handler_ = NULL;
+ waiting_frame_size_ = false;
+ started_ = false;
Wez 2013/01/10 00:58:14 We should already be stopped via Stop?
Sergey Ulanov 2013/01/11 23:46:20 DeAllocate can be called without calling Stop().
+ capture_pending_ = true;
Wez 2013/01/10 00:58:14 Why should capture_pending be true when deallocate
Sergey Ulanov 2013/01/11 23:46:20 Fixed now.
+}
+
+void ScreenCapturer::DoCapture() {
+ if (capture_pending_)
+ return;
+ capture_pending_ = true;
+ video_frame_capturer_->CaptureFrame();
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698