OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/media/capture/video_capture_oracle.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/format_macros.h" | |
10 #include "base/strings/stringprintf.h" | |
11 | |
12 namespace content { | |
13 | |
14 namespace { | |
15 | |
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". | |
19 // | |
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; | |
26 | |
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(); | |
35 } | |
36 | |
37 } // anonymous namespace | |
38 | |
39 VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta min_capture_period) | |
40 : next_frame_number_(0), | |
41 last_delivered_frame_number_(-1), | |
42 smoothing_sampler_(min_capture_period, | |
43 kNumRedundantCapturesOfStaticContent), | |
44 content_sampler_(min_capture_period) { | |
45 } | |
46 | |
47 VideoCaptureOracle::~VideoCaptureOracle() {} | |
48 | |
49 bool VideoCaptureOracle::ObserveEventAndDecideCapture( | |
50 Event event, | |
51 const gfx::Rect& damage_rect, | |
52 base::TimeTicks event_time) { | |
53 DCHECK_GE(event, 0); | |
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."; | |
58 return false; | |
59 } | |
60 last_event_time_[event] = event_time; | |
61 | |
62 bool should_sample = false; | |
63 duration_of_next_frame_ = base::TimeDelta(); | |
64 switch (event) { | |
65 case kCompositorUpdate: | |
66 smoothing_sampler_.ConsiderPresentationEvent(event_time); | |
67 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); | |
68 if (content_sampler_.HasProposal()) { | |
69 should_sample = content_sampler_.ShouldSample(); | |
70 if (should_sample) { | |
71 event_time = content_sampler_.frame_timestamp(); | |
72 duration_of_next_frame_ = content_sampler_.sampling_period(); | |
73 } | |
74 } else { | |
75 should_sample = smoothing_sampler_.ShouldSample(); | |
76 if (should_sample) | |
77 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); | |
78 } | |
79 break; | |
80 case kTimerPoll: | |
81 // While the timer is firing, only allow a sampling if there are none | |
82 // currently in-progress. | |
83 if (last_delivered_frame_number_ == (next_frame_number_ - 1)) { | |
84 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); | |
85 if (should_sample) | |
86 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); | |
87 } | |
88 break; | |
89 case kNumEvents: | |
90 NOTREACHED(); | |
91 break; | |
92 } | |
93 | |
94 SetFrameTimestamp(next_frame_number_, event_time); | |
95 return should_sample; | |
96 } | |
97 | |
98 int VideoCaptureOracle::RecordCapture() { | |
99 smoothing_sampler_.RecordSample(); | |
100 content_sampler_.RecordSample(GetFrameTimestamp(next_frame_number_)); | |
101 return next_frame_number_++; | |
102 } | |
103 | |
104 bool VideoCaptureOracle::CompleteCapture(int frame_number, | |
105 bool capture_was_successful, | |
106 base::TimeTicks* frame_timestamp) { | |
107 // Drop frame if previous frame number is higher. | |
108 if (last_delivered_frame_number_ > frame_number) { | |
109 LOG_IF(WARNING, capture_was_successful) | |
110 << "Out of order frame delivery detected (have #" << frame_number | |
111 << ", last was #" << last_delivered_frame_number_ | |
112 << "). Dropping frame."; | |
113 return false; | |
114 } | |
115 DCHECK_NE(last_delivered_frame_number_, frame_number); | |
116 last_delivered_frame_number_ = frame_number; | |
117 | |
118 if (!capture_was_successful) { | |
119 VLOG(2) << "Capture of frame #" << frame_number << " was not successful."; | |
120 return false; | |
121 } | |
122 | |
123 *frame_timestamp = GetFrameTimestamp(frame_number); | |
124 | |
125 // If enabled, log a measurement of how this frame timestamp has incremented | |
126 // in relation to an ideal increment. | |
127 if (VLOG_IS_ON(2) && frame_number > 0) { | |
128 const base::TimeDelta delta = | |
129 *frame_timestamp - GetFrameTimestamp(frame_number - 1); | |
130 if (content_sampler_.HasProposal()) { | |
131 const double estimated_frame_rate = | |
132 1000000.0 / content_sampler_.detected_period().InMicroseconds(); | |
133 const int rounded_frame_rate = | |
134 static_cast<int>(estimated_frame_rate + 0.5); | |
135 VLOG(2) << base::StringPrintf( | |
136 "Captured #%d: delta=%" PRId64 " usec" | |
137 ", now locked into {%s}, %+0.1f%% slower than %d FPS", | |
138 frame_number, | |
139 delta.InMicroseconds(), | |
140 content_sampler_.detected_region().ToString().c_str(), | |
141 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate), | |
142 rounded_frame_rate); | |
143 } else { | |
144 VLOG(2) << base::StringPrintf( | |
145 "Captured #%d: delta=%" PRId64 " usec" | |
146 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%", | |
147 frame_number, | |
148 delta.InMicroseconds(), | |
149 100.0 * FractionFromExpectedFrameRate(delta, 30), | |
150 100.0 * FractionFromExpectedFrameRate(delta, 25), | |
151 100.0 * FractionFromExpectedFrameRate(delta, 24)); | |
152 } | |
153 } | |
154 | |
155 return !frame_timestamp->is_null(); | |
156 } | |
157 | |
158 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { | |
159 DCHECK_LE(frame_number, next_frame_number_); | |
160 DCHECK_LT(next_frame_number_ - frame_number, kMaxFrameTimestamps); | |
161 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; | |
162 } | |
163 | |
164 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, | |
165 base::TimeTicks timestamp) { | |
166 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; | |
167 } | |
168 | |
169 } // namespace content | |
OLD | NEW |