1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
// Copyright (c) 2013 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 "base/debug/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;
} // anonymous namespace
VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta capture_period,
bool events_are_reliable)
: capture_period_(capture_period),
frame_number_(0),
last_delivered_frame_number_(0),
sampler_(capture_period_,
events_are_reliable,
kNumRedundantCapturesOfStaticContent) {}
bool VideoCaptureOracle::ObserveEventAndDecideCapture(
Event event,
base::TimeTicks event_time) {
// Record |event| and decide whether it's a good time to capture.
const bool content_is_dirty = (event == kCompositorUpdate ||
event == kSoftwarePaint);
bool should_sample;
if (content_is_dirty) {
frame_number_++;
should_sample = sampler_.AddEventAndConsiderSampling(event_time);
} else {
should_sample = sampler_.IsOverdueForSamplingAt(event_time);
}
return should_sample;
}
int VideoCaptureOracle::RecordCapture() {
sampler_.RecordSample();
return frame_number_;
}
bool VideoCaptureOracle::CompleteCapture(int frame_number,
base::TimeTicks timestamp) {
// Drop frame if previous frame number is higher or we're trying to deliver
// a frame with the same timestamp.
if (last_delivered_frame_number_ > frame_number ||
last_delivered_frame_timestamp_ == timestamp) {
LOG(ERROR) << "Frame with same timestamp or out of order delivery. "
<< "Dropping frame.";
return false;
}
if (last_delivered_frame_timestamp_ > timestamp) {
// We should not get here unless time was adjusted backwards.
LOG(ERROR) << "Frame with past timestamp (" << timestamp.ToInternalValue()
<< ") was delivered";
}
last_delivered_frame_number_ = frame_number;
last_delivered_frame_timestamp_ = timestamp;
return true;
}
SmoothEventSampler::SmoothEventSampler(base::TimeDelta capture_period,
bool events_are_reliable,
int redundant_capture_goal)
: events_are_reliable_(events_are_reliable),
capture_period_(capture_period),
redundant_capture_goal_(redundant_capture_goal),
token_bucket_capacity_(capture_period + capture_period / 2),
overdue_sample_count_(0),
token_bucket_(token_bucket_capacity_) {
DCHECK_GT(capture_period_.InMicroseconds(), 0);
}
bool SmoothEventSampler::AddEventAndConsiderSampling(
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_;
}
// Side note: If the system clock is reset, causing |current_event_| to be
// greater than |event_time|, everything here will simply gracefully adjust.
if (token_bucket_ < base::TimeDelta())
token_bucket_ = base::TimeDelta();
TRACE_COUNTER1("mirroring",
"MirroringTokenBucketUsec",
std::max<int64>(0, token_bucket_.InMicroseconds()));
}
current_event_ = event_time;
// Return true if one capture period's worth of tokens are in the bucket.
return token_bucket_ >= capture_period_;
}
void SmoothEventSampler::RecordSample() {
token_bucket_ -= capture_period_;
TRACE_COUNTER1("mirroring",
"MirroringTokenBucketUsec",
std::max<int64>(0, token_bucket_.InMicroseconds()));
bool was_paused = overdue_sample_count_ == redundant_capture_goal_;
if (HasUnrecordedEvent()) {
last_sample_ = current_event_;
overdue_sample_count_ = 0;
} else {
++overdue_sample_count_;
}
bool is_paused = overdue_sample_count_ == redundant_capture_goal_;
VLOG_IF(0, !was_paused && is_paused)
<< "Tab content unchanged for " << redundant_capture_goal_
<< " frames; capture will halt until content changes.";
VLOG_IF(0, was_paused && !is_paused)
<< "Content changed; capture will resume.";
}
bool SmoothEventSampler::IsOverdueForSamplingAt(base::TimeTicks event_time)
const {
DCHECK(!event_time.is_null());
// If we don't get events on compositor updates on this platform, then we
// don't reliably know whether we're dirty.
if (events_are_reliable_) {
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_;
if (dirty_interval < capture_period_ * 4)
return false;
else
return true;
}
bool SmoothEventSampler::HasUnrecordedEvent() const {
return !current_event_.is_null() && current_event_ != last_sample_;
}
} // namespace content
|