OLD | NEW |
| (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 | |
OLD | NEW |