1 /* 2 * libjingle 3 * Copyright 2010 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 // Implementation file of class VideoCapturer. 29 30 #include "talk/media/base/videocapturer.h" 31 32 #include <algorithm> 33 34 #include "libyuv/scale_argb.h" 35 #include "talk/media/base/videoframefactory.h" 36 #include "webrtc/base/common.h" 37 #include "webrtc/base/logging.h" 38 #include "webrtc/base/systeminfo.h" 39 40 #if defined(HAVE_WEBRTC_VIDEO) 41 #include "talk/media/webrtc/webrtcvideoframe.h" 42 #include "talk/media/webrtc/webrtcvideoframefactory.h" 43 #endif // HAVE_WEBRTC_VIDEO 44 45 namespace cricket { 46 47 namespace { 48 49 // TODO(thorcarpenter): This is a BIG hack to flush the system with black 50 // frames. Frontends should coordinate to update the video state of a muted 51 // user. When all frontends to this consider removing the black frame business. 52 const int kNumBlackFramesOnMute = 30; 53 54 // MessageHandler constants. 55 enum { 56 MSG_DO_PAUSE = 0, 57 MSG_DO_UNPAUSE, 58 MSG_STATE_CHANGE 59 }; 60 61 static const int64_t kMaxDistance = ~(static_cast<int64_t>(1) << 63); 62 #ifdef WEBRTC_LINUX 63 static const int kYU12Penalty = 16; // Needs to be higher than MJPG index. 64 #endif 65 static const int kDefaultScreencastFps = 5; 66 typedef rtc::TypedMessageData<CaptureState> StateChangeParams; 67 68 // Limit stats data collections to ~20 seconds of 30fps data before dropping 69 // old data in case stats aren't reset for long periods of time. 70 static const size_t kMaxAccumulatorSize = 600; 71 72 } // namespace 73 74 ///////////////////////////////////////////////////////////////////// 75 // Implementation of struct CapturedFrame 76 ///////////////////////////////////////////////////////////////////// 77 CapturedFrame::CapturedFrame() 78 : width(0), 79 height(0), 80 fourcc(0), 81 pixel_width(0), 82 pixel_height(0), 83 time_stamp(0), 84 data_size(0), 85 rotation(webrtc::kVideoRotation_0), 86 data(NULL) {} 87 88 // TODO(fbarchard): Remove this function once lmimediaengine stops using it. 89 bool CapturedFrame::GetDataSize(uint32_t* size) const { 90 if (!size || data_size == CapturedFrame::kUnknownDataSize) { 91 return false; 92 } 93 *size = data_size; 94 return true; 95 } 96 97 ///////////////////////////////////////////////////////////////////// 98 // Implementation of class VideoCapturer 99 ///////////////////////////////////////////////////////////////////// 100 VideoCapturer::VideoCapturer() 101 : thread_(rtc::Thread::Current()), 102 adapt_frame_drops_data_(kMaxAccumulatorSize), 103 frame_time_data_(kMaxAccumulatorSize), 104 apply_rotation_(true) { 105 Construct(); 106 } 107 108 VideoCapturer::VideoCapturer(rtc::Thread* thread) 109 : thread_(thread), 110 adapt_frame_drops_data_(kMaxAccumulatorSize), 111 frame_time_data_(kMaxAccumulatorSize), 112 apply_rotation_(true) { 113 Construct(); 114 } 115 116 void VideoCapturer::Construct() { 117 ClearAspectRatio(); 118 enable_camera_list_ = false; 119 square_pixel_aspect_ratio_ = false; 120 capture_state_ = CS_STOPPED; 121 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured); 122 scaled_width_ = 0; 123 scaled_height_ = 0; 124 muted_ = false; 125 black_frame_count_down_ = kNumBlackFramesOnMute; 126 enable_video_adapter_ = true; 127 adapt_frame_drops_ = 0; 128 previous_frame_time_ = 0.0; 129 #ifdef HAVE_WEBRTC_VIDEO 130 // There are lots of video capturers out there that don't call 131 // set_frame_factory. We can either go change all of them, or we 132 // can set this default. 133 // TODO(pthatcher): Remove this hack and require the frame factory 134 // to be passed in the constructor. 135 set_frame_factory(new WebRtcVideoFrameFactory()); 136 #endif 137 } 138 139 const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const { 140 return &filtered_supported_formats_; 141 } 142 143 bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) { 144 previous_frame_time_ = frame_length_time_reporter_.TimerNow(); 145 CaptureState result = Start(capture_format); 146 const bool success = (result == CS_RUNNING) || (result == CS_STARTING); 147 if (!success) { 148 return false; 149 } 150 if (result == CS_RUNNING) { 151 SetCaptureState(result); 152 } 153 return true; 154 } 155 156 void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) { 157 if (ratio_w == 0 || ratio_h == 0) { 158 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: " 159 << ratio_w << "x" << ratio_h; 160 return; 161 } 162 ratio_w_ = ratio_w; 163 ratio_h_ = ratio_h; 164 } 165 166 void VideoCapturer::ClearAspectRatio() { 167 ratio_w_ = 0; 168 ratio_h_ = 0; 169 } 170 171 // Override this to have more control of how your device is started/stopped. 172 bool VideoCapturer::Pause(bool pause) { 173 if (pause) { 174 if (capture_state() == CS_PAUSED) { 175 return true; 176 } 177 bool is_running = capture_state() == CS_STARTING || 178 capture_state() == CS_RUNNING; 179 if (!is_running) { 180 LOG(LS_ERROR) << "Cannot pause a stopped camera."; 181 return false; 182 } 183 LOG(LS_INFO) << "Pausing a camera."; 184 rtc::scoped_ptr<VideoFormat> capture_format_when_paused( 185 capture_format_ ? new VideoFormat(*capture_format_) : NULL); 186 Stop(); 187 SetCaptureState(CS_PAUSED); 188 // If you override this function be sure to restore the capture format 189 // after calling Stop(). 190 SetCaptureFormat(capture_format_when_paused.get()); 191 } else { // Unpause. 192 if (capture_state() != CS_PAUSED) { 193 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused."; 194 return false; 195 } 196 if (!capture_format_) { 197 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera."; 198 return false; 199 } 200 if (muted_) { 201 LOG(LS_WARNING) << "Camera cannot be unpaused while muted."; 202 return false; 203 } 204 LOG(LS_INFO) << "Unpausing a camera."; 205 if (!Start(*capture_format_)) { 206 LOG(LS_ERROR) << "Camera failed to start when unpausing."; 207 return false; 208 } 209 } 210 return true; 211 } 212 213 bool VideoCapturer::Restart(const VideoFormat& capture_format) { 214 if (!IsRunning()) { 215 return StartCapturing(capture_format); 216 } 217 218 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) { 219 // The reqested format is the same; nothing to do. 220 return true; 221 } 222 223 Stop(); 224 return StartCapturing(capture_format); 225 } 226 227 bool VideoCapturer::MuteToBlackThenPause(bool muted) { 228 if (muted == IsMuted()) { 229 return true; 230 } 231 232 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer."; 233 muted_ = muted; // Do this before calling Pause(). 234 if (muted) { 235 // Reset black frame count down. 236 black_frame_count_down_ = kNumBlackFramesOnMute; 237 // Following frames will be overritten with black, then the camera will be 238 // paused. 239 return true; 240 } 241 // Start the camera. 242 thread_->Clear(this, MSG_DO_PAUSE); 243 return Pause(false); 244 } 245 246 // Note that the last caller decides whether rotation should be applied if there 247 // are multiple send streams using the same camera. 248 bool VideoCapturer::SetApplyRotation(bool enable) { 249 apply_rotation_ = enable; 250 if (frame_factory_) { 251 frame_factory_->SetApplyRotation(apply_rotation_); 252 } 253 return true; 254 } 255 256 void VideoCapturer::SetSupportedFormats( 257 const std::vector<VideoFormat>& formats) { 258 supported_formats_ = formats; 259 UpdateFilteredSupportedFormats(); 260 } 261 262 bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format, 263 VideoFormat* best_format) { 264 // TODO(fbarchard): Directly support max_format. 265 UpdateFilteredSupportedFormats(); 266 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats(); 267 268 if (supported_formats->empty()) { 269 return false; 270 } 271 LOG(LS_INFO) << " Capture Requested " << format.ToString(); 272 int64_t best_distance = kMaxDistance; 273 std::vector<VideoFormat>::const_iterator best = supported_formats->end(); 274 std::vector<VideoFormat>::const_iterator i; 275 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) { 276 int64_t distance = GetFormatDistance(format, *i); 277 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is 278 // relatively bug free. 279 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance; 280 if (distance < best_distance) { 281 best_distance = distance; 282 best = i; 283 } 284 } 285 if (supported_formats->end() == best) { 286 LOG(LS_ERROR) << " No acceptable camera format found"; 287 return false; 288 } 289 290 if (best_format) { 291 best_format->width = best->width; 292 best_format->height = best->height; 293 best_format->fourcc = best->fourcc; 294 best_format->interval = best->interval; 295 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval " 296 << best_format->interval << " distance " << best_distance; 297 } 298 return true; 299 } 300 301 void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) { 302 max_format_.reset(new VideoFormat(max_format)); 303 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString(); 304 UpdateFilteredSupportedFormats(); 305 } 306 307 std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const { 308 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " "; 309 for (std::string::const_iterator i = fourcc_name.begin(); 310 i < fourcc_name.end(); ++i) { 311 // Test character is printable; Avoid isprint() which asserts on negatives. 312 if (*i < 32 || *i >= 127) { 313 fourcc_name = ""; 314 break; 315 } 316 } 317 318 std::ostringstream ss; 319 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height; 320 return ss.str(); 321 } 322 323 void VideoCapturer::set_frame_factory(VideoFrameFactory* frame_factory) { 324 frame_factory_.reset(frame_factory); 325 if (frame_factory) { 326 frame_factory->SetApplyRotation(apply_rotation_); 327 } 328 } 329 330 void VideoCapturer::GetStats(VariableInfo<int>* adapt_drops_stats, 331 VariableInfo<int>* effect_drops_stats, 332 VariableInfo<double>* frame_time_stats, 333 VideoFormat* last_captured_frame_format) { 334 rtc::CritScope cs(&frame_stats_crit_); 335 GetVariableSnapshot(adapt_frame_drops_data_, adapt_drops_stats); 336 GetVariableSnapshot(frame_time_data_, frame_time_stats); 337 *last_captured_frame_format = last_captured_frame_format_; 338 339 adapt_frame_drops_data_.Reset(); 340 frame_time_data_.Reset(); 341 } 342 343 void VideoCapturer::OnFrameCaptured(VideoCapturer*, 344 const CapturedFrame* captured_frame) { 345 if (muted_) { 346 if (black_frame_count_down_ == 0) { 347 thread_->Post(this, MSG_DO_PAUSE, NULL); 348 } else { 349 --black_frame_count_down_; 350 } 351 } 352 353 if (SignalVideoFrame.is_empty()) { 354 return; 355 } 356 357 // Use a temporary buffer to scale 358 rtc::scoped_ptr<uint8_t[]> scale_buffer; 359 360 if (IsScreencast()) { 361 int scaled_width, scaled_height; 362 int desired_screencast_fps = capture_format_.get() ? 363 VideoFormat::IntervalToFps(capture_format_->interval) : 364 kDefaultScreencastFps; 365 ComputeScale(captured_frame->width, captured_frame->height, 366 desired_screencast_fps, &scaled_width, &scaled_height); 367 368 if (FOURCC_ARGB == captured_frame->fourcc && 369 (scaled_width != captured_frame->width || 370 scaled_height != captured_frame->height)) { 371 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { 372 LOG(LS_INFO) << "Scaling Screencast from " 373 << captured_frame->width << "x" 374 << captured_frame->height << " to " 375 << scaled_width << "x" << scaled_height; 376 scaled_width_ = scaled_width; 377 scaled_height_ = scaled_height; 378 } 379 CapturedFrame* modified_frame = 380 const_cast<CapturedFrame*>(captured_frame); 381 const int modified_frame_size = scaled_width * scaled_height * 4; 382 scale_buffer.reset(new uint8_t[modified_frame_size]); 383 // Compute new width such that width * height is less than maximum but 384 // maintains original captured frame aspect ratio. 385 // Round down width to multiple of 4 so odd width won't round up beyond 386 // maximum, and so chroma channel is even width to simplify spatial 387 // resampling. 388 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data), 389 captured_frame->width * 4, captured_frame->width, 390 captured_frame->height, scale_buffer.get(), 391 scaled_width * 4, scaled_width, scaled_height, 392 libyuv::kFilterBilinear); 393 modified_frame->width = scaled_width; 394 modified_frame->height = scaled_height; 395 modified_frame->data_size = scaled_width * 4 * scaled_height; 396 modified_frame->data = scale_buffer.get(); 397 } 398 } 399 400 const int kYuy2Bpp = 2; 401 const int kArgbBpp = 4; 402 // TODO(fbarchard): Make a helper function to adjust pixels to square. 403 // TODO(fbarchard): Hook up experiment to scaling. 404 // TODO(fbarchard): Avoid scale and convert if muted. 405 // Temporary buffer is scoped here so it will persist until i420_frame.Init() 406 // makes a copy of the frame, converting to I420. 407 rtc::scoped_ptr<uint8_t[]> temp_buffer; 408 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only 409 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY. 410 bool can_scale = 411 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) || 412 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc); 413 414 // If pixels are not square, optionally use vertical scaling to make them 415 // square. Square pixels simplify the rest of the pipeline, including 416 // effects and rendering. 417 if (can_scale && square_pixel_aspect_ratio_ && 418 captured_frame->pixel_width != captured_frame->pixel_height) { 419 int scaled_width, scaled_height; 420 // modified_frame points to the captured_frame but with const casted away 421 // so it can be modified. 422 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame); 423 // Compute the frame size that makes pixels square pixel aspect ratio. 424 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height, 425 captured_frame->pixel_width, 426 captured_frame->pixel_height, 427 &scaled_width, &scaled_height); 428 429 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { 430 LOG(LS_INFO) << "Scaling WebCam from " 431 << captured_frame->width << "x" 432 << captured_frame->height << " to " 433 << scaled_width << "x" << scaled_height 434 << " for PAR " 435 << captured_frame->pixel_width << "x" 436 << captured_frame->pixel_height; 437 scaled_width_ = scaled_width; 438 scaled_height_ = scaled_height; 439 } 440 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp; 441 uint8_t* temp_buffer_data; 442 // Pixels are wide and short; Increasing height. Requires temporary buffer. 443 if (scaled_height > captured_frame->height) { 444 temp_buffer.reset(new uint8_t[modified_frame_size]); 445 temp_buffer_data = temp_buffer.get(); 446 } else { 447 // Pixels are narrow and tall; Decreasing height. Scale will be done 448 // in place. 449 temp_buffer_data = reinterpret_cast<uint8_t*>(captured_frame->data); 450 } 451 452 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp. 453 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data), 454 captured_frame->width * kYuy2Bpp, // Stride for YUY2. 455 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width. 456 abs(captured_frame->height), // Height. 457 temp_buffer_data, 458 scaled_width * kYuy2Bpp, // Stride for YUY2. 459 scaled_width * kYuy2Bpp / kArgbBpp, // Width. 460 abs(scaled_height), // New height. 461 libyuv::kFilterBilinear); 462 modified_frame->width = scaled_width; 463 modified_frame->height = scaled_height; 464 modified_frame->pixel_width = 1; 465 modified_frame->pixel_height = 1; 466 modified_frame->data_size = modified_frame_size; 467 modified_frame->data = temp_buffer_data; 468 } 469 470 // Size to crop captured frame to. This adjusts the captured frames 471 // aspect ratio to match the final view aspect ratio, considering pixel 472 // aspect ratio and rotation. The final size may be scaled down by video 473 // adapter to better match ratio_w_ x ratio_h_. 474 // Note that abs() of frame height is passed in, because source may be 475 // inverted, but output will be positive. 476 int cropped_width = captured_frame->width; 477 int cropped_height = captured_frame->height; 478 479 // TODO(fbarchard): Improve logic to pad or crop. 480 // MJPG can crop vertically, but not horizontally. This logic disables crop. 481 // Alternatively we could pad the image with black, or implement a 2 step 482 // crop. 483 bool can_crop = true; 484 if (captured_frame->fourcc == FOURCC_MJPG) { 485 float cam_aspect = static_cast<float>(captured_frame->width) / 486 static_cast<float>(captured_frame->height); 487 float view_aspect = static_cast<float>(ratio_w_) / 488 static_cast<float>(ratio_h_); 489 can_crop = cam_aspect <= view_aspect; 490 } 491 if (can_crop && !IsScreencast()) { 492 // TODO(ronghuawu): The capturer should always produce the native 493 // resolution and the cropping should be done in downstream code. 494 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width, 495 abs(captured_frame->height), captured_frame->pixel_width, 496 captured_frame->pixel_height, captured_frame->rotation, 497 &cropped_width, &cropped_height); 498 } 499 500 int adapted_width = cropped_width; 501 int adapted_height = cropped_height; 502 if (enable_video_adapter_ && !IsScreencast()) { 503 const VideoFormat adapted_format = 504 video_adapter_.AdaptFrameResolution(cropped_width, cropped_height); 505 if (adapted_format.IsSize0x0()) { 506 // VideoAdapter dropped the frame. 507 ++adapt_frame_drops_; 508 return; 509 } 510 adapted_width = adapted_format.width; 511 adapted_height = adapted_format.height; 512 } 513 514 if (!frame_factory_) { 515 LOG(LS_ERROR) << "No video frame factory."; 516 return; 517 } 518 519 rtc::scoped_ptr<VideoFrame> adapted_frame( 520 frame_factory_->CreateAliasedFrame(captured_frame, 521 cropped_width, cropped_height, 522 adapted_width, adapted_height)); 523 524 if (!adapted_frame) { 525 // TODO(fbarchard): LOG more information about captured frame attributes. 526 LOG(LS_ERROR) << "Couldn't convert to I420! " 527 << "From " << ToString(captured_frame) << " To " 528 << cropped_width << " x " << cropped_height; 529 return; 530 } 531 532 if (muted_) { 533 // TODO(pthatcher): Use frame_factory_->CreateBlackFrame() instead. 534 adapted_frame->SetToBlack(); 535 } 536 SignalVideoFrame(this, adapted_frame.get()); 537 538 UpdateStats(captured_frame); 539 } 540 541 void VideoCapturer::SetCaptureState(CaptureState state) { 542 if (state == capture_state_) { 543 // Don't trigger a state changed callback if the state hasn't changed. 544 return; 545 } 546 StateChangeParams* state_params = new StateChangeParams(state); 547 capture_state_ = state; 548 thread_->Post(this, MSG_STATE_CHANGE, state_params); 549 } 550 551 void VideoCapturer::OnMessage(rtc::Message* message) { 552 switch (message->message_id) { 553 case MSG_STATE_CHANGE: { 554 rtc::scoped_ptr<StateChangeParams> p( 555 static_cast<StateChangeParams*>(message->pdata)); 556 SignalStateChange(this, p->data()); 557 break; 558 } 559 case MSG_DO_PAUSE: { 560 Pause(true); 561 break; 562 } 563 case MSG_DO_UNPAUSE: { 564 Pause(false); 565 break; 566 } 567 default: { 568 ASSERT(false); 569 } 570 } 571 } 572 573 // Get the distance between the supported and desired formats. 574 // Prioritization is done according to this algorithm: 575 // 1) Width closeness. If not same, we prefer wider. 576 // 2) Height closeness. If not same, we prefer higher. 577 // 3) Framerate closeness. If not same, we prefer faster. 578 // 4) Compression. If desired format has a specific fourcc, we need exact match; 579 // otherwise, we use preference. 580 int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired, 581 const VideoFormat& supported) { 582 int64_t distance = kMaxDistance; 583 584 // Check fourcc. 585 uint32_t supported_fourcc = CanonicalFourCC(supported.fourcc); 586 int64_t delta_fourcc = kMaxDistance; 587 if (FOURCC_ANY == desired.fourcc) { 588 // Any fourcc is OK for the desired. Use preference to find best fourcc. 589 std::vector<uint32_t> preferred_fourccs; 590 if (!GetPreferredFourccs(&preferred_fourccs)) { 591 return distance; 592 } 593 594 for (size_t i = 0; i < preferred_fourccs.size(); ++i) { 595 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) { 596 delta_fourcc = i; 597 #ifdef WEBRTC_LINUX 598 // For HD avoid YU12 which is a software conversion and has 2 bugs 599 // b/7326348 b/6960899. Reenable when fixed. 600 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 || 601 supported_fourcc == FOURCC_YV12)) { 602 delta_fourcc += kYU12Penalty; 603 } 604 #endif 605 break; 606 } 607 } 608 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) { 609 delta_fourcc = 0; // Need exact match. 610 } 611 612 if (kMaxDistance == delta_fourcc) { 613 // Failed to match fourcc. 614 return distance; 615 } 616 617 // Check resolution and fps. 618 int desired_width = desired.width; 619 int desired_height = desired.height; 620 int64_t delta_w = supported.width - desired_width; 621 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval); 622 float delta_fps = 623 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval); 624 // Check height of supported height compared to height we would like it to be. 625 int64_t aspect_h = desired_width 626 ? supported.width * desired_height / desired_width 627 : desired_height; 628 int64_t delta_h = supported.height - aspect_h; 629 630 distance = 0; 631 // Set high penalty if the supported format is lower than the desired format. 632 // 3x means we would prefer down to down to 3/4, than up to double. 633 // But we'd prefer up to double than down to 1/2. This is conservative, 634 // strongly avoiding going down in resolution, similar to 635 // the old method, but not completely ruling it out in extreme situations. 636 // It also ignores framerate, which is often very low at high resolutions. 637 // TODO(fbarchard): Improve logic to use weighted factors. 638 static const int kDownPenalty = -3; 639 if (delta_w < 0) { 640 delta_w = delta_w * kDownPenalty; 641 } 642 if (delta_h < 0) { 643 delta_h = delta_h * kDownPenalty; 644 } 645 // Require camera fps to be at least 80% of what is requested if resolution 646 // matches. 647 // Require camera fps to be at least 96% of what is requested, or higher, 648 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97 649 if (delta_fps < 0) { 650 float min_desirable_fps = delta_w ? 651 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f : 652 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f; 653 delta_fps = -delta_fps; 654 if (supported_fps < min_desirable_fps) { 655 distance |= static_cast<int64_t>(1) << 62; 656 } else { 657 distance |= static_cast<int64_t>(1) << 15; 658 } 659 } 660 int64_t idelta_fps = static_cast<int>(delta_fps); 661 662 // 12 bits for width and height and 8 bits for fps and fourcc. 663 distance |= 664 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc; 665 666 return distance; 667 } 668 669 void VideoCapturer::UpdateFilteredSupportedFormats() { 670 filtered_supported_formats_.clear(); 671 filtered_supported_formats_ = supported_formats_; 672 if (!max_format_) { 673 return; 674 } 675 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin(); 676 while (iter != filtered_supported_formats_.end()) { 677 if (ShouldFilterFormat(*iter)) { 678 iter = filtered_supported_formats_.erase(iter); 679 } else { 680 ++iter; 681 } 682 } 683 if (filtered_supported_formats_.empty()) { 684 // The device only captures at resolutions higher than |max_format_| this 685 // indicates that |max_format_| should be ignored as it is better to capture 686 // at too high a resolution than to not capture at all. 687 filtered_supported_formats_ = supported_formats_; 688 } 689 } 690 691 bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const { 692 if (!enable_camera_list_) { 693 return false; 694 } 695 return format.width > max_format_->width || 696 format.height > max_format_->height; 697 } 698 699 void VideoCapturer::UpdateStats(const CapturedFrame* captured_frame) { 700 // Update stats protected from fetches from different thread. 701 rtc::CritScope cs(&frame_stats_crit_); 702 703 last_captured_frame_format_.width = captured_frame->width; 704 last_captured_frame_format_.height = captured_frame->height; 705 // TODO(ronghuawu): Useful to report interval as well? 706 last_captured_frame_format_.interval = 0; 707 last_captured_frame_format_.fourcc = captured_frame->fourcc; 708 709 double time_now = frame_length_time_reporter_.TimerNow(); 710 if (previous_frame_time_ != 0.0) { 711 adapt_frame_drops_data_.AddSample(adapt_frame_drops_); 712 frame_time_data_.AddSample(time_now - previous_frame_time_); 713 } 714 previous_frame_time_ = time_now; 715 adapt_frame_drops_ = 0; 716 } 717 718 template<class T> 719 void VideoCapturer::GetVariableSnapshot( 720 const rtc::RollingAccumulator<T>& data, 721 VariableInfo<T>* stats) { 722 stats->max_val = data.ComputeMax(); 723 stats->mean = data.ComputeMean(); 724 stats->min_val = data.ComputeMin(); 725 stats->variance = data.ComputeVariance(); 726 } 727 728 } // namespace cricket 729