2 // Copyright 2010 Google Inc.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
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.
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.
26 // Implementation file of class VideoCapturer.
28 #include "talk/media/base/videocapturer.h"
32 #if !defined(DISABLE_YUV)
33 #include "libyuv/scale_argb.h"
35 #include "talk/media/base/videoframefactory.h"
36 #include "talk/media/base/videoprocessor.h"
37 #include "webrtc/base/common.h"
38 #include "webrtc/base/logging.h"
39 #include "webrtc/base/systeminfo.h"
41 #if defined(HAVE_WEBRTC_VIDEO)
42 #include "talk/media/webrtc/webrtcvideoframe.h"
43 #include "talk/media/webrtc/webrtcvideoframefactory.h"
44 #endif // HAVE_WEBRTC_VIDEO
50 // TODO(thorcarpenter): This is a BIG hack to flush the system with black
51 // frames. Frontends should coordinate to update the video state of a muted
52 // user. When all frontends to this consider removing the black frame business.
53 const int kNumBlackFramesOnMute = 30;
55 // MessageHandler constants.
62 static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63);
64 static const int kYU12Penalty = 16; // Needs to be higher than MJPG index.
66 static const int kDefaultScreencastFps = 5;
67 typedef rtc::TypedMessageData<CaptureState> StateChangeParams;
69 // Limit stats data collections to ~20 seconds of 30fps data before dropping
70 // old data in case stats aren't reset for long periods of time.
71 static const size_t kMaxAccumulatorSize = 600;
75 /////////////////////////////////////////////////////////////////////
76 // Implementation of struct CapturedFrame
77 /////////////////////////////////////////////////////////////////////
78 CapturedFrame::CapturedFrame()
90 // TODO(fbarchard): Remove this function once lmimediaengine stops using it.
91 bool CapturedFrame::GetDataSize(uint32* size) const {
92 if (!size || data_size == CapturedFrame::kUnknownDataSize) {
99 /////////////////////////////////////////////////////////////////////
100 // Implementation of class VideoCapturer
101 /////////////////////////////////////////////////////////////////////
102 VideoCapturer::VideoCapturer()
103 : thread_(rtc::Thread::Current()),
104 adapt_frame_drops_data_(kMaxAccumulatorSize),
105 effect_frame_drops_data_(kMaxAccumulatorSize),
106 frame_time_data_(kMaxAccumulatorSize) {
110 VideoCapturer::VideoCapturer(rtc::Thread* thread)
112 adapt_frame_drops_data_(kMaxAccumulatorSize),
113 effect_frame_drops_data_(kMaxAccumulatorSize),
114 frame_time_data_(kMaxAccumulatorSize) {
118 void VideoCapturer::Construct() {
120 enable_camera_list_ = false;
121 square_pixel_aspect_ratio_ = false;
122 capture_state_ = CS_STOPPED;
123 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
126 screencast_max_pixels_ = 0;
128 black_frame_count_down_ = kNumBlackFramesOnMute;
129 enable_video_adapter_ = true;
130 adapt_frame_drops_ = 0;
131 effect_frame_drops_ = 0;
132 previous_frame_time_ = 0.0;
133 #ifdef HAVE_WEBRTC_VIDEO
134 // There are lots of video capturers out there that don't call
135 // set_frame_factory. We can either go change all of them, or we
136 // can set this default.
137 // TODO(pthatcher): Remove this hack and require the frame factory
138 // to be passed in the constructor.
139 set_frame_factory(new WebRtcVideoFrameFactory());
143 const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
144 return &filtered_supported_formats_;
147 bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
148 previous_frame_time_ = frame_length_time_reporter_.TimerNow();
149 CaptureState result = Start(capture_format);
150 const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
154 if (result == CS_RUNNING) {
155 SetCaptureState(result);
160 void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) {
161 if (ratio_w == 0 || ratio_h == 0) {
162 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: "
163 << ratio_w << "x" << ratio_h;
170 void VideoCapturer::ClearAspectRatio() {
175 // Override this to have more control of how your device is started/stopped.
176 bool VideoCapturer::Pause(bool pause) {
178 if (capture_state() == CS_PAUSED) {
181 bool is_running = capture_state() == CS_STARTING ||
182 capture_state() == CS_RUNNING;
184 LOG(LS_ERROR) << "Cannot pause a stopped camera.";
187 LOG(LS_INFO) << "Pausing a camera.";
188 rtc::scoped_ptr<VideoFormat> capture_format_when_paused(
189 capture_format_ ? new VideoFormat(*capture_format_) : NULL);
191 SetCaptureState(CS_PAUSED);
192 // If you override this function be sure to restore the capture format
193 // after calling Stop().
194 SetCaptureFormat(capture_format_when_paused.get());
196 if (capture_state() != CS_PAUSED) {
197 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused.";
200 if (!capture_format_) {
201 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera.";
205 LOG(LS_WARNING) << "Camera cannot be unpaused while muted.";
208 LOG(LS_INFO) << "Unpausing a camera.";
209 if (!Start(*capture_format_)) {
210 LOG(LS_ERROR) << "Camera failed to start when unpausing.";
217 bool VideoCapturer::Restart(const VideoFormat& capture_format) {
219 return StartCapturing(capture_format);
222 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) {
223 // The reqested format is the same; nothing to do.
228 return StartCapturing(capture_format);
231 bool VideoCapturer::MuteToBlackThenPause(bool muted) {
232 if (muted == IsMuted()) {
236 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer.";
237 muted_ = muted; // Do this before calling Pause().
239 // Reset black frame count down.
240 black_frame_count_down_ = kNumBlackFramesOnMute;
241 // Following frames will be overritten with black, then the camera will be
246 thread_->Clear(this, MSG_DO_PAUSE);
250 void VideoCapturer::SetSupportedFormats(
251 const std::vector<VideoFormat>& formats) {
252 supported_formats_ = formats;
253 UpdateFilteredSupportedFormats();
256 bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
257 VideoFormat* best_format) {
258 // TODO(fbarchard): Directly support max_format.
259 UpdateFilteredSupportedFormats();
260 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
262 if (supported_formats->empty()) {
265 LOG(LS_INFO) << " Capture Requested " << format.ToString();
266 int64 best_distance = kMaxDistance;
267 std::vector<VideoFormat>::const_iterator best = supported_formats->end();
268 std::vector<VideoFormat>::const_iterator i;
269 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
270 int64 distance = GetFormatDistance(format, *i);
271 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
272 // relatively bug free.
273 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
274 if (distance < best_distance) {
275 best_distance = distance;
279 if (supported_formats->end() == best) {
280 LOG(LS_ERROR) << " No acceptable camera format found";
285 best_format->width = best->width;
286 best_format->height = best->height;
287 best_format->fourcc = best->fourcc;
288 best_format->interval = best->interval;
289 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
290 << best_format->interval << " distance " << best_distance;
295 void VideoCapturer::AddVideoProcessor(VideoProcessor* video_processor) {
296 rtc::CritScope cs(&crit_);
297 ASSERT(std::find(video_processors_.begin(), video_processors_.end(),
298 video_processor) == video_processors_.end());
299 video_processors_.push_back(video_processor);
302 bool VideoCapturer::RemoveVideoProcessor(VideoProcessor* video_processor) {
303 rtc::CritScope cs(&crit_);
304 VideoProcessors::iterator found = std::find(
305 video_processors_.begin(), video_processors_.end(), video_processor);
306 if (found == video_processors_.end()) {
309 video_processors_.erase(found);
313 void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
314 max_format_.reset(new VideoFormat(max_format));
315 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
316 UpdateFilteredSupportedFormats();
319 std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const {
320 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " ";
321 for (std::string::const_iterator i = fourcc_name.begin();
322 i < fourcc_name.end(); ++i) {
323 // Test character is printable; Avoid isprint() which asserts on negatives.
324 if (*i < 32 || *i >= 127) {
330 std::ostringstream ss;
331 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height
332 << "x" << VideoFormat::IntervalToFpsFloat(captured_frame->elapsed_time);
336 void VideoCapturer::GetStats(VariableInfo<int>* adapt_drops_stats,
337 VariableInfo<int>* effect_drops_stats,
338 VariableInfo<double>* frame_time_stats,
339 VideoFormat* last_captured_frame_format) {
340 rtc::CritScope cs(&frame_stats_crit_);
341 GetVariableSnapshot(adapt_frame_drops_data_, adapt_drops_stats);
342 GetVariableSnapshot(effect_frame_drops_data_, effect_drops_stats);
343 GetVariableSnapshot(frame_time_data_, frame_time_stats);
344 *last_captured_frame_format = last_captured_frame_format_;
346 adapt_frame_drops_data_.Reset();
347 effect_frame_drops_data_.Reset();
348 frame_time_data_.Reset();
351 void VideoCapturer::OnFrameCaptured(VideoCapturer*,
352 const CapturedFrame* captured_frame) {
354 if (black_frame_count_down_ == 0) {
355 thread_->Post(this, MSG_DO_PAUSE, NULL);
357 --black_frame_count_down_;
361 if (SignalVideoFrame.is_empty()) {
364 #if !defined(DISABLE_YUV)
365 if (IsScreencast()) {
366 int scaled_width, scaled_height;
367 if (screencast_max_pixels_ > 0) {
368 ComputeScaleMaxPixels(captured_frame->width, captured_frame->height,
369 screencast_max_pixels_, &scaled_width, &scaled_height);
371 int desired_screencast_fps = capture_format_.get() ?
372 VideoFormat::IntervalToFps(capture_format_->interval) :
373 kDefaultScreencastFps;
374 ComputeScale(captured_frame->width, captured_frame->height,
375 desired_screencast_fps, &scaled_width, &scaled_height);
378 if (FOURCC_ARGB == captured_frame->fourcc &&
379 (scaled_width != captured_frame->width ||
380 scaled_height != captured_frame->height)) {
381 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
382 LOG(LS_INFO) << "Scaling Screencast from "
383 << captured_frame->width << "x"
384 << captured_frame->height << " to "
385 << scaled_width << "x" << scaled_height;
386 scaled_width_ = scaled_width;
387 scaled_height_ = scaled_height;
389 CapturedFrame* modified_frame =
390 const_cast<CapturedFrame*>(captured_frame);
391 // Compute new width such that width * height is less than maximum but
392 // maintains original captured frame aspect ratio.
393 // Round down width to multiple of 4 so odd width won't round up beyond
394 // maximum, and so chroma channel is even width to simplify spatial
396 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data),
397 captured_frame->width * 4, captured_frame->width,
398 captured_frame->height,
399 reinterpret_cast<uint8*>(modified_frame->data),
400 scaled_width * 4, scaled_width, scaled_height,
401 libyuv::kFilterBilinear);
402 modified_frame->width = scaled_width;
403 modified_frame->height = scaled_height;
404 modified_frame->data_size = scaled_width * 4 * scaled_height;
408 const int kYuy2Bpp = 2;
409 const int kArgbBpp = 4;
410 // TODO(fbarchard): Make a helper function to adjust pixels to square.
411 // TODO(fbarchard): Hook up experiment to scaling.
412 // TODO(fbarchard): Avoid scale and convert if muted.
413 // Temporary buffer is scoped here so it will persist until i420_frame.Init()
414 // makes a copy of the frame, converting to I420.
415 rtc::scoped_ptr<uint8[]> temp_buffer;
416 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only
417 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY.
419 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) ||
420 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc);
422 // If pixels are not square, optionally use vertical scaling to make them
423 // square. Square pixels simplify the rest of the pipeline, including
424 // effects and rendering.
425 if (can_scale && square_pixel_aspect_ratio_ &&
426 captured_frame->pixel_width != captured_frame->pixel_height) {
427 int scaled_width, scaled_height;
428 // modified_frame points to the captured_frame but with const casted away
429 // so it can be modified.
430 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame);
431 // Compute the frame size that makes pixels square pixel aspect ratio.
432 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height,
433 captured_frame->pixel_width,
434 captured_frame->pixel_height,
435 &scaled_width, &scaled_height);
437 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
438 LOG(LS_INFO) << "Scaling WebCam from "
439 << captured_frame->width << "x"
440 << captured_frame->height << " to "
441 << scaled_width << "x" << scaled_height
443 << captured_frame->pixel_width << "x"
444 << captured_frame->pixel_height;
445 scaled_width_ = scaled_width;
446 scaled_height_ = scaled_height;
448 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp;
449 uint8* temp_buffer_data;
450 // Pixels are wide and short; Increasing height. Requires temporary buffer.
451 if (scaled_height > captured_frame->height) {
452 temp_buffer.reset(new uint8[modified_frame_size]);
453 temp_buffer_data = temp_buffer.get();
455 // Pixels are narrow and tall; Decreasing height. Scale will be done
457 temp_buffer_data = reinterpret_cast<uint8*>(captured_frame->data);
460 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp.
461 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data),
462 captured_frame->width * kYuy2Bpp, // Stride for YUY2.
463 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width.
464 abs(captured_frame->height), // Height.
466 scaled_width * kYuy2Bpp, // Stride for YUY2.
467 scaled_width * kYuy2Bpp / kArgbBpp, // Width.
468 abs(scaled_height), // New height.
469 libyuv::kFilterBilinear);
470 modified_frame->width = scaled_width;
471 modified_frame->height = scaled_height;
472 modified_frame->pixel_width = 1;
473 modified_frame->pixel_height = 1;
474 modified_frame->data_size = modified_frame_size;
475 modified_frame->data = temp_buffer_data;
477 #endif // !DISABLE_YUV
479 // Size to crop captured frame to. This adjusts the captured frames
480 // aspect ratio to match the final view aspect ratio, considering pixel
481 // aspect ratio and rotation. The final size may be scaled down by video
482 // adapter to better match ratio_w_ x ratio_h_.
483 // Note that abs() of frame height is passed in, because source may be
484 // inverted, but output will be positive.
485 int desired_width = captured_frame->width;
486 int desired_height = captured_frame->height;
488 // TODO(fbarchard): Improve logic to pad or crop.
489 // MJPG can crop vertically, but not horizontally. This logic disables crop.
490 // Alternatively we could pad the image with black, or implement a 2 step
492 bool can_crop = true;
493 if (captured_frame->fourcc == FOURCC_MJPG) {
494 float cam_aspect = static_cast<float>(captured_frame->width) /
495 static_cast<float>(captured_frame->height);
496 float view_aspect = static_cast<float>(ratio_w_) /
497 static_cast<float>(ratio_h_);
498 can_crop = cam_aspect <= view_aspect;
500 if (can_crop && !IsScreencast()) {
501 // TODO(ronghuawu): The capturer should always produce the native
502 // resolution and the cropping should be done in downstream code.
503 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width,
504 abs(captured_frame->height), captured_frame->pixel_width,
505 captured_frame->pixel_height, captured_frame->rotation,
506 &desired_width, &desired_height);
509 if (!frame_factory_) {
510 LOG(LS_ERROR) << "No video frame factory.";
514 rtc::scoped_ptr<VideoFrame> i420_frame(
515 frame_factory_->CreateAliasedFrame(
516 captured_frame, desired_width, desired_height));
518 // TODO(fbarchard): LOG more information about captured frame attributes.
519 LOG(LS_ERROR) << "Couldn't convert to I420! "
520 << "From " << ToString(captured_frame) << " To "
521 << desired_width << " x " << desired_height;
525 VideoFrame* adapted_frame = i420_frame.get();
526 if (enable_video_adapter_ && !IsScreencast()) {
527 VideoFrame* out_frame = NULL;
528 video_adapter_.AdaptFrame(adapted_frame, &out_frame);
530 // VideoAdapter dropped the frame.
531 ++adapt_frame_drops_;
534 adapted_frame = out_frame;
537 if (!muted_ && !ApplyProcessors(adapted_frame)) {
538 // Processor dropped the frame.
539 ++effect_frame_drops_;
543 // TODO(pthatcher): Use frame_factory_->CreateBlackFrame() instead.
544 adapted_frame->SetToBlack();
546 SignalVideoFrame(this, adapted_frame);
548 UpdateStats(captured_frame);
551 void VideoCapturer::SetCaptureState(CaptureState state) {
552 if (state == capture_state_) {
553 // Don't trigger a state changed callback if the state hasn't changed.
556 StateChangeParams* state_params = new StateChangeParams(state);
557 capture_state_ = state;
558 thread_->Post(this, MSG_STATE_CHANGE, state_params);
561 void VideoCapturer::OnMessage(rtc::Message* message) {
562 switch (message->message_id) {
563 case MSG_STATE_CHANGE: {
564 rtc::scoped_ptr<StateChangeParams> p(
565 static_cast<StateChangeParams*>(message->pdata));
566 SignalStateChange(this, p->data());
573 case MSG_DO_UNPAUSE: {
583 // Get the distance between the supported and desired formats.
584 // Prioritization is done according to this algorithm:
585 // 1) Width closeness. If not same, we prefer wider.
586 // 2) Height closeness. If not same, we prefer higher.
587 // 3) Framerate closeness. If not same, we prefer faster.
588 // 4) Compression. If desired format has a specific fourcc, we need exact match;
589 // otherwise, we use preference.
590 int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired,
591 const VideoFormat& supported) {
592 int64 distance = kMaxDistance;
595 uint32 supported_fourcc = CanonicalFourCC(supported.fourcc);
596 int64 delta_fourcc = kMaxDistance;
597 if (FOURCC_ANY == desired.fourcc) {
598 // Any fourcc is OK for the desired. Use preference to find best fourcc.
599 std::vector<uint32> preferred_fourccs;
600 if (!GetPreferredFourccs(&preferred_fourccs)) {
604 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
605 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
608 // For HD avoid YU12 which is a software conversion and has 2 bugs
609 // b/7326348 b/6960899. Reenable when fixed.
610 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
611 supported_fourcc == FOURCC_YV12)) {
612 delta_fourcc += kYU12Penalty;
618 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
619 delta_fourcc = 0; // Need exact match.
622 if (kMaxDistance == delta_fourcc) {
623 // Failed to match fourcc.
627 // Check resolution and fps.
628 int desired_width = desired.width;
629 int desired_height = desired.height;
630 int64 delta_w = supported.width - desired_width;
631 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
633 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
634 // Check height of supported height compared to height we would like it to be.
636 desired_width ? supported.width * desired_height / desired_width
638 int64 delta_h = supported.height - aspect_h;
641 // Set high penalty if the supported format is lower than the desired format.
642 // 3x means we would prefer down to down to 3/4, than up to double.
643 // But we'd prefer up to double than down to 1/2. This is conservative,
644 // strongly avoiding going down in resolution, similar to
645 // the old method, but not completely ruling it out in extreme situations.
646 // It also ignores framerate, which is often very low at high resolutions.
647 // TODO(fbarchard): Improve logic to use weighted factors.
648 static const int kDownPenalty = -3;
650 delta_w = delta_w * kDownPenalty;
653 delta_h = delta_h * kDownPenalty;
655 // Require camera fps to be at least 80% of what is requested if resolution
657 // Require camera fps to be at least 96% of what is requested, or higher,
658 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
660 float min_desirable_fps = delta_w ?
661 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
662 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
663 delta_fps = -delta_fps;
664 if (supported_fps < min_desirable_fps) {
665 distance |= static_cast<int64>(1) << 62;
667 distance |= static_cast<int64>(1) << 15;
670 int64 idelta_fps = static_cast<int>(delta_fps);
672 // 12 bits for width and height and 8 bits for fps and fourcc.
674 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
679 bool VideoCapturer::ApplyProcessors(VideoFrame* video_frame) {
680 bool drop_frame = false;
681 rtc::CritScope cs(&crit_);
682 for (VideoProcessors::iterator iter = video_processors_.begin();
683 iter != video_processors_.end(); ++iter) {
684 (*iter)->OnFrame(kDummyVideoSsrc, video_frame, &drop_frame);
692 void VideoCapturer::UpdateFilteredSupportedFormats() {
693 filtered_supported_formats_.clear();
694 filtered_supported_formats_ = supported_formats_;
698 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
699 while (iter != filtered_supported_formats_.end()) {
700 if (ShouldFilterFormat(*iter)) {
701 iter = filtered_supported_formats_.erase(iter);
706 if (filtered_supported_formats_.empty()) {
707 // The device only captures at resolutions higher than |max_format_| this
708 // indicates that |max_format_| should be ignored as it is better to capture
709 // at too high a resolution than to not capture at all.
710 filtered_supported_formats_ = supported_formats_;
714 bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
715 if (!enable_camera_list_) {
718 return format.width > max_format_->width ||
719 format.height > max_format_->height;
722 void VideoCapturer::UpdateStats(const CapturedFrame* captured_frame) {
723 // Update stats protected from fetches from different thread.
724 rtc::CritScope cs(&frame_stats_crit_);
726 last_captured_frame_format_.width = captured_frame->width;
727 last_captured_frame_format_.height = captured_frame->height;
728 // TODO(ronghuawu): Useful to report interval as well?
729 last_captured_frame_format_.interval = 0;
730 last_captured_frame_format_.fourcc = captured_frame->fourcc;
732 double time_now = frame_length_time_reporter_.TimerNow();
733 if (previous_frame_time_ != 0.0) {
734 adapt_frame_drops_data_.AddSample(adapt_frame_drops_);
735 effect_frame_drops_data_.AddSample(effect_frame_drops_);
736 frame_time_data_.AddSample(time_now - previous_frame_time_);
738 previous_frame_time_ = time_now;
739 effect_frame_drops_ = 0;
740 adapt_frame_drops_ = 0;
744 void VideoCapturer::GetVariableSnapshot(
745 const rtc::RollingAccumulator<T>& data,
746 VariableInfo<T>* stats) {
747 stats->max_val = data.ComputeMax();
748 stats->mean = data.ComputeMean();
749 stats->min_val = data.ComputeMin();
750 stats->variance = data.ComputeVariance();
753 } // namespace cricket