Chromium Code Reviews| 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 |