1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/capture/video_capture_oracle.h"
9 #include "base/format_macros.h"
10 #include "base/strings/stringprintf.h"
16 // This value controls how many redundant, timer-base captures occur when the
17 // content is static. Redundantly capturing the same frame allows iterative
18 // quality enhancement, and also allows the buffer to fill in "buffered mode".
20 // TODO(nick): Controlling this here is a hack and a layering violation, since
21 // it's a strategy specific to the WebRTC consumer, and probably just papers
22 // over some frame dropping and quality bugs. It should either be controlled at
23 // a higher level, or else redundant frame generation should be pushed down
24 // further into the WebRTC encoding stack.
25 const int kNumRedundantCapturesOfStaticContent
= 200;
27 // Given the amount of time between frames, compare to the expected amount of
28 // time between frames at |frame_rate| and return the fractional difference.
29 double FractionFromExpectedFrameRate(base::TimeDelta delta
, int frame_rate
) {
30 DCHECK_GT(frame_rate
, 0);
31 const base::TimeDelta expected_delta
=
32 base::TimeDelta::FromSeconds(1) / frame_rate
;
33 return (delta
- expected_delta
).InMillisecondsF() /
34 expected_delta
.InMillisecondsF();
37 } // anonymous namespace
39 VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta min_capture_period
)
41 last_delivered_frame_number_(-1),
42 smoothing_sampler_(min_capture_period
,
43 kNumRedundantCapturesOfStaticContent
),
44 content_sampler_(min_capture_period
) {
47 VideoCaptureOracle::~VideoCaptureOracle() {}
49 bool VideoCaptureOracle::ObserveEventAndDecideCapture(
51 const gfx::Rect
& damage_rect
,
52 base::TimeTicks event_time
) {
54 DCHECK_LT(event
, kNumEvents
);
55 if (event_time
< last_event_time_
[event
]) {
56 LOG(WARNING
) << "Event time is not monotonically non-decreasing. "
57 << "Deciding not to capture this frame.";
60 last_event_time_
[event
] = event_time
;
64 case kCompositorUpdate
:
65 smoothing_sampler_
.ConsiderPresentationEvent(event_time
);
66 content_sampler_
.ConsiderPresentationEvent(damage_rect
, event_time
);
67 if (content_sampler_
.HasProposal()) {
68 should_sample
= content_sampler_
.ShouldSample();
70 event_time
= content_sampler_
.frame_timestamp();
72 should_sample
= smoothing_sampler_
.ShouldSample();
76 should_sample
= smoothing_sampler_
.IsOverdueForSamplingAt(event_time
);
80 SetFrameTimestamp(frame_number_
, event_time
);
84 int VideoCaptureOracle::RecordCapture() {
85 smoothing_sampler_
.RecordSample();
86 content_sampler_
.RecordSample(GetFrameTimestamp(frame_number_
));
87 return frame_number_
++;
90 bool VideoCaptureOracle::CompleteCapture(int frame_number
,
91 base::TimeTicks
* frame_timestamp
) {
92 // Drop frame if previous frame number is higher.
93 if (last_delivered_frame_number_
> frame_number
) {
94 LOG(WARNING
) << "Out of order frame delivery detected (have #"
95 << frame_number
<< ", last was #"
96 << last_delivered_frame_number_
<< "). Dropping frame.";
99 last_delivered_frame_number_
= frame_number
;
101 *frame_timestamp
= GetFrameTimestamp(frame_number
);
103 // If enabled, log a measurement of how this frame timestamp has incremented
104 // in relation to an ideal increment.
105 if (VLOG_IS_ON(2) && frame_number
> 0) {
106 const base::TimeDelta delta
=
107 *frame_timestamp
- GetFrameTimestamp(frame_number
- 1);
108 if (content_sampler_
.HasProposal()) {
109 const double estimated_frame_rate
=
110 1000000.0 / content_sampler_
.detected_period().InMicroseconds();
111 const int rounded_frame_rate
=
112 static_cast<int>(estimated_frame_rate
+ 0.5);
113 VLOG(2) << base::StringPrintf(
114 "Captured #%d: delta=%" PRId64
" usec"
115 ", now locked into {%s}, %+0.1f%% slower than %d FPS",
117 delta
.InMicroseconds(),
118 content_sampler_
.detected_region().ToString().c_str(),
119 100.0 * FractionFromExpectedFrameRate(delta
, rounded_frame_rate
),
122 VLOG(2) << base::StringPrintf(
123 "Captured #%d: delta=%" PRId64
" usec"
124 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%",
126 delta
.InMicroseconds(),
127 100.0 * FractionFromExpectedFrameRate(delta
, 30),
128 100.0 * FractionFromExpectedFrameRate(delta
, 25),
129 100.0 * FractionFromExpectedFrameRate(delta
, 24));
133 return !frame_timestamp
->is_null();
136 base::TimeTicks
VideoCaptureOracle::GetFrameTimestamp(int frame_number
) const {
137 DCHECK_LE(frame_number
, frame_number_
);
138 DCHECK_LT(frame_number_
- frame_number
, kMaxFrameTimestamps
);
139 return frame_timestamps_
[frame_number
% kMaxFrameTimestamps
];
142 void VideoCaptureOracle::SetFrameTimestamp(int frame_number
,
143 base::TimeTicks timestamp
) {
144 frame_timestamps_
[frame_number
% kMaxFrameTimestamps
] = timestamp
;
147 } // namespace content