| 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 |