| 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> |
| 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 12 matching lines...) Expand all Loading... |
| 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 #include "ui/wm/public/activation_client.h" | 35 #include "ui/wm/public/activation_client.h" |
| 34 | 36 |
| 35 namespace content { | 37 namespace content { |
| 36 | 38 |
| 37 namespace { | 39 namespace { |
| 38 | 40 |
| 39 int clip_byte(int x) { | |
| 40 return std::max(0, std::min(x, 255)); | |
| 41 } | |
| 42 | |
| 43 int alpha_blend(int alpha, int src, int dst) { | |
| 44 return (src * alpha + dst * (255 - alpha)) / 255; | |
| 45 } | |
| 46 | |
| 47 // Helper function to composite a cursor bitmap on a YUV420 video frame. | |
| 48 void RenderCursorOnVideoFrame( | |
| 49 const scoped_refptr<media::VideoFrame>& target, | |
| 50 const SkBitmap& cursor_bitmap, | |
| 51 const gfx::Point& cursor_position) { | |
| 52 DCHECK(target.get()); | |
| 53 DCHECK(!cursor_bitmap.isNull()); | |
| 54 | |
| 55 gfx::Rect rect = gfx::IntersectRects( | |
| 56 gfx::Rect(cursor_bitmap.width(), cursor_bitmap.height()) + | |
| 57 gfx::Vector2d(cursor_position.x(), cursor_position.y()), | |
| 58 target->visible_rect()); | |
| 59 | |
| 60 cursor_bitmap.lockPixels(); | |
| 61 for (int y = rect.y(); y < rect.bottom(); ++y) { | |
| 62 int cursor_y = y - cursor_position.y(); | |
| 63 uint8* yplane = target->data(media::VideoFrame::kYPlane) + | |
| 64 y * target->row_bytes(media::VideoFrame::kYPlane); | |
| 65 uint8* uplane = target->data(media::VideoFrame::kUPlane) + | |
| 66 (y / 2) * target->row_bytes(media::VideoFrame::kUPlane); | |
| 67 uint8* vplane = target->data(media::VideoFrame::kVPlane) + | |
| 68 (y / 2) * target->row_bytes(media::VideoFrame::kVPlane); | |
| 69 for (int x = rect.x(); x < rect.right(); ++x) { | |
| 70 int cursor_x = x - cursor_position.x(); | |
| 71 SkColor color = cursor_bitmap.getColor(cursor_x, cursor_y); | |
| 72 int alpha = SkColorGetA(color); | |
| 73 int color_r = SkColorGetR(color); | |
| 74 int color_g = SkColorGetG(color); | |
| 75 int color_b = SkColorGetB(color); | |
| 76 int color_y = clip_byte(((color_r * 66 + color_g * 129 + color_b * 25 + | |
| 77 128) >> 8) + 16); | |
| 78 yplane[x] = alpha_blend(alpha, color_y, yplane[x]); | |
| 79 | |
| 80 // Only sample U and V at even coordinates. | |
| 81 if ((x % 2 == 0) && (y % 2 == 0)) { | |
| 82 int color_u = clip_byte(((color_r * -38 + color_g * -74 + | |
| 83 color_b * 112 + 128) >> 8) + 128); | |
| 84 int color_v = clip_byte(((color_r * 112 + color_g * -94 + | |
| 85 color_b * -18 + 128) >> 8) + 128); | |
| 86 uplane[x / 2] = alpha_blend(alpha, color_u, uplane[x / 2]); | |
| 87 vplane[x / 2] = alpha_blend(alpha, color_v, vplane[x / 2]); | |
| 88 } | |
| 89 } | |
| 90 } | |
| 91 cursor_bitmap.unlockPixels(); | |
| 92 } | |
| 93 | |
| 94 using CaptureFrameCallback = | |
| 95 media::ThreadSafeCaptureOracle::CaptureFrameCallback; | |
| 96 | |
| 97 void CopyOutputFinishedForVideo( | |
| 98 base::WeakPtr<AuraWindowCaptureMachine> machine, | |
| 99 base::TimeTicks start_time, | |
| 100 const CaptureFrameCallback& capture_frame_cb, | |
| 101 const scoped_refptr<media::VideoFrame>& target, | |
| 102 const SkBitmap& cursor_bitmap, | |
| 103 const gfx::Point& cursor_position, | |
| 104 scoped_ptr<cc::SingleReleaseCallback> release_callback, | |
| 105 bool result) { | |
| 106 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 107 | |
| 108 if (!cursor_bitmap.isNull()) | |
| 109 RenderCursorOnVideoFrame(target, cursor_bitmap, cursor_position); | |
| 110 release_callback->Run(gpu::SyncToken(), false); | |
| 111 | |
| 112 // Only deliver the captured frame if the AuraWindowCaptureMachine has not | |
| 113 // been stopped (i.e., the WeakPtr is still valid). | |
| 114 if (machine.get()) | |
| 115 capture_frame_cb.Run(target, start_time, result); | |
| 116 } | |
| 117 | |
| 118 void RunSingleReleaseCallback(scoped_ptr<cc::SingleReleaseCallback> cb, | 41 void RunSingleReleaseCallback(scoped_ptr<cc::SingleReleaseCallback> cb, |
| 119 const gpu::SyncToken& sync_token) { | 42 const gpu::SyncToken& sync_token) { |
| 120 cb->Run(sync_token, false); | 43 cb->Run(sync_token, false); |
| 121 } | 44 } |
| 122 | 45 |
| 123 } // namespace | 46 } // namespace |
| 124 | 47 |
| 125 AuraWindowCaptureMachine::AuraWindowCaptureMachine() | 48 AuraWindowCaptureMachine::AuraWindowCaptureMachine() |
| 126 : desktop_window_(NULL), | 49 : desktop_window_(NULL), |
| 127 timer_(true, true), | 50 timer_(true, true), |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 power_save_blocker_.reset(); | 128 power_save_blocker_.reset(); |
| 206 | 129 |
| 207 // Stop observing compositor and window events. | 130 // Stop observing compositor and window events. |
| 208 if (desktop_window_) { | 131 if (desktop_window_) { |
| 209 aura::WindowTreeHost* window_host = desktop_window_->GetHost(); | 132 aura::WindowTreeHost* window_host = desktop_window_->GetHost(); |
| 210 // In the host destructor the compositor is destroyed before the window. | 133 // In the host destructor the compositor is destroyed before the window. |
| 211 if (window_host && window_host->compositor()) | 134 if (window_host && window_host->compositor()) |
| 212 window_host->compositor()->RemoveObserver(this); | 135 window_host->compositor()->RemoveObserver(this); |
| 213 desktop_window_->RemoveObserver(this); | 136 desktop_window_->RemoveObserver(this); |
| 214 desktop_window_ = NULL; | 137 desktop_window_ = NULL; |
| 138 cursor_renderer_.reset(); |
| 215 } | 139 } |
| 216 | 140 |
| 217 // Stop timer. | 141 // Stop timer. |
| 218 timer_.Stop(); | 142 timer_.Stop(); |
| 219 | 143 |
| 220 callback.Run(); | 144 callback.Run(); |
| 221 } | 145 } |
| 222 | 146 |
| 223 void AuraWindowCaptureMachine::SetWindow(aura::Window* window) { | 147 void AuraWindowCaptureMachine::SetWindow(aura::Window* window) { |
| 224 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 148 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 225 | 149 |
| 226 DCHECK(!desktop_window_); | 150 DCHECK(!desktop_window_); |
| 227 desktop_window_ = window; | 151 desktop_window_ = window; |
| 152 cursor_renderer_.reset(new CursorRendererAura(window)); |
| 228 | 153 |
| 229 // Start observing window events. | 154 // Start observing window events. |
| 230 desktop_window_->AddObserver(this); | 155 desktop_window_->AddObserver(this); |
| 231 | 156 |
| 232 // We must store this for the UMA reporting in DidCopyOutput() as | 157 // We must store this for the UMA reporting in DidCopyOutput() as |
| 233 // desktop_window_ might be destroyed at that point. | 158 // desktop_window_ might be destroyed at that point. |
| 234 screen_capture_ = window->IsRootWindow(); | 159 screen_capture_ = window->IsRootWindow(); |
| 235 IncrementDesktopCaptureCounter(screen_capture_ ? SCREEN_CAPTURER_CREATED | 160 IncrementDesktopCaptureCounter(screen_capture_ ? SCREEN_CAPTURER_CREATED |
| 236 : WINDOW_CAPTURER_CREATED); | 161 : WINDOW_CAPTURER_CREATED); |
| 237 } | 162 } |
| 238 | 163 |
| 239 void AuraWindowCaptureMachine::UpdateCaptureSize() { | 164 void AuraWindowCaptureMachine::UpdateCaptureSize() { |
| 240 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 165 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 241 if (oracle_proxy_.get() && desktop_window_) { | 166 if (oracle_proxy_.get() && desktop_window_) { |
| 242 ui::Layer* layer = desktop_window_->layer(); | 167 ui::Layer* layer = desktop_window_->layer(); |
| 243 oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel( | 168 oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel( |
| 244 layer, layer->bounds().size())); | 169 layer, layer->bounds().size())); |
| 245 } | 170 } |
| 246 ClearCursorState(); | 171 cursor_renderer_->Clear(); |
| 247 } | 172 } |
| 248 | 173 |
| 249 void AuraWindowCaptureMachine::Capture(bool dirty) { | 174 void AuraWindowCaptureMachine::Capture(bool dirty) { |
| 250 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 175 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 251 | 176 |
| 252 // Do not capture if the desktop window is already destroyed. | 177 // Do not capture if the desktop window is already destroyed. |
| 253 if (!desktop_window_) | 178 if (!desktop_window_) |
| 254 return; | 179 return; |
| 255 | 180 |
| 256 scoped_refptr<media::VideoFrame> frame; | 181 scoped_refptr<media::VideoFrame> frame; |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { | 306 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { |
| 382 yuv_readback_pipeline_.reset( | 307 yuv_readback_pipeline_.reset( |
| 383 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, | 308 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, |
| 384 result_rect.size(), | 309 result_rect.size(), |
| 385 result_rect, | 310 result_rect, |
| 386 region_in_frame.size(), | 311 region_in_frame.size(), |
| 387 true, | 312 true, |
| 388 true)); | 313 true)); |
| 389 } | 314 } |
| 390 | 315 |
| 391 gfx::Point cursor_position_in_frame = UpdateCursorState(region_in_frame); | 316 cursor_renderer_->SnapshotCursorState(region_in_frame); |
| 392 yuv_readback_pipeline_->ReadbackYUV( | 317 yuv_readback_pipeline_->ReadbackYUV( |
| 393 texture_mailbox.mailbox(), texture_mailbox.sync_token(), | 318 texture_mailbox.mailbox(), texture_mailbox.sync_token(), |
| 394 video_frame.get(), region_in_frame.origin(), | 319 video_frame.get(), region_in_frame.origin(), |
| 395 base::Bind(&CopyOutputFinishedForVideo, weak_factory_.GetWeakPtr(), | 320 base::Bind(&CopyOutputFinishedForVideo, weak_factory_.GetWeakPtr(), |
| 396 start_time, capture_frame_cb, video_frame, | 321 start_time, capture_frame_cb, video_frame, |
| 397 scaled_cursor_bitmap_, cursor_position_in_frame, | |
| 398 base::Passed(&release_callback))); | 322 base::Passed(&release_callback))); |
| 399 return true; | 323 return true; |
| 400 } | 324 } |
| 401 | 325 |
| 402 gfx::Point AuraWindowCaptureMachine::UpdateCursorState( | 326 using CaptureFrameCallback = |
| 403 const gfx::Rect& region_in_frame) { | 327 media::ThreadSafeCaptureOracle::CaptureFrameCallback; |
| 404 const gfx::Rect window_bounds = desktop_window_->GetBoundsInScreen(); | 328 |
| 405 gfx::Point cursor_position = aura::Env::GetInstance()->last_mouse_location(); | 329 void AuraWindowCaptureMachine::CopyOutputFinishedForVideo( |
| 406 if (!window_bounds.Contains(cursor_position)) { | 330 base::WeakPtr<AuraWindowCaptureMachine> machine, |
| 407 // Return early if there is no need to draw the cursor. | 331 base::TimeTicks start_time, |
| 408 ClearCursorState(); | 332 const CaptureFrameCallback& capture_frame_cb, |
| 409 return gfx::Point(); | 333 const scoped_refptr<media::VideoFrame>& target, |
| 334 scoped_ptr<cc::SingleReleaseCallback> release_callback, |
| 335 bool result) { |
| 336 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 337 |
| 338 release_callback->Run(gpu::SyncToken(), false); |
| 339 |
| 340 // Render the cursor and deliver the captured frame if the |
| 341 // AuraWindowCaptureMachine has not been stopped (i.e., the WeakPtr is |
| 342 // still valid). |
| 343 if (machine.get()) { |
| 344 if (machine->cursor_renderer_ && result) |
| 345 machine->cursor_renderer_->RenderOnVideoFrame(target); |
| 346 capture_frame_cb.Run(target, start_time, result); |
| 410 } | 347 } |
| 411 | |
| 412 aura::client::ActivationClient* activation_client = | |
| 413 aura::client::GetActivationClient(desktop_window_->GetRootWindow()); | |
| 414 DCHECK(activation_client); | |
| 415 aura::Window* active_window = activation_client->GetActiveWindow(); | |
| 416 if (!desktop_window_->Contains(active_window)) { | |
| 417 // Return early if the target window is not active. | |
| 418 ClearCursorState(); | |
| 419 return gfx::Point(); | |
| 420 } | |
| 421 | |
| 422 gfx::NativeCursor cursor = desktop_window_->GetHost()->last_cursor(); | |
| 423 gfx::Point cursor_hot_point; | |
| 424 if (last_cursor_ != cursor || | |
| 425 window_size_when_cursor_last_updated_ != window_bounds.size()) { | |
| 426 SkBitmap cursor_bitmap; | |
| 427 if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point)) { | |
| 428 const int scaled_width = cursor_bitmap.width() * | |
| 429 region_in_frame.width() / window_bounds.width(); | |
| 430 const int scaled_height = cursor_bitmap.height() * | |
| 431 region_in_frame.height() / window_bounds.height(); | |
| 432 if (scaled_width <= 0 || scaled_height <= 0) { | |
| 433 ClearCursorState(); | |
| 434 return gfx::Point(); | |
| 435 } | |
| 436 scaled_cursor_bitmap_ = skia::ImageOperations::Resize( | |
| 437 cursor_bitmap, | |
| 438 skia::ImageOperations::RESIZE_BEST, | |
| 439 scaled_width, | |
| 440 scaled_height); | |
| 441 last_cursor_ = cursor; | |
| 442 window_size_when_cursor_last_updated_ = window_bounds.size(); | |
| 443 } else { | |
| 444 // Clear cursor state if ui::GetCursorBitmap failed so that we do not | |
| 445 // render cursor on the captured frame. | |
| 446 ClearCursorState(); | |
| 447 } | |
| 448 } | |
| 449 | |
| 450 cursor_position.Offset(-window_bounds.x() - cursor_hot_point.x(), | |
| 451 -window_bounds.y() - cursor_hot_point.y()); | |
| 452 return gfx::Point( | |
| 453 region_in_frame.x() + cursor_position.x() * | |
| 454 region_in_frame.width() / window_bounds.width(), | |
| 455 region_in_frame.y() + cursor_position.y() * | |
| 456 region_in_frame.height() / window_bounds.height()); | |
| 457 } | |
| 458 | |
| 459 void AuraWindowCaptureMachine::ClearCursorState() { | |
| 460 last_cursor_ = ui::Cursor(); | |
| 461 window_size_when_cursor_last_updated_ = gfx::Size(); | |
| 462 scaled_cursor_bitmap_.reset(); | |
| 463 } | 348 } |
| 464 | 349 |
| 465 void AuraWindowCaptureMachine::OnWindowBoundsChanged( | 350 void AuraWindowCaptureMachine::OnWindowBoundsChanged( |
| 466 aura::Window* window, | 351 aura::Window* window, |
| 467 const gfx::Rect& old_bounds, | 352 const gfx::Rect& old_bounds, |
| 468 const gfx::Rect& new_bounds) { | 353 const gfx::Rect& new_bounds) { |
| 469 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 354 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 470 DCHECK(desktop_window_ && window == desktop_window_); | 355 DCHECK(desktop_window_ && window == desktop_window_); |
| 471 | 356 |
| 472 // Post a task to update capture size after first returning to the event loop. | 357 // Post a task to update capture size after first returning to the event loop. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 // TODO(miu): The CopyOutputRequest should be made earlier, at WillCommit(). | 392 // TODO(miu): The CopyOutputRequest should be made earlier, at WillCommit(). |
| 508 // http://crbug.com/492839 | 393 // http://crbug.com/492839 |
| 509 BrowserThread::PostTask( | 394 BrowserThread::PostTask( |
| 510 BrowserThread::UI, | 395 BrowserThread::UI, |
| 511 FROM_HERE, | 396 FROM_HERE, |
| 512 base::Bind(&AuraWindowCaptureMachine::Capture, weak_factory_.GetWeakPtr(), | 397 base::Bind(&AuraWindowCaptureMachine::Capture, weak_factory_.GetWeakPtr(), |
| 513 true)); | 398 true)); |
| 514 } | 399 } |
| 515 | 400 |
| 516 } // namespace content | 401 } // namespace content |
| OLD | NEW |