videocapturer.cc revision 97077a3ab27259164eb121034b6e0ebe9ba592df
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 45namespace cricket { 46 47namespace { 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. 52const int kNumBlackFramesOnMute = 30; 53 54// MessageHandler constants. 55enum { 56 MSG_DO_PAUSE = 0, 57 MSG_DO_UNPAUSE, 58 MSG_STATE_CHANGE 59}; 60 61static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63); 62static const int kYU12Penalty = 16; // Needs to be higher than MJPG index. 63static const int kDefaultScreencastFps = 5; 64typedef talk_base::TypedMessageData<CaptureState> StateChangeParams; 65 66} // namespace 67 68///////////////////////////////////////////////////////////////////// 69// Implementation of struct CapturedFrame 70///////////////////////////////////////////////////////////////////// 71CapturedFrame::CapturedFrame() 72 : width(0), 73 height(0), 74 fourcc(0), 75 pixel_width(0), 76 pixel_height(0), 77 elapsed_time(0), 78 time_stamp(0), 79 data_size(0), 80 rotation(0), 81 data(NULL) {} 82 83// TODO(fbarchard): Remove this function once lmimediaengine stops using it. 84bool CapturedFrame::GetDataSize(uint32* size) const { 85 if (!size || data_size == CapturedFrame::kUnknownDataSize) { 86 return false; 87 } 88 *size = data_size; 89 return true; 90} 91 92///////////////////////////////////////////////////////////////////// 93// Implementation of class VideoCapturer 94///////////////////////////////////////////////////////////////////// 95VideoCapturer::VideoCapturer() : thread_(talk_base::Thread::Current()) { 96 Construct(); 97} 98 99VideoCapturer::VideoCapturer(talk_base::Thread* thread) : thread_(thread) { 100 Construct(); 101} 102 103void VideoCapturer::Construct() { 104 ClearAspectRatio(); 105 enable_camera_list_ = false; 106 square_pixel_aspect_ratio_ = false; 107 capture_state_ = CS_STOPPED; 108 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured); 109 scaled_width_ = 0; 110 scaled_height_ = 0; 111 screencast_max_pixels_ = 0; 112 muted_ = false; 113 black_frame_count_down_ = kNumBlackFramesOnMute; 114} 115 116const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const { 117 return &filtered_supported_formats_; 118} 119 120bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) { 121 CaptureState result = Start(capture_format); 122 const bool success = (result == CS_RUNNING) || (result == CS_STARTING); 123 if (!success) { 124 return false; 125 } 126 if (result == CS_RUNNING) { 127 SetCaptureState(result); 128 } 129 return true; 130} 131 132void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) { 133 if (ratio_w == 0 || ratio_h == 0) { 134 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: " 135 << ratio_w << "x" << ratio_h; 136 return; 137 } 138 ratio_w_ = ratio_w; 139 ratio_h_ = ratio_h; 140} 141 142void VideoCapturer::ClearAspectRatio() { 143 ratio_w_ = 0; 144 ratio_h_ = 0; 145} 146 147// Override this to have more control of how your device is started/stopped. 148bool VideoCapturer::Pause(bool pause) { 149 if (pause) { 150 if (capture_state() == CS_PAUSED) { 151 return true; 152 } 153 bool is_running = capture_state() == CS_STARTING || 154 capture_state() == CS_RUNNING; 155 if (!is_running) { 156 LOG(LS_ERROR) << "Cannot pause a stopped camera."; 157 return false; 158 } 159 LOG(LS_INFO) << "Pausing a camera."; 160 talk_base::scoped_ptr<VideoFormat> capture_format_when_paused( 161 capture_format_ ? new VideoFormat(*capture_format_) : NULL); 162 Stop(); 163 SetCaptureState(CS_PAUSED); 164 // If you override this function be sure to restore the capture format 165 // after calling Stop(). 166 SetCaptureFormat(capture_format_when_paused.get()); 167 } else { // Unpause. 168 if (capture_state() != CS_PAUSED) { 169 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused."; 170 return false; 171 } 172 if (!capture_format_) { 173 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera."; 174 return false; 175 } 176 if (muted_) { 177 LOG(LS_WARNING) << "Camera cannot be unpaused while muted."; 178 return false; 179 } 180 LOG(LS_INFO) << "Unpausing a camera."; 181 if (!Start(*capture_format_)) { 182 LOG(LS_ERROR) << "Camera failed to start when unpausing."; 183 return false; 184 } 185 } 186 return true; 187} 188 189bool VideoCapturer::Restart(const VideoFormat& capture_format) { 190 if (!IsRunning()) { 191 return StartCapturing(capture_format); 192 } 193 194 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) { 195 // The reqested format is the same; nothing to do. 196 return true; 197 } 198 199 Stop(); 200 return StartCapturing(capture_format); 201} 202 203bool VideoCapturer::MuteToBlackThenPause(bool muted) { 204 if (muted == IsMuted()) { 205 return true; 206 } 207 208 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer."; 209 muted_ = muted; // Do this before calling Pause(). 210 if (muted) { 211 // Reset black frame count down. 212 black_frame_count_down_ = kNumBlackFramesOnMute; 213 // Following frames will be overritten with black, then the camera will be 214 // paused. 215 return true; 216 } 217 // Start the camera. 218 thread_->Clear(this, MSG_DO_PAUSE); 219 return Pause(false); 220} 221 222void VideoCapturer::SetSupportedFormats( 223 const std::vector<VideoFormat>& formats) { 224 supported_formats_ = formats; 225 UpdateFilteredSupportedFormats(); 226} 227 228bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format, 229 VideoFormat* best_format) { 230 // TODO(fbarchard): Directly support max_format. 231 UpdateFilteredSupportedFormats(); 232 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats(); 233 234 if (supported_formats->empty()) { 235 return false; 236 } 237 LOG(LS_INFO) << " Capture Requested " << format.ToString(); 238 int64 best_distance = kMaxDistance; 239 std::vector<VideoFormat>::const_iterator best = supported_formats->end(); 240 std::vector<VideoFormat>::const_iterator i; 241 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) { 242 int64 distance = GetFormatDistance(format, *i); 243 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is 244 // relatively bug free. 245 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance; 246 if (distance < best_distance) { 247 best_distance = distance; 248 best = i; 249 } 250 } 251 if (supported_formats->end() == best) { 252 LOG(LS_ERROR) << " No acceptable camera format found"; 253 return false; 254 } 255 256 if (best_format) { 257 best_format->width = best->width; 258 best_format->height = best->height; 259 best_format->fourcc = best->fourcc; 260 best_format->interval = talk_base::_max(format.interval, best->interval); 261 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval " 262 << best_format->interval << " distance " << best_distance; 263 } 264 return true; 265} 266 267void VideoCapturer::AddVideoProcessor(VideoProcessor* video_processor) { 268 talk_base::CritScope cs(&crit_); 269 ASSERT(std::find(video_processors_.begin(), video_processors_.end(), 270 video_processor) == video_processors_.end()); 271 video_processors_.push_back(video_processor); 272} 273 274bool VideoCapturer::RemoveVideoProcessor(VideoProcessor* video_processor) { 275 talk_base::CritScope cs(&crit_); 276 VideoProcessors::iterator found = std::find( 277 video_processors_.begin(), video_processors_.end(), video_processor); 278 if (found == video_processors_.end()) { 279 return false; 280 } 281 video_processors_.erase(found); 282 return true; 283} 284 285void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) { 286 max_format_.reset(new VideoFormat(max_format)); 287 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString(); 288 UpdateFilteredSupportedFormats(); 289} 290 291std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const { 292 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " "; 293 for (std::string::const_iterator i = fourcc_name.begin(); 294 i < fourcc_name.end(); ++i) { 295 // Test character is printable; Avoid isprint() which asserts on negatives. 296 if (*i < 32 || *i >= 127) { 297 fourcc_name = ""; 298 break; 299 } 300 } 301 302 std::ostringstream ss; 303 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height 304 << "x" << VideoFormat::IntervalToFps(captured_frame->elapsed_time); 305 return ss.str(); 306} 307 308void VideoCapturer::OnFrameCaptured(VideoCapturer*, 309 const CapturedFrame* captured_frame) { 310 if (muted_) { 311 if (black_frame_count_down_ == 0) { 312 thread_->Post(this, MSG_DO_PAUSE, NULL); 313 } else { 314 --black_frame_count_down_; 315 } 316 } 317 318 if (SignalVideoFrame.is_empty()) { 319 return; 320 } 321#if defined(HAVE_WEBRTC_VIDEO) 322#define VIDEO_FRAME_NAME WebRtcVideoFrame 323#endif 324#if defined(VIDEO_FRAME_NAME) 325#if !defined(DISABLE_YUV) 326 if (IsScreencast()) { 327 int scaled_width, scaled_height; 328 if (screencast_max_pixels_ > 0) { 329 ComputeScaleMaxPixels(captured_frame->width, captured_frame->height, 330 screencast_max_pixels_, &scaled_width, &scaled_height); 331 } else { 332 int desired_screencast_fps = capture_format_.get() ? 333 VideoFormat::IntervalToFps(capture_format_->interval) : 334 kDefaultScreencastFps; 335 ComputeScale(captured_frame->width, captured_frame->height, 336 desired_screencast_fps, &scaled_width, &scaled_height); 337 } 338 339 if (FOURCC_ARGB == captured_frame->fourcc && 340 (scaled_width != captured_frame->width || 341 scaled_height != captured_frame->height)) { 342 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { 343 LOG(LS_INFO) << "Scaling Screencast from " 344 << captured_frame->width << "x" 345 << captured_frame->height << " to " 346 << scaled_width << "x" << scaled_height; 347 scaled_width_ = scaled_width; 348 scaled_height_ = scaled_height; 349 } 350 CapturedFrame* modified_frame = 351 const_cast<CapturedFrame*>(captured_frame); 352 // Compute new width such that width * height is less than maximum but 353 // maintains original captured frame aspect ratio. 354 // Round down width to multiple of 4 so odd width won't round up beyond 355 // maximum, and so chroma channel is even width to simplify spatial 356 // resampling. 357 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data), 358 captured_frame->width * 4, captured_frame->width, 359 captured_frame->height, 360 reinterpret_cast<uint8*>(modified_frame->data), 361 scaled_width * 4, scaled_width, scaled_height, 362 libyuv::kFilterBilinear); 363 modified_frame->width = scaled_width; 364 modified_frame->height = scaled_height; 365 modified_frame->data_size = scaled_width * 4 * scaled_height; 366 } 367 } 368 369 const int kYuy2Bpp = 2; 370 const int kArgbBpp = 4; 371 // TODO(fbarchard): Make a helper function to adjust pixels to square. 372 // TODO(fbarchard): Hook up experiment to scaling. 373 // TODO(fbarchard): Avoid scale and convert if muted. 374 // Temporary buffer is scoped here so it will persist until i420_frame.Init() 375 // makes a copy of the frame, converting to I420. 376 talk_base::scoped_ptr<uint8[]> temp_buffer; 377 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only 378 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY. 379 bool can_scale = 380 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) || 381 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc); 382 383 // If pixels are not square, optionally use vertical scaling to make them 384 // square. Square pixels simplify the rest of the pipeline, including 385 // effects and rendering. 386 if (can_scale && square_pixel_aspect_ratio_ && 387 captured_frame->pixel_width != captured_frame->pixel_height) { 388 int scaled_width, scaled_height; 389 // modified_frame points to the captured_frame but with const casted away 390 // so it can be modified. 391 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame); 392 // Compute the frame size that makes pixels square pixel aspect ratio. 393 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height, 394 captured_frame->pixel_width, 395 captured_frame->pixel_height, 396 &scaled_width, &scaled_height); 397 398 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) { 399 LOG(LS_INFO) << "Scaling WebCam from " 400 << captured_frame->width << "x" 401 << captured_frame->height << " to " 402 << scaled_width << "x" << scaled_height 403 << " for PAR " 404 << captured_frame->pixel_width << "x" 405 << captured_frame->pixel_height; 406 scaled_width_ = scaled_width; 407 scaled_height_ = scaled_height; 408 } 409 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp; 410 uint8* temp_buffer_data; 411 // Pixels are wide and short; Increasing height. Requires temporary buffer. 412 if (scaled_height > captured_frame->height) { 413 temp_buffer.reset(new uint8[modified_frame_size]); 414 temp_buffer_data = temp_buffer.get(); 415 } else { 416 // Pixels are narrow and tall; Decreasing height. Scale will be done 417 // in place. 418 temp_buffer_data = reinterpret_cast<uint8*>(captured_frame->data); 419 } 420 421 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp. 422 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data), 423 captured_frame->width * kYuy2Bpp, // Stride for YUY2. 424 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width. 425 abs(captured_frame->height), // Height. 426 temp_buffer_data, 427 scaled_width * kYuy2Bpp, // Stride for YUY2. 428 scaled_width * kYuy2Bpp / kArgbBpp, // Width. 429 abs(scaled_height), // New height. 430 libyuv::kFilterBilinear); 431 modified_frame->width = scaled_width; 432 modified_frame->height = scaled_height; 433 modified_frame->pixel_width = 1; 434 modified_frame->pixel_height = 1; 435 modified_frame->data_size = modified_frame_size; 436 modified_frame->data = temp_buffer_data; 437 } 438#endif // !DISABLE_YUV 439 440 // Size to crop captured frame to. This adjusts the captured frames 441 // aspect ratio to match the final view aspect ratio, considering pixel 442 // aspect ratio and rotation. The final size may be scaled down by video 443 // adapter to better match ratio_w_ x ratio_h_. 444 // Note that abs() of frame height is passed in, because source may be 445 // inverted, but output will be positive. 446 int desired_width = captured_frame->width; 447 int desired_height = captured_frame->height; 448 449 // TODO(fbarchard): Improve logic to pad or crop. 450 // MJPG can crop vertically, but not horizontally. This logic disables crop. 451 // Alternatively we could pad the image with black, or implement a 2 step 452 // crop. 453 bool can_crop = true; 454 if (captured_frame->fourcc == FOURCC_MJPG) { 455 float cam_aspect = static_cast<float>(captured_frame->width) / 456 static_cast<float>(captured_frame->height); 457 float view_aspect = static_cast<float>(ratio_w_) / 458 static_cast<float>(ratio_h_); 459 can_crop = cam_aspect <= view_aspect; 460 } 461 if (can_crop && !IsScreencast()) { 462 // TODO(ronghuawu): The capturer should always produce the native 463 // resolution and the cropping should be done in downstream code. 464 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width, 465 abs(captured_frame->height), captured_frame->pixel_width, 466 captured_frame->pixel_height, captured_frame->rotation, 467 &desired_width, &desired_height); 468 } 469 470 VIDEO_FRAME_NAME i420_frame; 471 if (!i420_frame.Init(captured_frame, desired_width, desired_height)) { 472 // TODO(fbarchard): LOG more information about captured frame attributes. 473 LOG(LS_ERROR) << "Couldn't convert to I420! " 474 << "From " << ToString(captured_frame) << " To " 475 << desired_width << " x " << desired_height; 476 return; 477 } 478 if (!muted_ && !ApplyProcessors(&i420_frame)) { 479 // Processor dropped the frame. 480 return; 481 } 482 if (muted_) { 483 i420_frame.SetToBlack(); 484 } 485 SignalVideoFrame(this, &i420_frame); 486#endif // VIDEO_FRAME_NAME 487} 488 489void VideoCapturer::SetCaptureState(CaptureState state) { 490 if (state == capture_state_) { 491 // Don't trigger a state changed callback if the state hasn't changed. 492 return; 493 } 494 StateChangeParams* state_params = new StateChangeParams(state); 495 capture_state_ = state; 496 thread_->Post(this, MSG_STATE_CHANGE, state_params); 497} 498 499void VideoCapturer::OnMessage(talk_base::Message* message) { 500 switch (message->message_id) { 501 case MSG_STATE_CHANGE: { 502 talk_base::scoped_ptr<StateChangeParams> p( 503 static_cast<StateChangeParams*>(message->pdata)); 504 SignalStateChange(this, p->data()); 505 break; 506 } 507 case MSG_DO_PAUSE: { 508 Pause(true); 509 break; 510 } 511 case MSG_DO_UNPAUSE: { 512 Pause(false); 513 break; 514 } 515 default: { 516 ASSERT(false); 517 } 518 } 519} 520 521// Get the distance between the supported and desired formats. 522// Prioritization is done according to this algorithm: 523// 1) Width closeness. If not same, we prefer wider. 524// 2) Height closeness. If not same, we prefer higher. 525// 3) Framerate closeness. If not same, we prefer faster. 526// 4) Compression. If desired format has a specific fourcc, we need exact match; 527// otherwise, we use preference. 528int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired, 529 const VideoFormat& supported) { 530 int64 distance = kMaxDistance; 531 532 // Check fourcc. 533 uint32 supported_fourcc = CanonicalFourCC(supported.fourcc); 534 int64 delta_fourcc = kMaxDistance; 535 if (FOURCC_ANY == desired.fourcc) { 536 // Any fourcc is OK for the desired. Use preference to find best fourcc. 537 std::vector<uint32> preferred_fourccs; 538 if (!GetPreferredFourccs(&preferred_fourccs)) { 539 return distance; 540 } 541 542 for (size_t i = 0; i < preferred_fourccs.size(); ++i) { 543 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) { 544 delta_fourcc = i; 545#ifdef LINUX 546 // For HD avoid YU12 which is a software conversion and has 2 bugs 547 // b/7326348 b/6960899. Reenable when fixed. 548 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 || 549 supported_fourcc == FOURCC_YV12)) { 550 delta_fourcc += kYU12Penalty; 551 } 552#endif 553 break; 554 } 555 } 556 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) { 557 delta_fourcc = 0; // Need exact match. 558 } 559 560 if (kMaxDistance == delta_fourcc) { 561 // Failed to match fourcc. 562 return distance; 563 } 564 565 // Check resolution and fps. 566 int desired_width = desired.width; 567 int desired_height = desired.height; 568 int64 delta_w = supported.width - desired_width; 569 int64 supported_fps = VideoFormat::IntervalToFps(supported.interval); 570 int64 delta_fps = 571 supported_fps - VideoFormat::IntervalToFps(desired.interval); 572 // Check height of supported height compared to height we would like it to be. 573 int64 aspect_h = 574 desired_width ? supported.width * desired_height / desired_width 575 : desired_height; 576 int64 delta_h = supported.height - aspect_h; 577 578 distance = 0; 579 // Set high penalty if the supported format is lower than the desired format. 580 // 3x means we would prefer down to down to 3/4, than up to double. 581 // But we'd prefer up to double than down to 1/2. This is conservative, 582 // strongly avoiding going down in resolution, similar to 583 // the old method, but not completely ruling it out in extreme situations. 584 // It also ignores framerate, which is often very low at high resolutions. 585 // TODO(fbarchard): Improve logic to use weighted factors. 586 static const int kDownPenalty = -3; 587 if (delta_w < 0) { 588 delta_w = delta_w * kDownPenalty; 589 } 590 if (delta_h < 0) { 591 delta_h = delta_h * kDownPenalty; 592 } 593 // Require camera fps to be at least 80% of what is requested if resolution 594 // matches. 595 // Require camera fps to be at least 96% of what is requested, or higher, 596 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97 597 if (delta_fps < 0) { 598 int64 min_desirable_fps = delta_w ? 599 VideoFormat::IntervalToFps(desired.interval) * 29 / 30 : 600 VideoFormat::IntervalToFps(desired.interval) * 24 / 30; 601 delta_fps = -delta_fps; 602 if (supported_fps < min_desirable_fps) { 603 distance |= static_cast<int64>(1) << 62; 604 } else { 605 distance |= static_cast<int64>(1) << 15; 606 } 607 } 608 609 // 12 bits for width and height and 8 bits for fps and fourcc. 610 distance |= 611 (delta_w << 28) | (delta_h << 16) | (delta_fps << 8) | delta_fourcc; 612 613 return distance; 614} 615 616bool VideoCapturer::ApplyProcessors(VideoFrame* video_frame) { 617 bool drop_frame = false; 618 talk_base::CritScope cs(&crit_); 619 for (VideoProcessors::iterator iter = video_processors_.begin(); 620 iter != video_processors_.end(); ++iter) { 621 (*iter)->OnFrame(kDummyVideoSsrc, video_frame, &drop_frame); 622 if (drop_frame) { 623 return false; 624 } 625 } 626 return true; 627} 628 629void VideoCapturer::UpdateFilteredSupportedFormats() { 630 filtered_supported_formats_.clear(); 631 filtered_supported_formats_ = supported_formats_; 632 if (!max_format_) { 633 return; 634 } 635 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin(); 636 while (iter != filtered_supported_formats_.end()) { 637 if (ShouldFilterFormat(*iter)) { 638 iter = filtered_supported_formats_.erase(iter); 639 } else { 640 ++iter; 641 } 642 } 643 if (filtered_supported_formats_.empty()) { 644 // The device only captures at resolutions higher than |max_format_| this 645 // indicates that |max_format_| should be ignored as it is better to capture 646 // at too high a resolution than to not capture at all. 647 filtered_supported_formats_ = supported_formats_; 648 } 649} 650 651bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const { 652 if (!enable_camera_list_) { 653 return false; 654 } 655 return format.width > max_format_->width || 656 format.height > max_format_->height; 657} 658 659} // namespace cricket 660