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 |