1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaClock"
19 #include <utils/Log.h>
20 #include <map>
21
22 #include <media/stagefright/MediaClock.h>
23
24 #include <media/stagefright/foundation/ADebug.h>
25 #include <media/stagefright/foundation/AMessage.h>
26
27 namespace android {
28
29 // Maximum allowed time backwards from anchor change.
30 // If larger than this threshold, it's treated as discontinuity.
31 static const int64_t kAnchorFluctuationAllowedUs = 10000LL;
32
Timer(const sp<AMessage> & notify,int64_t mediaTimeUs,int64_t adjustRealUs)33 MediaClock::Timer::Timer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs)
34 : mNotify(notify),
35 mMediaTimeUs(mediaTimeUs),
36 mAdjustRealUs(adjustRealUs) {
37 }
38
MediaClock()39 MediaClock::MediaClock()
40 : mAnchorTimeMediaUs(-1),
41 mAnchorTimeRealUs(-1),
42 mMaxTimeMediaUs(INT64_MAX),
43 mStartingTimeMediaUs(-1),
44 mPlaybackRate(1.0),
45 mGeneration(0) {
46 mLooper = new ALooper;
47 mLooper->setName("MediaClock");
48 mLooper->start(false /* runOnCallingThread */,
49 false /* canCallJava */,
50 ANDROID_PRIORITY_AUDIO);
51 }
52
init()53 void MediaClock::init() {
54 mLooper->registerHandler(this);
55 }
56
~MediaClock()57 MediaClock::~MediaClock() {
58 reset();
59 if (mLooper != NULL) {
60 mLooper->unregisterHandler(id());
61 mLooper->stop();
62 }
63 }
64
reset()65 void MediaClock::reset() {
66 Mutex::Autolock autoLock(mLock);
67 auto it = mTimers.begin();
68 while (it != mTimers.end()) {
69 it->mNotify->setInt32("reason", TIMER_REASON_RESET);
70 it->mNotify->post();
71 it = mTimers.erase(it);
72 }
73 mMaxTimeMediaUs = INT64_MAX;
74 mStartingTimeMediaUs = -1;
75 updateAnchorTimesAndPlaybackRate_l(-1, -1, 1.0);
76 ++mGeneration;
77 }
78
setStartingTimeMedia(int64_t startingTimeMediaUs)79 void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
80 Mutex::Autolock autoLock(mLock);
81 mStartingTimeMediaUs = startingTimeMediaUs;
82 }
83
clearAnchor()84 void MediaClock::clearAnchor() {
85 Mutex::Autolock autoLock(mLock);
86 updateAnchorTimesAndPlaybackRate_l(-1, -1, mPlaybackRate);
87 }
88
updateAnchor(int64_t anchorTimeMediaUs,int64_t anchorTimeRealUs,int64_t maxTimeMediaUs)89 void MediaClock::updateAnchor(
90 int64_t anchorTimeMediaUs,
91 int64_t anchorTimeRealUs,
92 int64_t maxTimeMediaUs) {
93 if (anchorTimeMediaUs < 0 || anchorTimeRealUs < 0) {
94 ALOGW("reject anchor time since it is negative.");
95 return;
96 }
97
98 Mutex::Autolock autoLock(mLock);
99 int64_t nowUs = ALooper::GetNowUs();
100 int64_t nowMediaUs =
101 anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate;
102 if (nowMediaUs < 0) {
103 ALOGW("reject anchor time since it leads to negative media time.");
104 return;
105 }
106
107 if (maxTimeMediaUs != -1) {
108 mMaxTimeMediaUs = maxTimeMediaUs;
109 }
110 if (mAnchorTimeRealUs != -1) {
111 int64_t oldNowMediaUs =
112 mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
113 // earlier, we ensured that the anchor times are non-negative and the
114 // math to calculate the now/oldNow times stays non-negative.
115 // by casting into uint64_t, we gain headroom to avoid any overflows at the upper end
116 // when adding the fluctuation allowance.
117 if ((uint64_t)nowMediaUs < (uint64_t)oldNowMediaUs + kAnchorFluctuationAllowedUs
118 && (uint64_t)nowMediaUs + kAnchorFluctuationAllowedUs > (uint64_t)oldNowMediaUs) {
119 return;
120 }
121 }
122 updateAnchorTimesAndPlaybackRate_l(nowMediaUs, nowUs, mPlaybackRate);
123
124 ++mGeneration;
125 processTimers_l();
126 }
127
updateMaxTimeMedia(int64_t maxTimeMediaUs)128 void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
129 Mutex::Autolock autoLock(mLock);
130 mMaxTimeMediaUs = maxTimeMediaUs;
131 }
132
setPlaybackRate(float rate)133 void MediaClock::setPlaybackRate(float rate) {
134 CHECK_GE(rate, 0.0);
135 Mutex::Autolock autoLock(mLock);
136 if (mAnchorTimeRealUs == -1) {
137 mPlaybackRate = rate;
138 return;
139 }
140
141 int64_t nowUs = ALooper::GetNowUs();
142 int64_t nowMediaUs = mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
143 if (nowMediaUs < 0) {
144 ALOGW("setRate: anchor time should not be negative, set to 0.");
145 nowMediaUs = 0;
146 }
147 updateAnchorTimesAndPlaybackRate_l(nowMediaUs, nowUs, rate);
148
149 if (rate > 0.0) {
150 ++mGeneration;
151 processTimers_l();
152 }
153 }
154
getPlaybackRate() const155 float MediaClock::getPlaybackRate() const {
156 Mutex::Autolock autoLock(mLock);
157 return mPlaybackRate;
158 }
159
getMediaTime(int64_t realUs,int64_t * outMediaUs,bool allowPastMaxTime) const160 status_t MediaClock::getMediaTime(
161 int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
162 if (outMediaUs == NULL) {
163 return BAD_VALUE;
164 }
165
166 Mutex::Autolock autoLock(mLock);
167 return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime);
168 }
169
getMediaTime_l(int64_t realUs,int64_t * outMediaUs,bool allowPastMaxTime) const170 status_t MediaClock::getMediaTime_l(
171 int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
172 if (mAnchorTimeRealUs == -1) {
173 return NO_INIT;
174 }
175
176 int64_t mediaUs = mAnchorTimeMediaUs
177 + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
178 if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) {
179 mediaUs = mMaxTimeMediaUs;
180 }
181 if (mediaUs < mStartingTimeMediaUs) {
182 mediaUs = mStartingTimeMediaUs;
183 }
184 if (mediaUs < 0) {
185 mediaUs = 0;
186 }
187 *outMediaUs = mediaUs;
188 return OK;
189 }
190
getRealTimeFor(int64_t targetMediaUs,int64_t * outRealUs) const191 status_t MediaClock::getRealTimeFor(
192 int64_t targetMediaUs, int64_t *outRealUs) const {
193 if (outRealUs == NULL) {
194 return BAD_VALUE;
195 }
196
197 Mutex::Autolock autoLock(mLock);
198 if (mPlaybackRate == 0.0) {
199 return NO_INIT;
200 }
201
202 int64_t nowUs = ALooper::GetNowUs();
203 int64_t nowMediaUs;
204 status_t status =
205 getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);
206 if (status != OK) {
207 return status;
208 }
209 *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;
210 return OK;
211 }
212
addTimer(const sp<AMessage> & notify,int64_t mediaTimeUs,int64_t adjustRealUs)213 void MediaClock::addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs,
214 int64_t adjustRealUs) {
215 Mutex::Autolock autoLock(mLock);
216
217 bool updateTimer = (mPlaybackRate != 0.0);
218 if (updateTimer) {
219 auto it = mTimers.begin();
220 while (it != mTimers.end()) {
221 if (((it->mAdjustRealUs - (double)adjustRealUs) * (double)mPlaybackRate
222 + (it->mMediaTimeUs - mediaTimeUs)) <= 0) {
223 updateTimer = false;
224 break;
225 }
226 ++it;
227 }
228 }
229
230 mTimers.emplace_back(notify, mediaTimeUs, adjustRealUs);
231
232 if (updateTimer) {
233 ++mGeneration;
234 processTimers_l();
235 }
236 }
237
onMessageReceived(const sp<AMessage> & msg)238 void MediaClock::onMessageReceived(const sp<AMessage> &msg) {
239 switch (msg->what()) {
240 case kWhatTimeIsUp:
241 {
242 int32_t generation;
243 CHECK(msg->findInt32("generation", &generation));
244
245 Mutex::Autolock autoLock(mLock);
246 if (generation != mGeneration) {
247 break;
248 }
249 processTimers_l();
250 break;
251 }
252
253 default:
254 TRESPASS();
255 break;
256 }
257 }
258
processTimers_l()259 void MediaClock::processTimers_l() {
260 int64_t nowMediaTimeUs;
261 status_t status = getMediaTime_l(
262 ALooper::GetNowUs(), &nowMediaTimeUs, false /* allowPastMaxTime */);
263
264 if (status != OK) {
265 return;
266 }
267
268 int64_t nextLapseRealUs = INT64_MAX;
269 std::multimap<int64_t, Timer> notifyList;
270 auto it = mTimers.begin();
271 while (it != mTimers.end()) {
272 double diff = it->mAdjustRealUs * (double)mPlaybackRate
273 + it->mMediaTimeUs - nowMediaTimeUs;
274 int64_t diffMediaUs;
275 if (diff > (double)INT64_MAX) {
276 diffMediaUs = INT64_MAX;
277 } else if (diff < (double)INT64_MIN) {
278 diffMediaUs = INT64_MIN;
279 } else {
280 diffMediaUs = diff;
281 }
282
283 if (diffMediaUs <= 0) {
284 notifyList.emplace(diffMediaUs, *it);
285 it = mTimers.erase(it);
286 } else {
287 if (mPlaybackRate != 0.0
288 && (double)diffMediaUs < (double)INT64_MAX * (double)mPlaybackRate) {
289 int64_t targetRealUs = diffMediaUs / (double)mPlaybackRate;
290 if (targetRealUs < nextLapseRealUs) {
291 nextLapseRealUs = targetRealUs;
292 }
293 }
294 ++it;
295 }
296 }
297
298 auto itNotify = notifyList.begin();
299 while (itNotify != notifyList.end()) {
300 itNotify->second.mNotify->setInt32("reason", TIMER_REASON_REACHED);
301 itNotify->second.mNotify->post();
302 itNotify = notifyList.erase(itNotify);
303 }
304
305 if (mTimers.empty() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0
306 || nextLapseRealUs == INT64_MAX) {
307 return;
308 }
309
310 sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
311 msg->setInt32("generation", mGeneration);
312 msg->post(nextLapseRealUs);
313 }
314
updateAnchorTimesAndPlaybackRate_l(int64_t anchorTimeMediaUs,int64_t anchorTimeRealUs,float playbackRate)315 void MediaClock::updateAnchorTimesAndPlaybackRate_l(int64_t anchorTimeMediaUs,
316 int64_t anchorTimeRealUs, float playbackRate) {
317 if (mAnchorTimeMediaUs != anchorTimeMediaUs
318 || mAnchorTimeRealUs != anchorTimeRealUs
319 || mPlaybackRate != playbackRate) {
320 mAnchorTimeMediaUs = anchorTimeMediaUs;
321 mAnchorTimeRealUs = anchorTimeRealUs;
322 mPlaybackRate = playbackRate;
323 notifyDiscontinuity_l();
324 }
325 }
326
setNotificationMessage(const sp<AMessage> & msg)327 void MediaClock::setNotificationMessage(const sp<AMessage> &msg) {
328 Mutex::Autolock autoLock(mLock);
329 mNotify = msg;
330 }
331
notifyDiscontinuity_l()332 void MediaClock::notifyDiscontinuity_l() {
333 if (mNotify != nullptr) {
334 sp<AMessage> msg = mNotify->dup();
335 msg->setInt64("anchor-media-us", mAnchorTimeMediaUs);
336 msg->setInt64("anchor-real-us", mAnchorTimeRealUs);
337 msg->setFloat("playback-rate", mPlaybackRate);
338 msg->post();
339 }
340 }
341
342 } // namespace android
343