Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/media/capture/aura_window_capture_machine.h" | 5 #include "content/browser/media/capture/aura_window_capture_machine.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
|
xjz
2015/10/22 17:08:41
No need to include this.
Irfan
2015/10/26 23:10:31
see my earlier comment
| |
| 8 | |
| 7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 8 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 9 #include "base/timer/timer.h" | 11 #include "base/timer/timer.h" |
| 10 #include "cc/output/copy_output_request.h" | 12 #include "cc/output/copy_output_request.h" |
| 11 #include "cc/output/copy_output_result.h" | 13 #include "cc/output/copy_output_result.h" |
| 12 #include "content/browser/compositor/image_transport_factory.h" | 14 #include "content/browser/compositor/image_transport_factory.h" |
| 13 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" | 15 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" |
| 14 #include "content/common/gpu/client/gl_helper.h" | 16 #include "content/common/gpu/client/gl_helper.h" |
| 15 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
| 16 #include "content/public/browser/power_save_blocker.h" | 18 #include "content/public/browser/power_save_blocker.h" |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 28 #include "ui/base/cursor/cursors_aura.h" | 30 #include "ui/base/cursor/cursors_aura.h" |
| 29 #include "ui/compositor/compositor.h" | 31 #include "ui/compositor/compositor.h" |
| 30 #include "ui/compositor/dip_util.h" | 32 #include "ui/compositor/dip_util.h" |
| 31 #include "ui/compositor/layer.h" | 33 #include "ui/compositor/layer.h" |
| 32 #include "ui/gfx/screen.h" | 34 #include "ui/gfx/screen.h" |
| 33 | 35 |
| 34 namespace content { | 36 namespace content { |
| 35 | 37 |
| 36 namespace { | 38 namespace { |
| 37 | 39 |
| 38 int clip_byte(int x) { | |
| 39 return std::max(0, std::min(x, 255)); | |
| 40 } | |
| 41 | |
| 42 int alpha_blend(int alpha, int src, int dst) { | |
| 43 return (src * alpha + dst * (255 - alpha)) / 255; | |
| 44 } | |
| 45 | |
| 46 // Helper function to composite a cursor bitmap on a YUV420 video frame. | |
| 47 void RenderCursorOnVideoFrame( | |
| 48 const scoped_refptr<media::VideoFrame>& target, | |
| 49 const SkBitmap& cursor_bitmap, | |
| 50 const gfx::Point& cursor_position) { | |
| 51 DCHECK(target.get()); | |
| 52 DCHECK(!cursor_bitmap.isNull()); | |
| 53 | |
| 54 gfx::Rect rect = gfx::IntersectRects( | |
| 55 gfx::Rect(cursor_bitmap.width(), cursor_bitmap.height()) + | |
| 56 gfx::Vector2d(cursor_position.x(), cursor_position.y()), | |
| 57 target->visible_rect()); | |
| 58 | |
| 59 cursor_bitmap.lockPixels(); | |
| 60 for (int y = rect.y(); y < rect.bottom(); ++y) { | |
| 61 int cursor_y = y - cursor_position.y(); | |
| 62 uint8* yplane = target->data(media::VideoFrame::kYPlane) + | |
| 63 y * target->row_bytes(media::VideoFrame::kYPlane); | |
| 64 uint8* uplane = target->data(media::VideoFrame::kUPlane) + | |
| 65 (y / 2) * target->row_bytes(media::VideoFrame::kUPlane); | |
| 66 uint8* vplane = target->data(media::VideoFrame::kVPlane) + | |
| 67 (y / 2) * target->row_bytes(media::VideoFrame::kVPlane); | |
| 68 for (int x = rect.x(); x < rect.right(); ++x) { | |
| 69 int cursor_x = x - cursor_position.x(); | |
| 70 SkColor color = cursor_bitmap.getColor(cursor_x, cursor_y); | |
| 71 int alpha = SkColorGetA(color); | |
| 72 int color_r = SkColorGetR(color); | |
| 73 int color_g = SkColorGetG(color); | |
| 74 int color_b = SkColorGetB(color); | |
| 75 int color_y = clip_byte(((color_r * 66 + color_g * 129 + color_b * 25 + | |
| 76 128) >> 8) + 16); | |
| 77 yplane[x] = alpha_blend(alpha, color_y, yplane[x]); | |
| 78 | |
| 79 // Only sample U and V at even coordinates. | |
| 80 if ((x % 2 == 0) && (y % 2 == 0)) { | |
| 81 int color_u = clip_byte(((color_r * -38 + color_g * -74 + | |
| 82 color_b * 112 + 128) >> 8) + 128); | |
| 83 int color_v = clip_byte(((color_r * 112 + color_g * -94 + | |
| 84 color_b * -18 + 128) >> 8) + 128); | |
| 85 uplane[x / 2] = alpha_blend(alpha, color_u, uplane[x / 2]); | |
| 86 vplane[x / 2] = alpha_blend(alpha, color_v, vplane[x / 2]); | |
| 87 } | |
| 88 } | |
| 89 } | |
| 90 cursor_bitmap.unlockPixels(); | |
| 91 } | |
| 92 | |
| 93 using CaptureFrameCallback = | 40 using CaptureFrameCallback = |
| 94 media::ThreadSafeCaptureOracle::CaptureFrameCallback; | 41 media::ThreadSafeCaptureOracle::CaptureFrameCallback; |
| 95 | 42 |
| 96 void CopyOutputFinishedForVideo( | 43 void CopyOutputFinishedForVideo( |
| 97 base::WeakPtr<AuraWindowCaptureMachine> machine, | 44 base::WeakPtr<AuraWindowCaptureMachine> machine, |
| 98 base::TimeTicks start_time, | 45 base::TimeTicks start_time, |
| 99 const CaptureFrameCallback& capture_frame_cb, | 46 const CaptureFrameCallback& capture_frame_cb, |
| 100 const scoped_refptr<media::VideoFrame>& target, | 47 const scoped_refptr<media::VideoFrame>& target, |
| 101 const SkBitmap& cursor_bitmap, | 48 const CursorRenderer* const cursor_renderer, |
|
miu
2015/10/23 01:55:12
Since the CursorRenderer is owned by AuraWindowCap
Irfan
2015/10/26 23:10:31
Done.
| |
| 102 const gfx::Point& cursor_position, | |
| 103 scoped_ptr<cc::SingleReleaseCallback> release_callback, | 49 scoped_ptr<cc::SingleReleaseCallback> release_callback, |
| 104 bool result) { | 50 bool result) { |
| 105 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 51 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 106 | 52 |
| 107 if (!cursor_bitmap.isNull()) | 53 if (cursor_renderer && result) |
| 108 RenderCursorOnVideoFrame(target, cursor_bitmap, cursor_position); | 54 cursor_renderer->RenderOnVideoFrame(target); |
|
miu
2015/10/23 01:55:12
The |cursor_renderer| pointer is invalid if the Au
Irfan
2015/10/26 23:10:31
Done.
| |
| 109 release_callback->Run(0, false); | 55 release_callback->Run(0, false); |
| 110 | 56 |
| 111 // Only deliver the captured frame if the AuraWindowCaptureMachine has not | 57 // Only deliver the captured frame if the AuraWindowCaptureMachine has not |
| 112 // been stopped (i.e., the WeakPtr is still valid). | 58 // been stopped (i.e., the WeakPtr is still valid). |
| 113 if (machine.get()) | 59 if (machine.get()) |
| 114 capture_frame_cb.Run(target, start_time, result); | 60 capture_frame_cb.Run(target, start_time, result); |
| 115 } | 61 } |
| 116 | 62 |
| 117 void RunSingleReleaseCallback(scoped_ptr<cc::SingleReleaseCallback> cb, | 63 void RunSingleReleaseCallback(scoped_ptr<cc::SingleReleaseCallback> cb, |
| 118 uint32 sync_point) { | 64 uint32 sync_point) { |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 204 power_save_blocker_.reset(); | 150 power_save_blocker_.reset(); |
| 205 | 151 |
| 206 // Stop observing compositor and window events. | 152 // Stop observing compositor and window events. |
| 207 if (desktop_window_) { | 153 if (desktop_window_) { |
| 208 aura::WindowTreeHost* window_host = desktop_window_->GetHost(); | 154 aura::WindowTreeHost* window_host = desktop_window_->GetHost(); |
| 209 // In the host destructor the compositor is destroyed before the window. | 155 // In the host destructor the compositor is destroyed before the window. |
| 210 if (window_host && window_host->compositor()) | 156 if (window_host && window_host->compositor()) |
| 211 window_host->compositor()->RemoveObserver(this); | 157 window_host->compositor()->RemoveObserver(this); |
| 212 desktop_window_->RemoveObserver(this); | 158 desktop_window_->RemoveObserver(this); |
| 213 desktop_window_ = NULL; | 159 desktop_window_ = NULL; |
| 160 cursor_renderer_.reset(); | |
| 214 } | 161 } |
| 215 | 162 |
| 216 // Stop timer. | 163 // Stop timer. |
| 217 timer_.Stop(); | 164 timer_.Stop(); |
| 218 | 165 |
| 219 callback.Run(); | 166 callback.Run(); |
| 220 } | 167 } |
| 221 | 168 |
| 222 void AuraWindowCaptureMachine::SetWindow(aura::Window* window) { | 169 void AuraWindowCaptureMachine::SetWindow(aura::Window* window) { |
| 223 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 170 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 224 | 171 |
| 225 DCHECK(!desktop_window_); | 172 DCHECK(!desktop_window_); |
| 226 desktop_window_ = window; | 173 desktop_window_ = window; |
| 174 cursor_renderer_.reset(new CursorRendererAura(window)); | |
| 227 | 175 |
| 228 // Start observing window events. | 176 // Start observing window events. |
| 229 desktop_window_->AddObserver(this); | 177 desktop_window_->AddObserver(this); |
| 230 | 178 |
| 231 // We must store this for the UMA reporting in DidCopyOutput() as | 179 // We must store this for the UMA reporting in DidCopyOutput() as |
| 232 // desktop_window_ might be destroyed at that point. | 180 // desktop_window_ might be destroyed at that point. |
| 233 screen_capture_ = window->IsRootWindow(); | 181 screen_capture_ = window->IsRootWindow(); |
| 234 IncrementDesktopCaptureCounter(screen_capture_ ? SCREEN_CAPTURER_CREATED | 182 IncrementDesktopCaptureCounter(screen_capture_ ? SCREEN_CAPTURER_CREATED |
| 235 : WINDOW_CAPTURER_CREATED); | 183 : WINDOW_CAPTURER_CREATED); |
| 236 } | 184 } |
| 237 | 185 |
| 238 void AuraWindowCaptureMachine::UpdateCaptureSize() { | 186 void AuraWindowCaptureMachine::UpdateCaptureSize() { |
| 239 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 187 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 240 if (oracle_proxy_.get() && desktop_window_) { | 188 if (oracle_proxy_.get() && desktop_window_) { |
| 241 ui::Layer* layer = desktop_window_->layer(); | 189 ui::Layer* layer = desktop_window_->layer(); |
| 242 oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel( | 190 oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel( |
| 243 layer, layer->bounds().size())); | 191 layer, layer->bounds().size())); |
| 244 } | 192 } |
| 245 ClearCursorState(); | 193 cursor_renderer_->Clear(); |
| 246 } | 194 } |
| 247 | 195 |
| 248 void AuraWindowCaptureMachine::Capture(bool dirty) { | 196 void AuraWindowCaptureMachine::Capture(bool dirty) { |
| 249 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 197 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 250 | 198 |
| 251 // Do not capture if the desktop window is already destroyed. | 199 // Do not capture if the desktop window is already destroyed. |
| 252 if (!desktop_window_) | 200 if (!desktop_window_) |
| 253 return; | 201 return; |
| 254 | 202 |
| 255 scoped_refptr<media::VideoFrame> frame; | 203 scoped_refptr<media::VideoFrame> frame; |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 379 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { | 327 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { |
| 380 yuv_readback_pipeline_.reset( | 328 yuv_readback_pipeline_.reset( |
| 381 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, | 329 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, |
| 382 result_rect.size(), | 330 result_rect.size(), |
| 383 result_rect, | 331 result_rect, |
| 384 region_in_frame.size(), | 332 region_in_frame.size(), |
| 385 true, | 333 true, |
| 386 true)); | 334 true)); |
| 387 } | 335 } |
| 388 | 336 |
| 389 gfx::Point cursor_position_in_frame = UpdateCursorState(region_in_frame); | 337 cursor_renderer_->Update(region_in_frame); |
| 390 yuv_readback_pipeline_->ReadbackYUV( | 338 yuv_readback_pipeline_->ReadbackYUV( |
| 391 texture_mailbox.mailbox(), | 339 texture_mailbox.mailbox(), texture_mailbox.sync_point(), |
| 392 texture_mailbox.sync_point(), | 340 video_frame.get(), region_in_frame.origin(), |
| 393 video_frame.get(), | 341 base::Bind(&CopyOutputFinishedForVideo, weak_factory_.GetWeakPtr(), |
| 394 region_in_frame.origin(), | 342 start_time, capture_frame_cb, video_frame, |
| 395 base::Bind(&CopyOutputFinishedForVideo, | 343 cursor_renderer_.get(), base::Passed(&release_callback))); |
| 396 weak_factory_.GetWeakPtr(), | |
| 397 start_time, | |
| 398 capture_frame_cb, | |
| 399 video_frame, | |
| 400 scaled_cursor_bitmap_, | |
| 401 cursor_position_in_frame, | |
| 402 base::Passed(&release_callback))); | |
| 403 return true; | 344 return true; |
| 404 } | 345 } |
| 405 | 346 |
| 406 gfx::Point AuraWindowCaptureMachine::UpdateCursorState( | |
| 407 const gfx::Rect& region_in_frame) { | |
| 408 const gfx::Rect window_bounds = desktop_window_->GetBoundsInScreen(); | |
| 409 gfx::Point cursor_position = aura::Env::GetInstance()->last_mouse_location(); | |
| 410 if (!window_bounds.Contains(cursor_position)) { | |
| 411 // Return early if there is no need to draw the cursor. | |
| 412 ClearCursorState(); | |
| 413 return gfx::Point(); | |
| 414 } | |
| 415 | |
| 416 gfx::NativeCursor cursor = desktop_window_->GetHost()->last_cursor(); | |
| 417 gfx::Point cursor_hot_point; | |
| 418 if (last_cursor_ != cursor || | |
| 419 window_size_when_cursor_last_updated_ != window_bounds.size()) { | |
| 420 SkBitmap cursor_bitmap; | |
| 421 if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point)) { | |
| 422 const int scaled_width = cursor_bitmap.width() * | |
| 423 region_in_frame.width() / window_bounds.width(); | |
| 424 const int scaled_height = cursor_bitmap.height() * | |
| 425 region_in_frame.height() / window_bounds.height(); | |
| 426 if (scaled_width <= 0 || scaled_height <= 0) { | |
| 427 ClearCursorState(); | |
| 428 return gfx::Point(); | |
| 429 } | |
| 430 scaled_cursor_bitmap_ = skia::ImageOperations::Resize( | |
| 431 cursor_bitmap, | |
| 432 skia::ImageOperations::RESIZE_BEST, | |
| 433 scaled_width, | |
| 434 scaled_height); | |
| 435 last_cursor_ = cursor; | |
| 436 window_size_when_cursor_last_updated_ = window_bounds.size(); | |
| 437 } else { | |
| 438 // Clear cursor state if ui::GetCursorBitmap failed so that we do not | |
| 439 // render cursor on the captured frame. | |
| 440 ClearCursorState(); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 cursor_position.Offset(-window_bounds.x() - cursor_hot_point.x(), | |
| 445 -window_bounds.y() - cursor_hot_point.y()); | |
| 446 return gfx::Point( | |
| 447 region_in_frame.x() + cursor_position.x() * | |
| 448 region_in_frame.width() / window_bounds.width(), | |
| 449 region_in_frame.y() + cursor_position.y() * | |
| 450 region_in_frame.height() / window_bounds.height()); | |
| 451 } | |
| 452 | |
| 453 void AuraWindowCaptureMachine::ClearCursorState() { | |
| 454 last_cursor_ = ui::Cursor(); | |
| 455 window_size_when_cursor_last_updated_ = gfx::Size(); | |
| 456 scaled_cursor_bitmap_.reset(); | |
| 457 } | |
| 458 | |
| 459 void AuraWindowCaptureMachine::OnWindowBoundsChanged( | 347 void AuraWindowCaptureMachine::OnWindowBoundsChanged( |
| 460 aura::Window* window, | 348 aura::Window* window, |
| 461 const gfx::Rect& old_bounds, | 349 const gfx::Rect& old_bounds, |
| 462 const gfx::Rect& new_bounds) { | 350 const gfx::Rect& new_bounds) { |
| 463 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 351 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 464 DCHECK(desktop_window_ && window == desktop_window_); | 352 DCHECK(desktop_window_ && window == desktop_window_); |
| 465 | 353 |
| 466 // Post a task to update capture size after first returning to the event loop. | 354 // Post a task to update capture size after first returning to the event loop. |
| 467 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 355 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 468 &AuraWindowCaptureMachine::UpdateCaptureSize, | 356 &AuraWindowCaptureMachine::UpdateCaptureSize, |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 501 // TODO(miu): The CopyOutputRequest should be made earlier, at WillCommit(). | 389 // TODO(miu): The CopyOutputRequest should be made earlier, at WillCommit(). |
| 502 // http://crbug.com/492839 | 390 // http://crbug.com/492839 |
| 503 BrowserThread::PostTask( | 391 BrowserThread::PostTask( |
| 504 BrowserThread::UI, | 392 BrowserThread::UI, |
| 505 FROM_HERE, | 393 FROM_HERE, |
| 506 base::Bind(&AuraWindowCaptureMachine::Capture, weak_factory_.GetWeakPtr(), | 394 base::Bind(&AuraWindowCaptureMachine::Capture, weak_factory_.GetWeakPtr(), |
| 507 true)); | 395 true)); |
| 508 } | 396 } |
| 509 | 397 |
| 510 } // namespace content | 398 } // namespace content |
| OLD | NEW |