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