| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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.h" | 5 #include "content/browser/media/capture/desktop_capture_device.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 #include <string.h> | 9 #include <string.h> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 DesktopMediaID::Type type); | 68 DesktopMediaID::Type type); |
| 69 ~Core() override; | 69 ~Core() override; |
| 70 | 70 |
| 71 // Implementation of VideoCaptureDevice methods. | 71 // Implementation of VideoCaptureDevice methods. |
| 72 void AllocateAndStart(const media::VideoCaptureParams& params, | 72 void AllocateAndStart(const media::VideoCaptureParams& params, |
| 73 std::unique_ptr<Client> client); | 73 std::unique_ptr<Client> client); |
| 74 | 74 |
| 75 void SetNotificationWindowId(gfx::NativeViewId window_id); | 75 void SetNotificationWindowId(gfx::NativeViewId window_id); |
| 76 | 76 |
| 77 private: | 77 private: |
| 78 | 78 // webrtc::DesktopCapturer::Callback interface. |
| 79 // webrtc::DesktopCapturer::Callback interface | 79 void OnCaptureResult( |
| 80 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override; | 80 webrtc::DesktopCapturer::Result result, |
| 81 std::unique_ptr<webrtc::DesktopFrame> frame) override; |
| 81 | 82 |
| 82 // Method that is scheduled on |task_runner_| to be called on regular interval | 83 // Method that is scheduled on |task_runner_| to be called on regular interval |
| 83 // to capture a frame. | 84 // to capture a frame. |
| 84 void OnCaptureTimer(); | 85 void OnCaptureTimer(); |
| 85 | 86 |
| 86 // Captures a frame and schedules timer for the next one. | 87 // Captures a frame and schedules timer for the next one. |
| 87 void CaptureFrameAndScheduleNext(); | 88 void CaptureFrameAndScheduleNext(); |
| 88 | 89 |
| 89 // Captures a single frame. | 90 // Captures a single frame. |
| 90 void DoCapture(); | 91 void DoCapture(); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 CaptureFrameAndScheduleNext(); | 184 CaptureFrameAndScheduleNext(); |
| 184 } | 185 } |
| 185 | 186 |
| 186 void DesktopCaptureDevice::Core::SetNotificationWindowId( | 187 void DesktopCaptureDevice::Core::SetNotificationWindowId( |
| 187 gfx::NativeViewId window_id) { | 188 gfx::NativeViewId window_id) { |
| 188 DCHECK(task_runner_->BelongsToCurrentThread()); | 189 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 189 DCHECK(window_id); | 190 DCHECK(window_id); |
| 190 desktop_capturer_->SetExcludedWindow(window_id); | 191 desktop_capturer_->SetExcludedWindow(window_id); |
| 191 } | 192 } |
| 192 | 193 |
| 193 void DesktopCaptureDevice::Core::OnCaptureCompleted( | 194 void DesktopCaptureDevice::Core::OnCaptureResult( |
| 194 webrtc::DesktopFrame* frame) { | 195 webrtc::DesktopCapturer::Result result, |
| 196 std::unique_ptr<webrtc::DesktopFrame> frame) { |
| 195 DCHECK(task_runner_->BelongsToCurrentThread()); | 197 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 196 DCHECK(capture_in_progress_); | 198 DCHECK(capture_in_progress_); |
| 197 | 199 |
| 200 capture_in_progress_ = false; |
| 201 |
| 202 bool success = result == webrtc::DesktopCapturer::Result::SUCCESS; |
| 203 |
| 198 if (!first_capture_returned_) { | 204 if (!first_capture_returned_) { |
| 199 first_capture_returned_ = true; | 205 first_capture_returned_ = true; |
| 200 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { | 206 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { |
| 201 IncrementDesktopCaptureCounter(frame ? FIRST_SCREEN_CAPTURE_SUCCEEDED | 207 IncrementDesktopCaptureCounter(success ? FIRST_SCREEN_CAPTURE_SUCCEEDED |
| 202 : FIRST_SCREEN_CAPTURE_FAILED); | 208 : FIRST_SCREEN_CAPTURE_FAILED); |
| 203 } else { | 209 } else { |
| 204 IncrementDesktopCaptureCounter(frame ? FIRST_WINDOW_CAPTURE_SUCCEEDED | 210 IncrementDesktopCaptureCounter(success ? FIRST_WINDOW_CAPTURE_SUCCEEDED |
| 205 : FIRST_WINDOW_CAPTURE_FAILED); | 211 : FIRST_WINDOW_CAPTURE_FAILED); |
| 206 } | 212 } |
| 207 } | 213 } |
| 208 | 214 |
| 209 capture_in_progress_ = false; | 215 if (!success) { |
| 210 | 216 if (result == webrtc::DesktopCapturer::Result::ERROR_PERMANENT) |
| 211 if (!frame) { | 217 client_->OnError(FROM_HERE, "The desktop capturer has failed."); |
| 212 client_->OnError(FROM_HERE, "Failed to capture a frame."); | |
| 213 return; | 218 return; |
| 214 } | 219 } |
| 220 DCHECK(frame); |
| 215 | 221 |
| 216 if (!client_) | 222 if (!client_) |
| 217 return; | 223 return; |
| 218 | 224 |
| 219 base::TimeDelta capture_time( | 225 base::TimeDelta capture_time( |
| 220 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); | 226 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); |
| 221 | 227 |
| 222 // The two UMA_ blocks must be put in its own scope since it creates a static | 228 // The two UMA_ blocks must be put in its own scope since it creates a static |
| 223 // variable which expected constant histogram name. | 229 // variable which expected constant histogram name. |
| 224 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { | 230 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { |
| 225 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time); | 231 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time); |
| 226 } else { | 232 } else { |
| 227 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time); | 233 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time); |
| 228 } | 234 } |
| 229 | 235 |
| 230 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
| 231 | |
| 232 // If the frame size has changed, drop the output frame (if any), and | 236 // If the frame size has changed, drop the output frame (if any), and |
| 233 // determine the new output size. | 237 // determine the new output size. |
| 234 if (!previous_frame_size_.equals(frame->size())) { | 238 if (!previous_frame_size_.equals(frame->size())) { |
| 235 output_frame_.reset(); | 239 output_frame_.reset(); |
| 236 resolution_chooser_->SetSourceSize(gfx::Size(frame->size().width(), | 240 resolution_chooser_->SetSourceSize(gfx::Size(frame->size().width(), |
| 237 frame->size().height())); | 241 frame->size().height())); |
| 238 previous_frame_size_ = frame->size(); | 242 previous_frame_size_ = frame->size(); |
| 239 } | 243 } |
| 240 // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so | 244 // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so |
| 241 // it can convert the frame to I420 format. | 245 // it can convert the frame to I420 format. |
| 242 const webrtc::DesktopSize output_size( | 246 const webrtc::DesktopSize output_size( |
| 243 resolution_chooser_->capture_size().width() & ~1, | 247 resolution_chooser_->capture_size().width() & ~1, |
| 244 resolution_chooser_->capture_size().height() & ~1); | 248 resolution_chooser_->capture_size().height() & ~1); |
| 245 if (output_size.is_empty()) | 249 if (output_size.is_empty()) |
| 246 return; | 250 return; |
| 247 | 251 |
| 252 webrtc::DesktopFrame* frame_ptr = frame.get(); |
| 253 |
| 248 // On OSX We receive a 1x1 frame when the shared window is minimized. It | 254 // On OSX We receive a 1x1 frame when the shared window is minimized. It |
| 249 // cannot be subsampled to I420 and will be dropped downstream. So we replace | 255 // cannot be subsampled to I420 and will be dropped downstream. So we replace |
| 250 // it with a black frame to avoid the video appearing frozen at the last | 256 // it with a black frame to avoid the video appearing frozen at the last |
| 251 // frame. | 257 // frame. |
| 252 if (frame->size().width() == 1 || frame->size().height() == 1) { | 258 if (frame->size().width() == 1 || frame->size().height() == 1) { |
| 253 if (!black_frame_) { | 259 if (!black_frame_) { |
| 254 black_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 260 black_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 255 memset(black_frame_->data(), | 261 memset(black_frame_->data(), 0, |
| 256 0, | |
| 257 black_frame_->stride() * black_frame_->size().height()); | 262 black_frame_->stride() * black_frame_->size().height()); |
| 258 } | 263 } |
| 259 owned_frame.reset(); | 264 frame_ptr = black_frame_.get(); |
| 260 frame = black_frame_.get(); | |
| 261 } | 265 } |
| 262 | 266 |
| 263 size_t output_bytes = output_size.width() * output_size.height() * | 267 size_t output_bytes = output_size.width() * output_size.height() * |
| 264 webrtc::DesktopFrame::kBytesPerPixel; | 268 webrtc::DesktopFrame::kBytesPerPixel; |
| 265 const uint8_t* output_data = NULL; | 269 const uint8_t* output_data = NULL; |
| 266 | 270 |
| 267 if (!frame->size().equals(output_size)) { | 271 if (!frame_ptr->size().equals(output_size)) { |
| 268 // Down-scale and/or letterbox to the target format if the frame does not | 272 // Down-scale and/or letterbox to the target format if the frame does not |
| 269 // match the output size. | 273 // match the output size. |
| 270 | 274 |
| 271 // Allocate a buffer of the correct size to scale the frame into. | 275 // Allocate a buffer of the correct size to scale the frame into. |
| 272 // |output_frame_| is cleared whenever the output size changes, so we don't | 276 // |output_frame_| is cleared whenever the output size changes, so we don't |
| 273 // need to worry about clearing out stale pixel data in letterboxed areas. | 277 // need to worry about clearing out stale pixel data in letterboxed areas. |
| 274 if (!output_frame_) { | 278 if (!output_frame_) { |
| 275 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 279 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 276 memset(output_frame_->data(), 0, output_bytes); | 280 memset(output_frame_->data(), 0, output_bytes); |
| 277 } | 281 } |
| 278 DCHECK(output_frame_->size().equals(output_size)); | 282 DCHECK(output_frame_->size().equals(output_size)); |
| 279 | 283 |
| 280 // TODO(wez): Optimize this to scale only changed portions of the output, | 284 // TODO(wez): Optimize this to scale only changed portions of the output, |
| 281 // using ARGBScaleClip(). | 285 // using ARGBScaleClip(). |
| 282 const webrtc::DesktopRect output_rect = | 286 const webrtc::DesktopRect output_rect = |
| 283 ComputeLetterboxRect(output_size, frame->size()); | 287 ComputeLetterboxRect(output_size, frame_ptr->size()); |
| 284 uint8_t* output_rect_data = output_frame_->data() + | 288 uint8_t* output_rect_data = |
| 285 output_frame_->stride() * output_rect.top() + | 289 output_frame_->GetFrameDataAtPos(output_rect.top_left()); |
| 286 webrtc::DesktopFrame::kBytesPerPixel * output_rect.left(); | 290 libyuv::ARGBScale(frame_ptr->data(), frame_ptr->stride(), |
| 287 libyuv::ARGBScale(frame->data(), frame->stride(), | 291 frame_ptr->size().width(), frame_ptr->size().height(), |
| 288 frame->size().width(), frame->size().height(), | |
| 289 output_rect_data, output_frame_->stride(), | 292 output_rect_data, output_frame_->stride(), |
| 290 output_rect.width(), output_rect.height(), | 293 output_rect.width(), output_rect.height(), |
| 291 libyuv::kFilterBilinear); | 294 libyuv::kFilterBilinear); |
| 292 output_data = output_frame_->data(); | 295 output_data = output_frame_->data(); |
| 293 } else if (IsFrameUnpackedOrInverted(frame)) { | 296 } else if (IsFrameUnpackedOrInverted(frame_ptr)) { |
| 294 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom | 297 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom |
| 295 // copy. | 298 // copy. |
| 296 // This is required if the frame is inverted (see crbug.com/306876), or if | 299 // This is required if the frame is inverted (see crbug.com/306876), or if |
| 297 // |frame| is cropped form a larger frame (see crbug.com/437740). | 300 // |frame| is cropped form a larger frame (see crbug.com/437740). |
| 298 if (!output_frame_) { | 301 if (!output_frame_) { |
| 299 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 302 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 300 memset(output_frame_->data(), 0, output_bytes); | 303 memset(output_frame_->data(), 0, output_bytes); |
| 301 } | 304 } |
| 302 | 305 |
| 303 output_frame_->CopyPixelsFrom( | 306 output_frame_->CopyPixelsFrom( |
| 304 *frame, | 307 *frame, webrtc::DesktopVector(), |
| 305 webrtc::DesktopVector(), | 308 webrtc::DesktopRect::MakeSize(frame_ptr->size())); |
| 306 webrtc::DesktopRect::MakeSize(frame->size())); | |
| 307 output_data = output_frame_->data(); | 309 output_data = output_frame_->data(); |
| 308 } else { | 310 } else { |
| 309 // If the captured frame matches the output size, we can return the pixel | 311 // If the captured frame matches the output size, we can return the pixel |
| 310 // data directly. | 312 // data directly. |
| 311 output_data = frame->data(); | 313 output_data = frame_ptr->data(); |
| 312 } | 314 } |
| 313 | 315 |
| 314 base::TimeTicks now = base::TimeTicks::Now(); | 316 base::TimeTicks now = base::TimeTicks::Now(); |
| 315 if (first_ref_time_.is_null()) | 317 if (first_ref_time_.is_null()) |
| 316 first_ref_time_ = now; | 318 first_ref_time_ = now; |
| 317 client_->OnIncomingCapturedData( | 319 client_->OnIncomingCapturedData( |
| 318 output_data, output_bytes, | 320 output_data, output_bytes, |
| 319 media::VideoCaptureFormat( | 321 media::VideoCaptureFormat( |
| 320 gfx::Size(output_size.width(), output_size.height()), | 322 gfx::Size(output_size.width(), output_size.height()), |
| 321 requested_frame_rate_, media::PIXEL_FORMAT_ARGB), | 323 requested_frame_rate_, media::PIXEL_FORMAT_ARGB), |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 #else | 457 #else |
| 456 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT; | 458 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT; |
| 457 #endif | 459 #endif |
| 458 | 460 |
| 459 thread_.StartWithOptions(base::Thread::Options(thread_type, 0)); | 461 thread_.StartWithOptions(base::Thread::Options(thread_type, 0)); |
| 460 | 462 |
| 461 core_.reset(new Core(thread_.task_runner(), std::move(capturer), type)); | 463 core_.reset(new Core(thread_.task_runner(), std::move(capturer), type)); |
| 462 } | 464 } |
| 463 | 465 |
| 464 } // namespace content | 466 } // namespace content |
| OLD | NEW |