Index: content/browser/media/capture/animated_content_sampler.cc |
diff --git a/content/browser/media/capture/video_capture_oracle.cc b/content/browser/media/capture/animated_content_sampler.cc |
similarity index 52% |
copy from content/browser/media/capture/video_capture_oracle.cc |
copy to content/browser/media/capture/animated_content_sampler.cc |
index 3cddc38b0c2fa33360901c8d7b046690eacf7d4e..a30364b0e97d215acfc836442698112180b452a0 100644 |
--- a/content/browser/media/capture/video_capture_oracle.cc |
+++ b/content/browser/media/capture/animated_content_sampler.cc |
@@ -1,30 +1,15 @@ |
-// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2015 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "content/browser/media/capture/video_capture_oracle.h" |
+#include "content/browser/media/capture/animated_content_sampler.h" |
#include <algorithm> |
-#include "base/format_macros.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/trace_event/trace_event.h" |
- |
namespace content { |
namespace { |
-// This value controls how many redundant, timer-base captures occur when the |
-// content is static. Redundantly capturing the same frame allows iterative |
-// quality enhancement, and also allows the buffer to fill in "buffered mode". |
-// |
-// TODO(nick): Controlling this here is a hack and a layering violation, since |
-// it's a strategy specific to the WebRTC consumer, and probably just papers |
-// over some frame dropping and quality bugs. It should either be controlled at |
-// a higher level, or else redundant frame generation should be pushed down |
-// further into the WebRTC encoding stack. |
-const int kNumRedundantCapturesOfStaticContent = 200; |
- |
// These specify the minimum/maximum amount of recent event history to examine |
// to detect animated content. If the values are too low, there is a greater |
// risk of false-positive detections and low accuracy. If they are too high, |
@@ -52,199 +37,8 @@ const int kMaxLockInPeriodMicros = 83333; // 12 FPS |
// value, the higher the variance in frame timestamps. |
const int kDriftCorrectionMillis = 2000; |
-// Given the amount of time between frames, compare to the expected amount of |
-// time between frames at |frame_rate| and return the fractional difference. |
-double FractionFromExpectedFrameRate(base::TimeDelta delta, int frame_rate) { |
- DCHECK_GT(frame_rate, 0); |
- const base::TimeDelta expected_delta = |
- base::TimeDelta::FromSeconds(1) / frame_rate; |
- return (delta - expected_delta).InMillisecondsF() / |
- expected_delta.InMillisecondsF(); |
-} |
- |
} // anonymous namespace |
-VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta min_capture_period) |
- : frame_number_(0), |
- last_delivered_frame_number_(-1), |
- smoothing_sampler_(min_capture_period, |
- kNumRedundantCapturesOfStaticContent), |
- content_sampler_(min_capture_period) { |
-} |
- |
-VideoCaptureOracle::~VideoCaptureOracle() {} |
- |
-bool VideoCaptureOracle::ObserveEventAndDecideCapture( |
- Event event, |
- const gfx::Rect& damage_rect, |
- base::TimeTicks event_time) { |
- DCHECK_GE(event, 0); |
- DCHECK_LT(event, kNumEvents); |
- if (event_time < last_event_time_[event]) { |
- LOG(WARNING) << "Event time is not monotonically non-decreasing. " |
- << "Deciding not to capture this frame."; |
- return false; |
- } |
- last_event_time_[event] = event_time; |
- |
- bool should_sample; |
- switch (event) { |
- case kCompositorUpdate: |
- smoothing_sampler_.ConsiderPresentationEvent(event_time); |
- content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); |
- if (content_sampler_.HasProposal()) { |
- should_sample = content_sampler_.ShouldSample(); |
- if (should_sample) |
- event_time = content_sampler_.frame_timestamp(); |
- } else { |
- should_sample = smoothing_sampler_.ShouldSample(); |
- } |
- break; |
- default: |
- should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); |
- break; |
- } |
- |
- SetFrameTimestamp(frame_number_, event_time); |
- return should_sample; |
-} |
- |
-int VideoCaptureOracle::RecordCapture() { |
- smoothing_sampler_.RecordSample(); |
- content_sampler_.RecordSample(GetFrameTimestamp(frame_number_)); |
- return frame_number_++; |
-} |
- |
-bool VideoCaptureOracle::CompleteCapture(int frame_number, |
- base::TimeTicks* frame_timestamp) { |
- // Drop frame if previous frame number is higher. |
- if (last_delivered_frame_number_ > frame_number) { |
- LOG(WARNING) << "Out of order frame delivery detected (have #" |
- << frame_number << ", last was #" |
- << last_delivered_frame_number_ << "). Dropping frame."; |
- return false; |
- } |
- last_delivered_frame_number_ = frame_number; |
- |
- *frame_timestamp = GetFrameTimestamp(frame_number); |
- |
- // If enabled, log a measurement of how this frame timestamp has incremented |
- // in relation to an ideal increment. |
- if (VLOG_IS_ON(2) && frame_number > 0) { |
- const base::TimeDelta delta = |
- *frame_timestamp - GetFrameTimestamp(frame_number - 1); |
- if (content_sampler_.HasProposal()) { |
- const double estimated_frame_rate = |
- 1000000.0 / content_sampler_.detected_period().InMicroseconds(); |
- const int rounded_frame_rate = |
- static_cast<int>(estimated_frame_rate + 0.5); |
- VLOG(2) << base::StringPrintf( |
- "Captured #%d: delta=%" PRId64 " usec" |
- ", now locked into {%s}, %+0.1f%% slower than %d FPS", |
- frame_number, |
- delta.InMicroseconds(), |
- content_sampler_.detected_region().ToString().c_str(), |
- 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate), |
- rounded_frame_rate); |
- } else { |
- VLOG(2) << base::StringPrintf( |
- "Captured #%d: delta=%" PRId64 " usec" |
- ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%", |
- frame_number, |
- delta.InMicroseconds(), |
- 100.0 * FractionFromExpectedFrameRate(delta, 30), |
- 100.0 * FractionFromExpectedFrameRate(delta, 25), |
- 100.0 * FractionFromExpectedFrameRate(delta, 24)); |
- } |
- } |
- |
- return !frame_timestamp->is_null(); |
-} |
- |
-base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { |
- DCHECK_LE(frame_number, frame_number_); |
- DCHECK_LT(frame_number_ - frame_number, kMaxFrameTimestamps); |
- return frame_timestamps_[frame_number % kMaxFrameTimestamps]; |
-} |
- |
-void VideoCaptureOracle::SetFrameTimestamp(int frame_number, |
- base::TimeTicks timestamp) { |
- frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; |
-} |
- |
-SmoothEventSampler::SmoothEventSampler(base::TimeDelta min_capture_period, |
- int redundant_capture_goal) |
- : min_capture_period_(min_capture_period), |
- redundant_capture_goal_(redundant_capture_goal), |
- token_bucket_capacity_(min_capture_period + min_capture_period / 2), |
- overdue_sample_count_(0), |
- token_bucket_(token_bucket_capacity_) { |
- DCHECK_GT(min_capture_period_.InMicroseconds(), 0); |
-} |
- |
-void SmoothEventSampler::ConsiderPresentationEvent(base::TimeTicks event_time) { |
- DCHECK(!event_time.is_null()); |
- |
- // Add tokens to the bucket based on advancement in time. Then, re-bound the |
- // number of tokens in the bucket. Overflow occurs when there is too much |
- // time between events (a common case), or when RecordSample() is not being |
- // called often enough (a bug). On the other hand, if RecordSample() is being |
- // called too often (e.g., as a reaction to IsOverdueForSamplingAt()), the |
- // bucket will underflow. |
- if (!current_event_.is_null()) { |
- if (current_event_ < event_time) { |
- token_bucket_ += event_time - current_event_; |
- if (token_bucket_ > token_bucket_capacity_) |
- token_bucket_ = token_bucket_capacity_; |
- } |
- TRACE_COUNTER1("gpu.capture", |
- "MirroringTokenBucketUsec", |
- std::max<int64>(0, token_bucket_.InMicroseconds())); |
- } |
- current_event_ = event_time; |
-} |
- |
-bool SmoothEventSampler::ShouldSample() const { |
- return token_bucket_ >= min_capture_period_; |
-} |
- |
-void SmoothEventSampler::RecordSample() { |
- token_bucket_ -= min_capture_period_; |
- if (token_bucket_ < base::TimeDelta()) |
- token_bucket_ = base::TimeDelta(); |
- TRACE_COUNTER1("gpu.capture", |
- "MirroringTokenBucketUsec", |
- std::max<int64>(0, token_bucket_.InMicroseconds())); |
- |
- if (HasUnrecordedEvent()) { |
- last_sample_ = current_event_; |
- overdue_sample_count_ = 0; |
- } else { |
- ++overdue_sample_count_; |
- } |
-} |
- |
-bool SmoothEventSampler::IsOverdueForSamplingAt(base::TimeTicks event_time) |
- const { |
- DCHECK(!event_time.is_null()); |
- |
- if (!HasUnrecordedEvent() && overdue_sample_count_ >= redundant_capture_goal_) |
- return false; // Not dirty. |
- |
- if (last_sample_.is_null()) |
- return true; |
- |
- // If we're dirty but not yet old, then we've recently gotten updates, so we |
- // won't request a sample just yet. |
- base::TimeDelta dirty_interval = event_time - last_sample_; |
- return dirty_interval >= |
- base::TimeDelta::FromMilliseconds(kNonAnimatingThresholdMillis); |
-} |
- |
-bool SmoothEventSampler::HasUnrecordedEvent() const { |
- return !current_event_.is_null() && current_event_ != last_sample_; |
-} |
- |
AnimatedContentSampler::AnimatedContentSampler( |
base::TimeDelta min_capture_period) |
: min_capture_period_(min_capture_period) {} |