| 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_); |
| 199 capture_in_progress_ = false; |
| 200 |
| 201 bool success = result == webrtc::DesktopCapturer::Result::SUCCESS; |
| 197 | 202 |
| 198 if (!first_capture_returned_) { | 203 if (!first_capture_returned_) { |
| 199 first_capture_returned_ = true; | 204 first_capture_returned_ = true; |
| 200 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { | 205 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { |
| 201 IncrementDesktopCaptureCounter(frame ? FIRST_SCREEN_CAPTURE_SUCCEEDED | 206 IncrementDesktopCaptureCounter(success ? FIRST_SCREEN_CAPTURE_SUCCEEDED |
| 202 : FIRST_SCREEN_CAPTURE_FAILED); | 207 : FIRST_SCREEN_CAPTURE_FAILED); |
| 203 } else { | 208 } else { |
| 204 IncrementDesktopCaptureCounter(frame ? FIRST_WINDOW_CAPTURE_SUCCEEDED | 209 IncrementDesktopCaptureCounter(success ? FIRST_WINDOW_CAPTURE_SUCCEEDED |
| 205 : FIRST_WINDOW_CAPTURE_FAILED); | 210 : FIRST_WINDOW_CAPTURE_FAILED); |
| 206 } | 211 } |
| 207 } | 212 } |
| 208 | 213 |
| 209 capture_in_progress_ = false; | 214 if (!success) { |
| 210 | 215 if (result == webrtc::DesktopCapturer::Result::ERROR_PERMANENT) |
| 211 if (!frame) { | 216 client_->OnError(FROM_HERE, "The desktop capturer has failed."); |
| 212 client_->OnError(FROM_HERE, "Failed to capture a frame."); | |
| 213 return; | 217 return; |
| 214 } | 218 } |
| 219 DCHECK(frame); |
| 215 | 220 |
| 216 if (!client_) | 221 if (!client_) |
| 217 return; | 222 return; |
| 218 | 223 |
| 219 base::TimeDelta capture_time( | 224 base::TimeDelta capture_time( |
| 220 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); | 225 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); |
| 221 | 226 |
| 222 // The two UMA_ blocks must be put in its own scope since it creates a static | 227 // The two UMA_ blocks must be put in its own scope since it creates a static |
| 223 // variable which expected constant histogram name. | 228 // variable which expected constant histogram name. |
| 224 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { | 229 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { |
| 225 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time); | 230 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time); |
| 226 } else { | 231 } else { |
| 227 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time); | 232 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time); |
| 228 } | 233 } |
| 229 | 234 |
| 230 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
| 231 | |
| 232 // If the frame size has changed, drop the output frame (if any), and | 235 // If the frame size has changed, drop the output frame (if any), and |
| 233 // determine the new output size. | 236 // determine the new output size. |
| 234 if (!previous_frame_size_.equals(frame->size())) { | 237 if (!previous_frame_size_.equals(frame->size())) { |
| 235 output_frame_.reset(); | 238 output_frame_.reset(); |
| 236 resolution_chooser_->SetSourceSize(gfx::Size(frame->size().width(), | 239 resolution_chooser_->SetSourceSize(gfx::Size(frame->size().width(), |
| 237 frame->size().height())); | 240 frame->size().height())); |
| 238 previous_frame_size_ = frame->size(); | 241 previous_frame_size_ = frame->size(); |
| 239 } | 242 } |
| 240 // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so | 243 // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so |
| 241 // it can convert the frame to I420 format. | 244 // it can convert the frame to I420 format. |
| 242 const webrtc::DesktopSize output_size( | 245 const webrtc::DesktopSize output_size( |
| 243 resolution_chooser_->capture_size().width() & ~1, | 246 resolution_chooser_->capture_size().width() & ~1, |
| 244 resolution_chooser_->capture_size().height() & ~1); | 247 resolution_chooser_->capture_size().height() & ~1); |
| 245 if (output_size.is_empty()) | 248 if (output_size.is_empty()) |
| 246 return; | 249 return; |
| 247 | 250 |
| 248 // On OSX We receive a 1x1 frame when the shared window is minimized. It | 251 size_t output_bytes = output_size.width() * output_size.height() * |
| 249 // cannot be subsampled to I420 and will be dropped downstream. So we replace | 252 webrtc::DesktopFrame::kBytesPerPixel; |
| 250 // it with a black frame to avoid the video appearing frozen at the last | 253 const uint8_t* output_data = nullptr; |
| 251 // frame. | 254 |
| 252 if (frame->size().width() == 1 || frame->size().height() == 1) { | 255 if (frame->size().equals(webrtc::DesktopSize(1, 1))) { |
| 253 if (!black_frame_) { | 256 // On OSX We receive a 1x1 frame when the shared window is minimized. It |
| 257 // cannot be subsampled to I420 and will be dropped downstream. So we |
| 258 // replace it with a black frame to avoid the video appearing frozen at the |
| 259 // last frame. |
| 260 if (!black_frame_ || !black_frame_->size().equals(output_size)) { |
| 254 black_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 261 black_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 255 memset(black_frame_->data(), | 262 memset(black_frame_->data(), 0, |
| 256 0, | |
| 257 black_frame_->stride() * black_frame_->size().height()); | 263 black_frame_->stride() * black_frame_->size().height()); |
| 258 } | 264 } |
| 259 owned_frame.reset(); | 265 output_data = black_frame_->data(); |
| 260 frame = black_frame_.get(); | 266 } else if (!frame->size().equals(output_size)) { |
| 261 } | |
| 262 | |
| 263 size_t output_bytes = output_size.width() * output_size.height() * | |
| 264 webrtc::DesktopFrame::kBytesPerPixel; | |
| 265 const uint8_t* output_data = NULL; | |
| 266 | |
| 267 if (!frame->size().equals(output_size)) { | |
| 268 // Down-scale and/or letterbox to the target format if the frame does not | 267 // Down-scale and/or letterbox to the target format if the frame does not |
| 269 // match the output size. | 268 // match the output size. |
| 270 | 269 |
| 271 // Allocate a buffer of the correct size to scale the frame into. | 270 // 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 | 271 // |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. | 272 // need to worry about clearing out stale pixel data in letterboxed areas. |
| 274 if (!output_frame_) { | 273 if (!output_frame_) { |
| 275 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 274 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 276 memset(output_frame_->data(), 0, output_bytes); | 275 memset(output_frame_->data(), 0, output_bytes); |
| 277 } | 276 } |
| 278 DCHECK(output_frame_->size().equals(output_size)); | 277 DCHECK(output_frame_->size().equals(output_size)); |
| 279 | 278 |
| 280 // TODO(wez): Optimize this to scale only changed portions of the output, | 279 // TODO(wez): Optimize this to scale only changed portions of the output, |
| 281 // using ARGBScaleClip(). | 280 // using ARGBScaleClip(). |
| 282 const webrtc::DesktopRect output_rect = | 281 const webrtc::DesktopRect output_rect = |
| 283 ComputeLetterboxRect(output_size, frame->size()); | 282 ComputeLetterboxRect(output_size, frame->size()); |
| 284 uint8_t* output_rect_data = output_frame_->data() + | 283 uint8_t* output_rect_data = |
| 285 output_frame_->stride() * output_rect.top() + | 284 output_frame_->GetFrameDataAtPos(output_rect.top_left()); |
| 286 webrtc::DesktopFrame::kBytesPerPixel * output_rect.left(); | 285 libyuv::ARGBScale(frame->data(), frame->stride(), frame->size().width(), |
| 287 libyuv::ARGBScale(frame->data(), frame->stride(), | 286 frame->size().height(), output_rect_data, |
| 288 frame->size().width(), frame->size().height(), | 287 output_frame_->stride(), output_rect.width(), |
| 289 output_rect_data, output_frame_->stride(), | 288 output_rect.height(), libyuv::kFilterBilinear); |
| 290 output_rect.width(), output_rect.height(), | |
| 291 libyuv::kFilterBilinear); | |
| 292 output_data = output_frame_->data(); | 289 output_data = output_frame_->data(); |
| 293 } else if (IsFrameUnpackedOrInverted(frame)) { | 290 } else if (IsFrameUnpackedOrInverted(frame.get())) { |
| 294 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom | 291 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom |
| 295 // copy. | 292 // copy. |
| 296 // This is required if the frame is inverted (see crbug.com/306876), or if | 293 // 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). | 294 // |frame| is cropped form a larger frame (see crbug.com/437740). |
| 298 if (!output_frame_) { | 295 if (!output_frame_) { |
| 299 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); | 296 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); |
| 300 memset(output_frame_->data(), 0, output_bytes); | 297 memset(output_frame_->data(), 0, output_bytes); |
| 301 } | 298 } |
| 302 | 299 |
| 303 output_frame_->CopyPixelsFrom( | 300 output_frame_->CopyPixelsFrom(*frame, webrtc::DesktopVector(), |
| 304 *frame, | 301 webrtc::DesktopRect::MakeSize(frame->size())); |
| 305 webrtc::DesktopVector(), | |
| 306 webrtc::DesktopRect::MakeSize(frame->size())); | |
| 307 output_data = output_frame_->data(); | 302 output_data = output_frame_->data(); |
| 308 } else { | 303 } else { |
| 309 // If the captured frame matches the output size, we can return the pixel | 304 // If the captured frame matches the output size, we can return the pixel |
| 310 // data directly. | 305 // data directly. |
| 311 output_data = frame->data(); | 306 output_data = frame->data(); |
| 312 } | 307 } |
| 313 | 308 |
| 314 base::TimeTicks now = base::TimeTicks::Now(); | 309 base::TimeTicks now = base::TimeTicks::Now(); |
| 315 if (first_ref_time_.is_null()) | 310 if (first_ref_time_.is_null()) |
| 316 first_ref_time_ = now; | 311 first_ref_time_ = now; |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 #else | 450 #else |
| 456 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT; | 451 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT; |
| 457 #endif | 452 #endif |
| 458 | 453 |
| 459 thread_.StartWithOptions(base::Thread::Options(thread_type, 0)); | 454 thread_.StartWithOptions(base::Thread::Options(thread_type, 0)); |
| 460 | 455 |
| 461 core_.reset(new Core(thread_.task_runner(), std::move(capturer), type)); | 456 core_.reset(new Core(thread_.task_runner(), std::move(capturer), type)); |
| 462 } | 457 } |
| 463 | 458 |
| 464 } // namespace content | 459 } // namespace content |
| OLD | NEW |