OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 12 matching lines...) Expand all Loading... | |
23 #include "webrtc/media/engine/webrtcvideoframefactory.h" | 23 #include "webrtc/media/engine/webrtcvideoframefactory.h" |
24 | 24 |
25 namespace cricket { | 25 namespace cricket { |
26 | 26 |
27 namespace { | 27 namespace { |
28 | 28 |
29 static const int64_t kMaxDistance = ~(static_cast<int64_t>(1) << 63); | 29 static const int64_t kMaxDistance = ~(static_cast<int64_t>(1) << 63); |
30 #ifdef WEBRTC_LINUX | 30 #ifdef WEBRTC_LINUX |
31 static const int kYU12Penalty = 16; // Needs to be higher than MJPG index. | 31 static const int kYU12Penalty = 16; // Needs to be higher than MJPG index. |
32 #endif | 32 #endif |
33 static const int kDefaultScreencastFps = 5; | |
34 | 33 |
35 } // namespace | 34 } // namespace |
36 | 35 |
37 ///////////////////////////////////////////////////////////////////// | 36 ///////////////////////////////////////////////////////////////////// |
38 // Implementation of struct CapturedFrame | 37 // Implementation of struct CapturedFrame |
39 ///////////////////////////////////////////////////////////////////// | 38 ///////////////////////////////////////////////////////////////////// |
40 CapturedFrame::CapturedFrame() | 39 CapturedFrame::CapturedFrame() |
41 : width(0), | 40 : width(0), |
42 height(0), | 41 height(0), |
43 fourcc(0), | 42 fourcc(0), |
(...skipping 15 matching lines...) Expand all Loading... | |
59 | 58 |
60 ///////////////////////////////////////////////////////////////////// | 59 ///////////////////////////////////////////////////////////////////// |
61 // Implementation of class VideoCapturer | 60 // Implementation of class VideoCapturer |
62 ///////////////////////////////////////////////////////////////////// | 61 ///////////////////////////////////////////////////////////////////// |
63 VideoCapturer::VideoCapturer() : apply_rotation_(false) { | 62 VideoCapturer::VideoCapturer() : apply_rotation_(false) { |
64 thread_checker_.DetachFromThread(); | 63 thread_checker_.DetachFromThread(); |
65 Construct(); | 64 Construct(); |
66 } | 65 } |
67 | 66 |
68 void VideoCapturer::Construct() { | 67 void VideoCapturer::Construct() { |
69 ratio_w_ = 0; | |
70 ratio_h_ = 0; | |
71 enable_camera_list_ = false; | 68 enable_camera_list_ = false; |
72 square_pixel_aspect_ratio_ = false; | |
73 capture_state_ = CS_STOPPED; | 69 capture_state_ = CS_STOPPED; |
74 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured); | 70 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured); |
75 scaled_width_ = 0; | 71 scaled_width_ = 0; |
76 scaled_height_ = 0; | 72 scaled_height_ = 0; |
77 enable_video_adapter_ = true; | 73 enable_video_adapter_ = true; |
78 // There are lots of video capturers out there that don't call | 74 // There are lots of video capturers out there that don't call |
79 // set_frame_factory. We can either go change all of them, or we | 75 // set_frame_factory. We can either go change all of them, or we |
80 // can set this default. | 76 // can set this default. |
81 // TODO(pthatcher): Remove this hack and require the frame factory | 77 // TODO(pthatcher): Remove this hack and require the frame factory |
82 // to be passed in the constructor. | 78 // to be passed in the constructor. |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
217 wants.max_pixel_count_step_up); | 213 wants.max_pixel_count_step_up); |
218 } | 214 } |
219 } | 215 } |
220 | 216 |
221 void VideoCapturer::OnFrameCaptured(VideoCapturer*, | 217 void VideoCapturer::OnFrameCaptured(VideoCapturer*, |
222 const CapturedFrame* captured_frame) { | 218 const CapturedFrame* captured_frame) { |
223 if (!broadcaster_.frame_wanted()) { | 219 if (!broadcaster_.frame_wanted()) { |
224 return; | 220 return; |
225 } | 221 } |
226 | 222 |
227 // Use a temporary buffer to scale | 223 int adapted_width = captured_frame->width; |
228 std::unique_ptr<uint8_t[]> scale_buffer; | 224 int adapted_height = captured_frame->height; |
229 if (IsScreencast()) { | |
230 int scaled_width, scaled_height; | |
231 int desired_screencast_fps = | |
232 capture_format_.get() | |
233 ? VideoFormat::IntervalToFps(capture_format_->interval) | |
234 : kDefaultScreencastFps; | |
235 ComputeScale(captured_frame->width, captured_frame->height, | |
236 desired_screencast_fps, &scaled_width, &scaled_height); | |
237 | |
238 if (FOURCC_ARGB == captured_frame->fourcc && | |
239 (scaled_width != captured_frame->width || | |
240 scaled_height != captured_frame->height)) { | |
241 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { | |
242 LOG(LS_INFO) << "Scaling Screencast from " << captured_frame->width | |
243 << "x" << captured_frame->height << " to " << scaled_width | |
244 << "x" << scaled_height; | |
245 scaled_width_ = scaled_width; | |
246 scaled_height_ = scaled_height; | |
247 } | |
248 CapturedFrame* modified_frame = | |
249 const_cast<CapturedFrame*>(captured_frame); | |
250 const int modified_frame_size = scaled_width * scaled_height * 4; | |
251 scale_buffer.reset(new uint8_t[modified_frame_size]); | |
252 // Compute new width such that width * height is less than maximum but | |
253 // maintains original captured frame aspect ratio. | |
254 // Round down width to multiple of 4 so odd width won't round up beyond | |
255 // maximum, and so chroma channel is even width to simplify spatial | |
256 // resampling. | |
257 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data), | |
258 captured_frame->width * 4, captured_frame->width, | |
259 captured_frame->height, scale_buffer.get(), | |
260 scaled_width * 4, scaled_width, scaled_height, | |
261 libyuv::kFilterBilinear); | |
262 modified_frame->width = scaled_width; | |
263 modified_frame->height = scaled_height; | |
264 modified_frame->data_size = scaled_width * 4 * scaled_height; | |
265 modified_frame->data = scale_buffer.get(); | |
266 } | |
267 } | |
268 | |
269 const int kYuy2Bpp = 2; | |
270 const int kArgbBpp = 4; | |
271 // TODO(fbarchard): Make a helper function to adjust pixels to square. | |
272 // TODO(fbarchard): Hook up experiment to scaling. | |
273 // Temporary buffer is scoped here so it will persist until i420_frame.Init() | |
274 // makes a copy of the frame, converting to I420. | |
275 std::unique_ptr<uint8_t[]> temp_buffer; | |
276 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only | |
277 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY. | |
278 bool can_scale = | |
279 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) || | |
280 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc); | |
281 | |
282 // If pixels are not square, optionally use vertical scaling to make them | |
283 // square. Square pixels simplify the rest of the pipeline, including | |
284 // effects and rendering. | |
285 if (can_scale && square_pixel_aspect_ratio_ && | |
286 captured_frame->pixel_width != captured_frame->pixel_height) { | |
287 int scaled_width, scaled_height; | |
288 // modified_frame points to the captured_frame but with const casted away | |
289 // so it can be modified. | |
290 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame); | |
291 // Compute the frame size that makes pixels square pixel aspect ratio. | |
292 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height, | |
293 captured_frame->pixel_width, | |
294 captured_frame->pixel_height, | |
295 &scaled_width, &scaled_height); | |
296 | |
297 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { | |
298 LOG(LS_INFO) << "Scaling WebCam from " | |
299 << captured_frame->width << "x" | |
300 << captured_frame->height << " to " | |
301 << scaled_width << "x" << scaled_height | |
302 << " for PAR " | |
303 << captured_frame->pixel_width << "x" | |
304 << captured_frame->pixel_height; | |
305 scaled_width_ = scaled_width; | |
306 scaled_height_ = scaled_height; | |
307 } | |
308 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp; | |
309 uint8_t* temp_buffer_data; | |
310 // Pixels are wide and short; Increasing height. Requires temporary buffer. | |
311 if (scaled_height > captured_frame->height) { | |
312 temp_buffer.reset(new uint8_t[modified_frame_size]); | |
313 temp_buffer_data = temp_buffer.get(); | |
314 } else { | |
315 // Pixels are narrow and tall; Decreasing height. Scale will be done | |
316 // in place. | |
317 temp_buffer_data = reinterpret_cast<uint8_t*>(captured_frame->data); | |
318 } | |
319 | |
320 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp. | |
321 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data), | |
322 captured_frame->width * kYuy2Bpp, // Stride for YUY2. | |
323 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width. | |
324 abs(captured_frame->height), // Height. | |
325 temp_buffer_data, | |
326 scaled_width * kYuy2Bpp, // Stride for YUY2. | |
327 scaled_width * kYuy2Bpp / kArgbBpp, // Width. | |
328 abs(scaled_height), // New height. | |
329 libyuv::kFilterBilinear); | |
330 modified_frame->width = scaled_width; | |
331 modified_frame->height = scaled_height; | |
332 modified_frame->pixel_width = 1; | |
333 modified_frame->pixel_height = 1; | |
334 modified_frame->data_size = modified_frame_size; | |
335 modified_frame->data = temp_buffer_data; | |
336 } | |
337 | |
338 // Size to crop captured frame to. This adjusts the captured frames | |
339 // aspect ratio to match the final view aspect ratio, considering pixel | |
340 // aspect ratio and rotation. The final size may be scaled down by video | |
341 // adapter to better match ratio_w_ x ratio_h_. | |
342 // Note that abs() of frame height is passed in, because source may be | |
343 // inverted, but output will be positive. | |
344 int cropped_width = captured_frame->width; | |
345 int cropped_height = captured_frame->height; | |
346 | |
347 // TODO(fbarchard): Improve logic to pad or crop. | |
348 // MJPG can crop vertically, but not horizontally. This logic disables crop. | |
349 // Alternatively we could pad the image with black, or implement a 2 step | |
350 // crop. | |
351 bool can_crop = true; | |
352 if (captured_frame->fourcc == FOURCC_MJPG) { | |
353 float cam_aspect = static_cast<float>(captured_frame->width) / | |
354 static_cast<float>(captured_frame->height); | |
355 float view_aspect = static_cast<float>(ratio_w_) / | |
356 static_cast<float>(ratio_h_); | |
357 can_crop = cam_aspect <= view_aspect; | |
358 } | |
359 if (can_crop && !IsScreencast()) { | |
360 // TODO(ronghuawu): The capturer should always produce the native | |
361 // resolution and the cropping should be done in downstream code. | |
362 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width, | |
363 abs(captured_frame->height), captured_frame->pixel_width, | |
364 captured_frame->pixel_height, captured_frame->rotation, | |
365 &cropped_width, &cropped_height); | |
366 } | |
367 | |
368 int adapted_width = cropped_width; | |
369 int adapted_height = cropped_height; | |
370 if (enable_video_adapter_ && !IsScreencast()) { | 225 if (enable_video_adapter_ && !IsScreencast()) { |
371 const VideoFormat adapted_format = | 226 const VideoFormat adapted_format = |
372 video_adapter_.AdaptFrameResolution(cropped_width, cropped_height); | 227 video_adapter_.AdaptFrameResolution(adapted_width, adapted_height); |
373 if (adapted_format.IsSize0x0()) { | 228 if (adapted_format.IsSize0x0()) { |
374 // VideoAdapter dropped the frame. | 229 // VideoAdapter dropped the frame. |
375 return; | 230 return; |
376 } | 231 } |
377 adapted_width = adapted_format.width; | 232 adapted_width = adapted_format.width; |
378 adapted_height = adapted_format.height; | 233 adapted_height = adapted_format.height; |
379 } | 234 } |
380 | 235 |
381 if (!frame_factory_) { | 236 if (!frame_factory_) { |
382 LOG(LS_ERROR) << "No video frame factory."; | 237 LOG(LS_ERROR) << "No video frame factory."; |
383 return; | 238 return; |
384 } | 239 } |
385 | 240 |
386 std::unique_ptr<VideoFrame> adapted_frame( | 241 // TODO(nisse): Reorganize frame factory methods, deleting crop |
387 frame_factory_->CreateAliasedFrame(captured_frame, | 242 // support there too. |
388 cropped_width, cropped_height, | 243 std::unique_ptr<VideoFrame> adapted_frame(frame_factory_->CreateAliasedFrame( |
389 adapted_width, adapted_height)); | 244 captured_frame, captured_frame->width, captured_frame->height, |
245 adapted_width, adapted_height)); | |
390 | 246 |
391 if (!adapted_frame) { | 247 if (!adapted_frame) { |
392 // TODO(fbarchard): LOG more information about captured frame attributes. | 248 // TODO(fbarchard): LOG more information about captured frame attributes. |
perkj_webrtc
2016/05/04 10:47:45
maybe remove this TODO too.
| |
393 LOG(LS_ERROR) << "Couldn't convert to I420! " | 249 LOG(LS_ERROR) << "Couldn't convert to I420! " |
394 << "From " << ToString(captured_frame) << " To " | 250 << "From " << ToString(captured_frame) << " To " |
395 << cropped_width << " x " << cropped_height; | 251 << adapted_width << " x " << adapted_height; |
396 return; | 252 return; |
397 } | 253 } |
398 | 254 |
399 OnFrame(this, adapted_frame.get()); | 255 OnFrame(this, adapted_frame.get()); |
400 UpdateInputSize(captured_frame); | 256 UpdateInputSize(captured_frame); |
401 } | 257 } |
402 | 258 |
403 void VideoCapturer::OnFrame(VideoCapturer* capturer, const VideoFrame* frame) { | 259 void VideoCapturer::OnFrame(VideoCapturer* capturer, const VideoFrame* frame) { |
404 broadcaster_.OnFrame(*frame); | 260 broadcaster_.OnFrame(*frame); |
405 } | 261 } |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
545 void VideoCapturer::UpdateInputSize(const CapturedFrame* captured_frame) { | 401 void VideoCapturer::UpdateInputSize(const CapturedFrame* captured_frame) { |
546 // Update stats protected from fetches from different thread. | 402 // Update stats protected from fetches from different thread. |
547 rtc::CritScope cs(&frame_stats_crit_); | 403 rtc::CritScope cs(&frame_stats_crit_); |
548 | 404 |
549 input_size_valid_ = true; | 405 input_size_valid_ = true; |
550 input_width_ = captured_frame->width; | 406 input_width_ = captured_frame->width; |
551 input_height_ = captured_frame->height; | 407 input_height_ = captured_frame->height; |
552 } | 408 } |
553 | 409 |
554 } // namespace cricket | 410 } // namespace cricket |
OLD | NEW |