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/desktop_capture_device_aura.h" | 5 #include "content/browser/media/capture/desktop_capture_device_aura.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/metrics/histogram.h" | |
| 8 #include "base/timer/timer.h" | 9 #include "base/timer/timer.h" |
| 9 #include "cc/output/copy_output_request.h" | 10 #include "cc/output/copy_output_request.h" |
| 10 #include "cc/output/copy_output_result.h" | 11 #include "cc/output/copy_output_result.h" |
| 11 #include "content/browser/compositor/image_transport_factory.h" | 12 #include "content/browser/compositor/image_transport_factory.h" |
| 12 #include "content/browser/media/capture/content_video_capture_device_core.h" | 13 #include "content/browser/media/capture/content_video_capture_device_core.h" |
| 14 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" | |
| 13 #include "content/common/gpu/client/gl_helper.h" | 15 #include "content/common/gpu/client/gl_helper.h" |
| 14 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 15 #include "media/base/video_util.h" | 17 #include "media/base/video_util.h" |
| 16 #include "media/video/capture/video_capture_types.h" | 18 #include "media/video/capture/video_capture_types.h" |
| 17 #include "skia/ext/image_operations.h" | 19 #include "skia/ext/image_operations.h" |
| 18 #include "third_party/skia/include/core/SkBitmap.h" | 20 #include "third_party/skia/include/core/SkBitmap.h" |
| 19 #include "ui/aura/env.h" | 21 #include "ui/aura/env.h" |
| 20 #include "ui/aura/window.h" | 22 #include "ui/aura/window.h" |
| 21 #include "ui/aura/window_observer.h" | 23 #include "ui/aura/window_observer.h" |
| 22 #include "ui/aura/window_tree_host.h" | 24 #include "ui/aura/window_tree_host.h" |
| 23 #include "ui/base/cursor/cursors_aura.h" | 25 #include "ui/base/cursor/cursors_aura.h" |
| 24 #include "ui/compositor/compositor.h" | 26 #include "ui/compositor/compositor.h" |
| 25 #include "ui/compositor/dip_util.h" | 27 #include "ui/compositor/dip_util.h" |
| 26 #include "ui/compositor/layer.h" | 28 #include "ui/compositor/layer.h" |
| 27 #include "ui/gfx/screen.h" | 29 #include "ui/gfx/screen.h" |
| 28 | 30 |
| 29 namespace content { | 31 namespace content { |
| 30 | 32 |
| 31 namespace { | 33 namespace { |
| 32 | 34 |
| 35 void IncrementCounter(DesktopCaptureCounters counter) { | |
| 36 UMA_HISTOGRAM_ENUMERATION(kUMADesopCaptureCounters, | |
| 37 counter, | |
| 38 DESKTOP_CAPTURE_COUNTER_BOUNDARY); | |
| 39 } | |
| 40 | |
| 33 int clip_byte(int x) { | 41 int clip_byte(int x) { |
| 34 return std::max(0, std::min(x, 255)); | 42 return std::max(0, std::min(x, 255)); |
| 35 } | 43 } |
| 36 | 44 |
| 37 int alpha_blend(int alpha, int src, int dst) { | 45 int alpha_blend(int alpha, int src, int dst) { |
| 38 return (src * alpha + dst * (255 - alpha)) / 255; | 46 return (src * alpha + dst * (255 - alpha)) / 255; |
| 39 } | 47 } |
| 40 | 48 |
| 41 // Helper function to composite a cursor bitmap on a YUV420 video frame. | 49 // Helper function to composite a cursor bitmap on a YUV420 video frame. |
| 42 void RenderCursorOnVideoFrame( | 50 void RenderCursorOnVideoFrame( |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 // Update capture size. Must be called on the UI thread. | 130 // Update capture size. Must be called on the UI thread. |
| 123 void UpdateCaptureSize(); | 131 void UpdateCaptureSize(); |
| 124 | 132 |
| 125 // Response callback for cc::Layer::RequestCopyOfOutput(). | 133 // Response callback for cc::Layer::RequestCopyOfOutput(). |
| 126 void DidCopyOutput( | 134 void DidCopyOutput( |
| 127 scoped_refptr<media::VideoFrame> video_frame, | 135 scoped_refptr<media::VideoFrame> video_frame, |
| 128 base::TimeTicks start_time, | 136 base::TimeTicks start_time, |
| 129 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, | 137 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, |
| 130 scoped_ptr<cc::CopyOutputResult> result); | 138 scoped_ptr<cc::CopyOutputResult> result); |
| 131 | 139 |
| 140 // A helper which does the real work for DidCopyOutput. Returns true if | |
| 141 // succeeded. | |
| 142 bool DidCopyOutputHelper( | |
| 143 scoped_refptr<media::VideoFrame> video_frame, | |
| 144 base::TimeTicks start_time, | |
| 145 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, | |
| 146 scoped_ptr<cc::CopyOutputResult> result); | |
| 147 | |
| 132 // Helper function to update cursor state. | 148 // Helper function to update cursor state. |
| 133 // |region_in_frame| defines the desktop bound in the captured frame. | 149 // |region_in_frame| defines the desktop bound in the captured frame. |
| 134 // Returns the current cursor position in captured frame. | 150 // Returns the current cursor position in captured frame. |
| 135 gfx::Point UpdateCursorState(const gfx::Rect& region_in_frame); | 151 gfx::Point UpdateCursorState(const gfx::Rect& region_in_frame); |
| 136 | 152 |
| 137 // Clears cursor state. | 153 // Clears cursor state. |
| 138 void ClearCursorState(); | 154 void ClearCursorState(); |
| 139 | 155 |
| 140 // The window associated with the desktop. | 156 // The window associated with the desktop. |
| 141 aura::Window* desktop_window_; | 157 aura::Window* desktop_window_; |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 285 RenderCursorOnVideoFrame(target, cursor_bitmap, cursor_position); | 301 RenderCursorOnVideoFrame(target, cursor_bitmap, cursor_position); |
| 286 release_callback->Run(0, false); | 302 release_callback->Run(0, false); |
| 287 capture_frame_cb.Run(start_time, result); | 303 capture_frame_cb.Run(start_time, result); |
| 288 } | 304 } |
| 289 | 305 |
| 290 void DesktopVideoCaptureMachine::DidCopyOutput( | 306 void DesktopVideoCaptureMachine::DidCopyOutput( |
| 291 scoped_refptr<media::VideoFrame> video_frame, | 307 scoped_refptr<media::VideoFrame> video_frame, |
| 292 base::TimeTicks start_time, | 308 base::TimeTicks start_time, |
| 293 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, | 309 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, |
| 294 scoped_ptr<cc::CopyOutputResult> result) { | 310 scoped_ptr<cc::CopyOutputResult> result) { |
| 311 static bool first_call = true; | |
| 312 | |
| 313 bool succeeded = DidCopyOutputHelper( | |
| 314 video_frame, start_time, capture_frame_cb, result.Pass()); | |
| 315 | |
| 316 if (first_call) { | |
| 317 first_call = false; | |
| 318 if (window_id_.type == DesktopMediaID::TYPE_SCREEN) { | |
| 319 IncrementCounter(succeeded ? FIRST_SCREEN_CAPTURE_SUCCEEDED : | |
| 320 FIRST_SCREEN_CAPTURE_FAILED); | |
|
Sergey Ulanov
2014/04/24 19:33:24
nit: indentation. Also : can be wrapped. clang-for
jiayl
2014/04/24 20:58:55
Done.
| |
| 321 } else { | |
| 322 IncrementCounter(succeeded ? FIRST_WINDOW_CAPTURE_SUCCEEDED : | |
| 323 FIRST_WINDOW_CAPTURE_SUCCEEDED); | |
| 324 } | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 bool DesktopVideoCaptureMachine::DidCopyOutputHelper( | |
|
Sergey Ulanov
2014/04/24 19:33:24
Call this something like ProcessCopyOutputResponse
jiayl
2014/04/24 20:58:55
Done.
| |
| 329 scoped_refptr<media::VideoFrame> video_frame, | |
| 330 base::TimeTicks start_time, | |
| 331 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, | |
| 332 scoped_ptr<cc::CopyOutputResult> result) { | |
| 295 if (result->IsEmpty() || result->size().IsEmpty() || !desktop_layer_) | 333 if (result->IsEmpty() || result->size().IsEmpty() || !desktop_layer_) |
| 296 return; | 334 return false; |
| 297 | 335 |
| 298 // Compute the dest size we want after the letterboxing resize. Make the | 336 // Compute the dest size we want after the letterboxing resize. Make the |
| 299 // coordinates and sizes even because we letterbox in YUV space | 337 // coordinates and sizes even because we letterbox in YUV space |
| 300 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to | 338 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to |
| 301 // line up correctly. | 339 // line up correctly. |
| 302 // The video frame's coded_size() and the result's size() are both physical | 340 // The video frame's coded_size() and the result's size() are both physical |
| 303 // pixels. | 341 // pixels. |
| 304 gfx::Rect region_in_frame = | 342 gfx::Rect region_in_frame = |
| 305 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()), | 343 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()), |
| 306 result->size()); | 344 result->size()); |
| 307 region_in_frame = gfx::Rect(region_in_frame.x() & ~1, | 345 region_in_frame = gfx::Rect(region_in_frame.x() & ~1, |
| 308 region_in_frame.y() & ~1, | 346 region_in_frame.y() & ~1, |
| 309 region_in_frame.width() & ~1, | 347 region_in_frame.width() & ~1, |
| 310 region_in_frame.height() & ~1); | 348 region_in_frame.height() & ~1); |
| 311 if (region_in_frame.IsEmpty()) | 349 if (region_in_frame.IsEmpty()) |
| 312 return; | 350 return false; |
| 313 | 351 |
| 314 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | 352 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); |
| 315 GLHelper* gl_helper = factory->GetGLHelper(); | 353 GLHelper* gl_helper = factory->GetGLHelper(); |
| 316 if (!gl_helper) | 354 if (!gl_helper) |
| 317 return; | 355 return false; |
| 318 | 356 |
| 319 cc::TextureMailbox texture_mailbox; | 357 cc::TextureMailbox texture_mailbox; |
| 320 scoped_ptr<cc::SingleReleaseCallback> release_callback; | 358 scoped_ptr<cc::SingleReleaseCallback> release_callback; |
| 321 result->TakeTexture(&texture_mailbox, &release_callback); | 359 result->TakeTexture(&texture_mailbox, &release_callback); |
| 322 DCHECK(texture_mailbox.IsTexture()); | 360 DCHECK(texture_mailbox.IsTexture()); |
| 323 if (!texture_mailbox.IsTexture()) | 361 if (!texture_mailbox.IsTexture()) |
| 324 return; | 362 return false; |
| 325 | 363 |
| 326 gfx::Rect result_rect(result->size()); | 364 gfx::Rect result_rect(result->size()); |
| 327 if (!yuv_readback_pipeline_ || | 365 if (!yuv_readback_pipeline_ || |
| 328 yuv_readback_pipeline_->scaler()->SrcSize() != result_rect.size() || | 366 yuv_readback_pipeline_->scaler()->SrcSize() != result_rect.size() || |
| 329 yuv_readback_pipeline_->scaler()->SrcSubrect() != result_rect || | 367 yuv_readback_pipeline_->scaler()->SrcSubrect() != result_rect || |
| 330 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { | 368 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { |
| 331 yuv_readback_pipeline_.reset( | 369 yuv_readback_pipeline_.reset( |
| 332 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, | 370 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST, |
| 333 result_rect.size(), | 371 result_rect.size(), |
| 334 result_rect, | 372 result_rect, |
| 335 video_frame->coded_size(), | 373 video_frame->coded_size(), |
| 336 region_in_frame, | 374 region_in_frame, |
| 337 true, | 375 true, |
| 338 true)); | 376 true)); |
| 339 } | 377 } |
| 340 | 378 |
| 341 gfx::Point cursor_position_in_frame = UpdateCursorState(region_in_frame); | 379 gfx::Point cursor_position_in_frame = UpdateCursorState(region_in_frame); |
| 342 yuv_readback_pipeline_->ReadbackYUV( | 380 yuv_readback_pipeline_->ReadbackYUV( |
| 343 texture_mailbox.mailbox(), | 381 texture_mailbox.mailbox(), |
| 344 texture_mailbox.sync_point(), | 382 texture_mailbox.sync_point(), |
| 345 video_frame.get(), | 383 video_frame.get(), |
| 346 base::Bind(&CopyOutputFinishedForVideo, | 384 base::Bind(&CopyOutputFinishedForVideo, |
| 347 start_time, | 385 start_time, |
| 348 capture_frame_cb, | 386 capture_frame_cb, |
| 349 video_frame, | 387 video_frame, |
| 350 scaled_cursor_bitmap_, | 388 scaled_cursor_bitmap_, |
| 351 cursor_position_in_frame, | 389 cursor_position_in_frame, |
| 352 base::Passed(&release_callback))); | 390 base::Passed(&release_callback))); |
| 391 | |
| 392 base::TimeDelta capture_time = base::TimeTicks::Now() - start_time; | |
|
Sergey Ulanov
2014/04/24 19:33:24
Move this to DidCopyOutput() so that all UMA logic
jiayl
2014/04/24 20:58:55
Done.
| |
| 393 if (window_id_.type == DesktopMediaID::TYPE_SCREEN) | |
| 394 UMA_HISTOGRAM_TIMES(kUMAScreenCaptureTime, capture_time); | |
|
Sergey Ulanov
2014/04/24 19:33:24
{}
jiayl
2014/04/24 20:58:55
It's not required for single line conditionals.
| |
| 395 else | |
| 396 UMA_HISTOGRAM_TIMES(kUMAWindowCaptureTime, capture_time); | |
| 397 | |
| 398 return true; | |
| 353 } | 399 } |
| 354 | 400 |
| 355 gfx::Point DesktopVideoCaptureMachine::UpdateCursorState( | 401 gfx::Point DesktopVideoCaptureMachine::UpdateCursorState( |
| 356 const gfx::Rect& region_in_frame) { | 402 const gfx::Rect& region_in_frame) { |
| 357 const gfx::Rect desktop_bounds = desktop_layer_->bounds(); | 403 const gfx::Rect desktop_bounds = desktop_layer_->bounds(); |
| 358 gfx::NativeCursor cursor = | 404 gfx::NativeCursor cursor = |
| 359 desktop_window_->GetHost()->last_cursor(); | 405 desktop_window_->GetHost()->last_cursor(); |
| 360 if (last_cursor_ != cursor) { | 406 if (last_cursor_ != cursor) { |
| 361 SkBitmap cursor_bitmap; | 407 SkBitmap cursor_bitmap; |
| 362 if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point_)) { | 408 if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point_)) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 425 : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>( | 471 : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>( |
| 426 new DesktopVideoCaptureMachine(source)))) {} | 472 new DesktopVideoCaptureMachine(source)))) {} |
| 427 | 473 |
| 428 DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() { | 474 DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() { |
| 429 DVLOG(2) << "DesktopCaptureDeviceAura@" << this << " destroying."; | 475 DVLOG(2) << "DesktopCaptureDeviceAura@" << this << " destroying."; |
| 430 } | 476 } |
| 431 | 477 |
| 432 // static | 478 // static |
| 433 media::VideoCaptureDevice* DesktopCaptureDeviceAura::Create( | 479 media::VideoCaptureDevice* DesktopCaptureDeviceAura::Create( |
| 434 const DesktopMediaID& source) { | 480 const DesktopMediaID& source) { |
| 481 IncrementCounter(source.type == DesktopMediaID::TYPE_SCREEN ? | |
| 482 SCREEN_CAPTURER_CREATED : WINDOW_CATPTURER_CREATED); | |
| 435 return new DesktopCaptureDeviceAura(source); | 483 return new DesktopCaptureDeviceAura(source); |
| 436 } | 484 } |
| 437 | 485 |
| 438 void DesktopCaptureDeviceAura::AllocateAndStart( | 486 void DesktopCaptureDeviceAura::AllocateAndStart( |
| 439 const media::VideoCaptureParams& params, | 487 const media::VideoCaptureParams& params, |
| 440 scoped_ptr<Client> client) { | 488 scoped_ptr<Client> client) { |
| 441 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 489 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
| 442 core_->AllocateAndStart(params, client.Pass()); | 490 core_->AllocateAndStart(params, client.Pass()); |
| 443 } | 491 } |
| 444 | 492 |
| 445 void DesktopCaptureDeviceAura::StopAndDeAllocate() { | 493 void DesktopCaptureDeviceAura::StopAndDeAllocate() { |
| 446 core_->StopAndDeAllocate(); | 494 core_->StopAndDeAllocate(); |
| 447 } | 495 } |
| 448 | 496 |
| 449 } // namespace content | 497 } // namespace content |
| OLD | NEW |