Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(401)

Side by Side Diff: services/media/common/rate_control_base.cc

Issue 1986303002: Motown: Use new TimelineTransform and related definitions (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Fixes per feedback. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "base/debug/stack_trace.h"
6 #include "base/logging.h"
7 #include "mojo/services/media/common/cpp/local_time.h"
8 #include "services/media/common/rate_control_base.h"
9
10 namespace mojo {
11 namespace media {
12
13 static inline int64_t LocalTimeNow() {
14 return LocalClock::now().time_since_epoch().count();
15 }
16
17 RateControlBase::RateControlBase()
18 : binding_(this)
19 , current_transform_(0, 1) {
20 }
21
22 RateControlBase::~RateControlBase() {
23 Reset();
24 }
25
26 bool RateControlBase::Bind(InterfaceRequest<RateControl> request) {
27 Reset();
28
29 binding_.Bind(request.Pass());
30 binding_.set_connection_error_handler([this]() -> void {
31 Reset();
32 });
33
34 return true;
35 }
36
37 void RateControlBase::SnapshotCurrentTransform(LinearTransform* out,
38 uint32_t* generation) {
39 DCHECK(out);
40 base::AutoLock lock(transform_lock_);
41 ApplyPendingChangesLocked(LocalTimeNow());
42 *out = current_transform_;
43 if (generation) {
44 *generation = generation_;
45 }
46 }
47
48 void RateControlBase::GetCurrentTransform(
49 const GetCurrentTransformCallback& cbk) {
50 TimelineTransformPtr ret(TimelineTransform::New());
51 ret->quad = TimelineQuad::New();
52
53 LinearTransform trans;
54 SnapshotCurrentTransform(&trans);
55 ret->quad->target_offset = trans.a_zero;
56 ret->quad->reference_offset = trans.b_zero;
57 ret->quad->target_delta = trans.scale.denominator;
58 ret->quad->reference_delta = trans.scale.numerator;
59 ret->reference_timeline_id = TimelineTransform::kContextual;
60 ret->target_timeline_id = TimelineTransform::kLocalTimeID;
61
62 cbk.Run(ret.Pass());
63 }
64
65 // TODO(johngro): implement or remove. Until we have the ability to query the
66 // clock in the target timeline (or at least, transform local time to the target
67 // timeline), we have no way to apply scheduled changes.
68 void RateControlBase::SetTargetTimelineID(uint32_t id) {
69 if (id != TimelineTransform::kLocalTimeID) {
70 LOG(ERROR) << "Unsupported target timeline id ("
71 << id << ") during SetTargetTimelineID";
72 Reset();
73 }
74 }
75
76 void RateControlBase::SetCurrentQuad(TimelineQuadPtr quad) {
77 // A target delta of zero means that the transformation from the target
78 // timeline to the media timeline is singular. This is not permitted, log an
79 // error and close the connection if someone attempts to do this.
80 if (!quad->target_delta) {
81 OnIllegalRateChange(quad->reference_delta, quad->target_delta);
82 return;
83 } else {
84 base::AutoLock lock(transform_lock_);
85
86 reference_pending_changes_.clear();
87 target_pending_changes_.clear();
88
89 current_transform_.a_zero = quad->target_offset;
90 current_transform_.b_zero = quad->reference_offset;
91
92 if (quad->reference_delta) {
93 current_transform_.scale =
94 LinearTransform::Ratio(quad->reference_delta, quad->target_delta);
95 } else {
96 current_transform_.scale.numerator = 0;
97 current_transform_.scale.denominator = 1;
98 }
99
100 AdvanceGenerationLocked();
101 }
102 }
103
104 void RateControlBase::SetRate(uint32_t reference_delta, uint32_t target_delta) {
105 // Only rate changes with a non-zero target_delta are permitted. See comment
106 // in SetCurrentQuad.
107 if (!target_delta) {
108 OnIllegalRateChange(reference_delta, target_delta);
109 return;
110 } else {
111 base::AutoLock lock(transform_lock_);
112
113 // Make sure we are up to date.
114 int64_t target_now = LocalTimeNow();
115 ApplyPendingChangesLocked(target_now);
116
117 DCHECK(current_transform_.scale.denominator);
118 int64_t reference_now;
119 if (!current_transform_.DoForwardTransform(target_now, &reference_now)) {
120 // TODO(johngro): we cannot apply this transformation because of
121 // overflow, so we are forced to skip it. Should we introduce a callback
122 // to allow the user to know that their transformation was skipped?
123 // Alternatively, should we log something about how the transformation was
124 // skipped?
125 return;
126 }
127
128 current_transform_.a_zero = target_now;
129 current_transform_.b_zero = reference_now;
130 current_transform_.scale.numerator = reference_delta;
131 current_transform_.scale.denominator = target_delta;
132
133 AdvanceGenerationLocked();
134 }
135 }
136
137 void RateControlBase::SetRateAtReferenceTime(uint32_t reference_delta,
138 uint32_t target_delta,
139 int64_t reference_time) {
140 // Only rate changes with a non-zero target_delta are permitted. See comment
141 // in SetCurrentQuad.
142 if (!target_delta) {
143 OnIllegalRateChange(reference_delta, target_delta);
144 return;
145 } else {
146 base::AutoLock lock(transform_lock_);
147
148 // If the user tries to schedule a change which takes place before any
149 // already scheduled change, ignore it.
150 if (reference_pending_changes_.size() &&
151 reference_pending_changes_.back().b_zero >= reference_time) {
152 return;
153 }
154
155 reference_pending_changes_.emplace_back(0,
156 reference_delta,
157 target_delta,
158 reference_time);
159 }
160 }
161
162 void RateControlBase::SetRateAtTargetTime(uint32_t reference_delta,
163 uint32_t target_delta,
164 int64_t target_time) {
165 // Only rate changes with a non-zero target_delta are permitted. See comment
166 // in SetCurrentQuad.
167 if (!target_delta) {
168 OnIllegalRateChange(reference_delta, target_delta);
169 return;
170 } else {
171 base::AutoLock lock(transform_lock_);
172
173 // If the user tries to schedule a change which takes place before any
174 // already scheduled change, ignore it.
175 if (target_pending_changes_.size() &&
176 target_pending_changes_.back().a_zero >= target_time) {
177 return;
178 }
179
180 target_pending_changes_.emplace_back(target_time,
181 reference_delta,
182 target_delta,
183 0);
184 }
185 }
186
187 void RateControlBase::CancelPendingChanges() {
188 base::AutoLock lock(transform_lock_);
189
190 reference_pending_changes_.clear();
191 target_pending_changes_.clear();
192 }
193
194 void RateControlBase::ApplyPendingChangesLocked(int64_t target_now) {
195 bool advance_generation = false;
196
197 do {
198 // Grab a pointer to the next pending target scheduled transform which is
199 // not in the future, if any.
200 int64_t target_age;
201 const LinearTransform* target_trans = nullptr;
202 if (target_pending_changes_.size() &&
203 (target_now >= target_pending_changes_.front().a_zero)) {
204 target_trans = &target_pending_changes_.front();
205 target_age = target_now - target_trans->a_zero;
206 }
207
208 // Grab a pointer to the next pending reference scheduled transform which is
209 // not in the future, if any.
210 //
211 // TODO(johngro): Optimize this. When we have pending reference scheduled
212 // transformations, we don't have to compute this each and every time. We
213 // could just keep the time of the next reference scheduled change
214 // (expressed in target time) pre-computed, and only update it when the
215 // current transformation actually changes.
216 int64_t reference_age;
217 int64_t next_reference_change_target_time;
218 const LinearTransform* reference_trans = nullptr;
219 if (reference_pending_changes_.size()) {
220 if (current_transform_.DoReverseTransform(
221 reference_pending_changes_.front().b_zero,
222 &next_reference_change_target_time)) {
223 if (target_now >= next_reference_change_target_time) {
224 reference_age = target_now - next_reference_change_target_time;
225 reference_trans = &reference_pending_changes_.front();
226 }
227 }
228 }
229
230 if (target_trans && (!reference_trans || (reference_age <= target_age))) {
231 // If we have a target scheduled transform which should be applied, and we
232 // either have no reference scheduled transform which should be applied,
233 // or we have a reference scheduled transform which should be applied
234 // after the pending target scheduled transform, go ahead and apply the
235 // target transform.
236 //
237 // Note: if we cannot apply this transformation due to overflow, we have a
238 // serious problem. For now, we just purge the scheduled transformation
239 // and move on, but this is something which should never happen. We
240 // should probably signal an error up to the user somehow.
241 int64_t next_target_change_reference_time;
242
243 if (current_transform_.DoForwardTransform(
244 target_trans->a_zero,
245 &next_target_change_reference_time)) {
246 current_transform_.a_zero = target_trans->a_zero;
247 current_transform_.b_zero = next_target_change_reference_time;
248 current_transform_.scale.numerator = target_trans->scale.numerator;
249 current_transform_.scale.denominator = target_trans->scale.denominator;
250 DCHECK(current_transform_.scale.denominator);
251 }
252
253 advance_generation = true;
254 target_pending_changes_.pop_front();
255 } else if (reference_trans) {
256 // We have a reference scheduled transformation which should be applied
257 // before any pending target scheduled transformation. Do so now. No
258 // need to compute the splice point for the function, we have already done
259 // so when determining if we should apply this transformation or not.
260 current_transform_.a_zero = next_reference_change_target_time;
261 current_transform_.b_zero = reference_trans->a_zero;
262 current_transform_.scale.numerator = reference_trans->scale.numerator;
263 current_transform_.scale.denominator = reference_trans->scale.denominator;
264 DCHECK(current_transform_.scale.denominator);
265
266 advance_generation = true;
267 reference_pending_changes_.pop_front();
268 } else {
269 // We have no transformations which need to be applied at the moment. We
270 // are done for now.
271 break;
272 }
273 } while (true);
274
275 // If we have applied any changes, advance the transformation generation
276 if (advance_generation) {
277 AdvanceGenerationLocked();
278 }
279 }
280
281 void RateControlBase::OnIllegalRateChange(uint32_t numerator,
282 uint32_t denominator) {
283 LOG(ERROR) << "Illegal rate change requested ("
284 << numerator << "/" << denominator << ")";
285 Reset();
286 }
287
288 void RateControlBase::Reset() {
289 CancelPendingChanges();
290 SetRate(0, 1);
291
292 if (binding_.is_bound()) {
293 binding_.set_connection_error_handler(mojo::Closure());
294 binding_.Close();
295 }
296 }
297
298 } // namespace media
299 } // namespace mojo
OLDNEW
« no previous file with comments | « services/media/common/rate_control_base.h ('k') | services/media/common/timeline_control_site.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698