Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/renderer_host/media/screen_capturer.h" | |
| 6 | |
| 7 #include "remoting/capturer/capture_data.h" | |
| 8 #include "remoting/capturer/mouse_cursor_shape.h" | |
| 9 #include "third_party/skia/include/core/SkCanvas.h" | |
| 10 #include "third_party/skia/include/core/SkDevice.h" | |
| 11 | |
| 12 namespace content { | |
| 13 | |
| 14 namespace { | |
| 15 const int kBytesPerPixel = 4; | |
| 16 } // namespace | |
| 17 | |
| 18 ScreenCapturer::ScreenCapturer() | |
| 19 : capture_thread_("Screen Capture Thread"), | |
| 20 event_handler_(NULL), | |
| 21 capture_pending_(false), | |
| 22 waiting_frame_size_(false), | |
| 23 started_(false) { | |
| 24 name_.device_name = "Screen"; | |
| 25 } | |
| 26 | |
| 27 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.
| |
| 28 } | |
| 29 | |
| 30 void ScreenCapturer::Allocate(int width, int height, | |
| 31 int frame_rate, | |
| 32 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.
| |
| 33 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.
| |
| 34 | |
| 35 // base::Unretained is safe because we control lifetime of the thread. | |
| 36 capture_thread_.message_loop()->PostTask( | |
| 37 FROM_HERE, base::Bind(&ScreenCapturer::DoAllocate, base::Unretained(this), | |
| 38 frame_rate, event_handler)); | |
| 39 } | |
| 40 | |
| 41 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.
| |
| 42 capture_thread_.message_loop()->PostTask( | |
| 43 FROM_HERE, base::Bind(&ScreenCapturer::DoStart, base::Unretained(this))); | |
| 44 } | |
| 45 | |
| 46 void ScreenCapturer::Stop() { | |
| 47 capture_thread_.message_loop()->PostTask( | |
| 48 FROM_HERE, base::Bind(&ScreenCapturer::DoStop, base::Unretained(this))); | |
| 49 } | |
| 50 | |
| 51 void ScreenCapturer::DeAllocate() { | |
| 52 if (capture_thread_.IsRunning()) { | |
| 53 capture_thread_.message_loop()->PostTask( | |
| 54 FROM_HERE, base::Bind(&ScreenCapturer::DoDeAllocate, | |
| 55 base::Unretained(this))); | |
| 56 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
| |
| 57 } | |
| 58 } | |
| 59 | |
| 60 const media::VideoCaptureDevice::Name& ScreenCapturer::device_name() { | |
| 61 return name_; | |
| 62 } | |
| 63 | |
| 64 void ScreenCapturer::OnCaptureCompleted( | |
| 65 scoped_refptr<remoting::CaptureData> capture_data) { | |
| 66 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 67 DCHECK(capture_pending_); | |
| 68 capture_pending_ = false; | |
| 69 | |
| 70 if (waiting_frame_size_) { | |
| 71 frame_size_ = capture_data->size(); | |
| 72 waiting_frame_size_ = false; | |
| 73 | |
| 74 // 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.
| |
| 75 media::VideoCaptureCapability caps; | |
| 76 caps.width = frame_size_.width(); | |
| 77 caps.height = frame_size_.height(); | |
| 78 caps.frame_rate = frame_rate_; | |
| 79 caps.color = media::VideoCaptureCapability::kARGB; | |
| 80 caps.expected_capture_delay = | |
| 81 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
| |
| 82 caps.interlaced = false; | |
| 83 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
| |
| 84 } | |
| 85 | |
| 86 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
| |
| 87 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
| |
| 88 size_t buffer_size = | |
| 89 frame_size_.width() * frame_size_.height() * | |
| 90 remoting::CaptureData::kBytesPerPixel; | |
| 91 | |
| 92 if (capture_data->size() == frame_size_) { | |
| 93 // 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.
| |
| 94 resized_bitmap_.reset(); | |
| 95 event_handler_->OnIncomingCapturedFrame( | |
| 96 capture_data->data(), buffer_size, base::Time::Now()); | |
| 97 return; | |
| 98 } | |
| 99 | |
| 100 // 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
| |
| 101 // is stored to |resized_bitmap_|. Only regions of the screen that are | |
| 102 // changing are copied. | |
| 103 | |
| 104 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.
| |
| 105 | |
| 106 if (resized_bitmap_.width() != frame_size_.width() || | |
| 107 resized_bitmap_.height() != frame_size_.height()) { | |
| 108 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, | |
| 109 frame_size_.width(), frame_size_.height()); | |
| 110 resized_bitmap_.setIsOpaque(true); | |
| 111 resized_bitmap_.allocPixels(); | |
| 112 redraw_all = true; | |
| 113 } | |
| 114 | |
| 115 float scale_x = static_cast<float>(frame_size_.width()) / | |
| 116 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.
| |
| 117 float scale_y = static_cast<float>(frame_size_.height()) / | |
| 118 capture_data->size().height(); | |
| 119 float scale; | |
| 120 float x, y; | |
| 121 // Center the image in case aspect ratio is different. | |
| 122 if (scale_x > scale_y) { | |
| 123 scale = scale_y; | |
| 124 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; | |
| 125 y = 0; | |
|
Wez
2013/01/10 00:58:14
nit: 0.0f
Sergey Ulanov
2013/01/11 23:46:20
Done.
| |
| 126 } else { | |
| 127 scale = scale_x; | |
| 128 x = 0; | |
| 129 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; | |
| 130 } | |
| 131 | |
| 132 SkDevice device(resized_bitmap_); | |
| 133 SkCanvas canvas(&device); | |
| 134 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.
| |
| 135 | |
| 136 if (redraw_all) { | |
| 137 SkBitmap source_bitmap; | |
| 138 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
| 139 capture_data->size().width(), | |
| 140 capture_data->size().height()); | |
| 141 source_bitmap.setIsOpaque(true); | |
| 142 source_bitmap.setPixels(capture_data->data()); | |
| 143 canvas.drawBitmap(source_bitmap, x / scale, y / scale, NULL); | |
| 144 } else { | |
| 145 int source_stride = capture_data->stride(); | |
| 146 for (SkRegion::Iterator i(capture_data->dirty_region()); | |
| 147 !i.done(); i.next()) { | |
| 148 SkBitmap source_bitmap; | |
| 149 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
| 150 i.rect().width(), i.rect().height(), | |
| 151 source_stride); | |
| 152 source_bitmap.setIsOpaque(true); | |
| 153 source_bitmap.setPixels( | |
| 154 capture_data->data() + i.rect().top() * source_stride + | |
| 155 i.rect().left() * remoting::CaptureData::kBytesPerPixel); | |
| 156 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, | |
| 157 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
| |
| 158 } | |
| 159 } | |
| 160 | |
| 161 event_handler_->OnIncomingCapturedFrame( | |
| 162 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, | |
| 163 base::Time::Now()); | |
| 164 } | |
| 165 | |
| 166 void ScreenCapturer::OnCursorShapeChanged( | |
| 167 scoped_ptr<remoting::MouseCursorShape> cursor_shape) { | |
| 168 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 169 } | |
| 170 | |
| 171 void ScreenCapturer::DoAllocate(int frame_rate, | |
| 172 EventHandler* event_handler) { | |
| 173 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 174 frame_rate_ = frame_rate; | |
| 175 event_handler_ = event_handler; | |
| 176 if (!video_frame_capturer_) | |
| 177 video_frame_capturer_ = remoting::VideoFrameCapturer::Create(); | |
| 178 if (video_frame_capturer_) | |
| 179 video_frame_capturer_->Start(this); | |
| 180 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.
| |
| 181 DoCapture(); | |
| 182 } | |
| 183 | |
| 184 void ScreenCapturer::DoStart() { | |
| 185 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 186 started_ = true; | |
| 187 if (video_frame_capturer_) { | |
| 188 timer_.reset(new base::RepeatingTimer<ScreenCapturer>()); | |
| 189 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / frame_rate_, | |
| 190 base::Bind(&ScreenCapturer::DoCapture, | |
| 191 base::Unretained(this))); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void ScreenCapturer::DoStop() { | |
| 196 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 197 started_ = false; | |
| 198 if (video_frame_capturer_) { | |
| 199 timer_.reset(); | |
| 200 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.
| |
| 201 } | |
| 202 } | |
| 203 | |
| 204 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.
| |
| 205 DCHECK(capture_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 206 video_frame_capturer_.reset(); | |
| 207 event_handler_ = NULL; | |
| 208 waiting_frame_size_ = false; | |
| 209 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().
| |
| 210 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.
| |
| 211 } | |
| 212 | |
| 213 void ScreenCapturer::DoCapture() { | |
| 214 if (capture_pending_) | |
| 215 return; | |
| 216 capture_pending_ = true; | |
| 217 video_frame_capturer_->CaptureFrame(); | |
| 218 } | |
| 219 | |
| 220 } // namespace content | |
| OLD | NEW |