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 if (nowMediaUs < oldNowMediaUs + kAnchorFluctuationAllowedUs
114 && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) {
115 return;
116 }
117 }
118 updateAnchorTimesAndPlaybackRate_l(nowMediaUs, nowUs, mPlaybackRate);
119
120 ++mGeneration;
121 processTimers_l();
122 }
123
updateMaxTimeMedia(int64_t maxTimeMediaUs)124 void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
125 Mutex::Autolock autoLock(mLock);
126 mMaxTimeMediaUs = maxTimeMediaUs;
127 }
128
setPlaybackRate(float rate)129 void MediaClock::setPlaybackRate(float rate) {
130 CHECK_GE(rate, 0.0);
131 Mutex::Autolock autoLock(mLock);
132 if (mAnchorTimeRealUs == -1) {
133 mPlaybackRate = rate;
134 return;
135 }
136
137 int64_t nowUs = ALooper::GetNowUs();
138 int64_t nowMediaUs = mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
139 if (nowMediaUs < 0) {
140 ALOGW("setRate: anchor time should not be negative, set to 0.");
141 nowMediaUs = 0;
142 }
143 updateAnchorTimesAndPlaybackRate_l(nowMediaUs, nowUs, rate);
144
145 if (rate > 0.0) {
146 ++mGeneration;
147 processTimers_l();
148 }
149 }
150
getPlaybackRate() const151 float MediaClock::getPlaybackRate() const {
152 Mutex::Autolock autoLock(mLock);
153 return mPlaybackRate;
154 }
155
getMediaTime(int64_t realUs,int64_t * outMediaUs,bool allowPastMaxTime) const156 status_t MediaClock::getMediaTime(
157 int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
158 if (outMediaUs == NULL) {
159 return BAD_VALUE;
160 }
161
162 Mutex::Autolock autoLock(mLock);
163 return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime);
164 }
165
getMediaTime_l(int64_t realUs,int64_t * outMediaUs,bool allowPastMaxTime) const166 status_t MediaClock::getMediaTime_l(
167 int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
168 if (mAnchorTimeRealUs == -1) {
169 return NO_INIT;
170 }
171
172 int64_t mediaUs = mAnchorTimeMediaUs
173 + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
174 if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) {
175 mediaUs = mMaxTimeMediaUs;
176 }
177 if (mediaUs < mStartingTimeMediaUs) {
178 mediaUs = mStartingTimeMediaUs;
179 }
180 if (mediaUs < 0) {
181 mediaUs = 0;
182 }
183 *outMediaUs = mediaUs;
184 return OK;
185 }
186
getRealTimeFor(int64_t targetMediaUs,int64_t * outRealUs) const187 status_t MediaClock::getRealTimeFor(
188 int64_t targetMediaUs, int64_t *outRealUs) const {
189 if (outRealUs == NULL) {
190 return BAD_VALUE;
191 }
192
193 Mutex::Autolock autoLock(mLock);
194 if (mPlaybackRate == 0.0) {
195 return NO_INIT;
196 }
197
198 int64_t nowUs = ALooper::GetNowUs();
199 int64_t nowMediaUs;
200 status_t status =
201 getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);
202 if (status != OK) {
203 return status;
204 }
205 *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;
206 return OK;
207 }
208
addTimer(const sp<AMessage> & notify,int64_t mediaTimeUs,int64_t adjustRealUs)209 void MediaClock::addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs,
210 int64_t adjustRealUs) {
211 Mutex::Autolock autoLock(mLock);
212
213 bool updateTimer = (mPlaybackRate != 0.0);
214 if (updateTimer) {
215 auto it = mTimers.begin();
216 while (it != mTimers.end()) {
217 if (((it->mAdjustRealUs - (double)adjustRealUs) * (double)mPlaybackRate
218 + (it->mMediaTimeUs - mediaTimeUs)) <= 0) {
219 updateTimer = false;
220 break;
221 }
222 ++it;
223 }
224 }
225
226 mTimers.emplace_back(notify, mediaTimeUs, adjustRealUs);
227
228 if (updateTimer) {
229 ++mGeneration;
230 processTimers_l();
231 }
232 }
233
onMessageReceived(const sp<AMessage> & msg)234 void MediaClock::onMessageReceived(const sp<AMessage> &msg) {
235 switch (msg->what()) {
236 case kWhatTimeIsUp:
237 {
238 int32_t generation;
239 CHECK(msg->findInt32("generation", &generation));
240
241 Mutex::Autolock autoLock(mLock);
242 if (generation != mGeneration) {
243 break;
244 }
245 processTimers_l();
246 break;
247 }
248
249 default:
250 TRESPASS();
251 break;
252 }
253 }
254
processTimers_l()255 void MediaClock::processTimers_l() {
256 int64_t nowMediaTimeUs;
257 status_t status = getMediaTime_l(
258 ALooper::GetNowUs(), &nowMediaTimeUs, false /* allowPastMaxTime */);
259
260 if (status != OK) {
261 return;
262 }
263
264 int64_t nextLapseRealUs = INT64_MAX;
265 std::multimap<int64_t, Timer> notifyList;
266 auto it = mTimers.begin();
267 while (it != mTimers.end()) {
268 double diff = it->mAdjustRealUs * (double)mPlaybackRate
269 + it->mMediaTimeUs - nowMediaTimeUs;
270 int64_t diffMediaUs;
271 if (diff > (double)INT64_MAX) {
272 diffMediaUs = INT64_MAX;
273 } else if (diff < (double)INT64_MIN) {
274 diffMediaUs = INT64_MIN;
275 } else {
276 diffMediaUs = diff;
277 }
278
279 if (diffMediaUs <= 0) {
280 notifyList.emplace(diffMediaUs, *it);
281 it = mTimers.erase(it);
282 } else {
283 if (mPlaybackRate != 0.0
284 && (double)diffMediaUs < (double)INT64_MAX * (double)mPlaybackRate) {
285 int64_t targetRealUs = diffMediaUs / (double)mPlaybackRate;
286 if (targetRealUs < nextLapseRealUs) {
287 nextLapseRealUs = targetRealUs;
288 }
289 }
290 ++it;
291 }
292 }
293
294 auto itNotify = notifyList.begin();
295 while (itNotify != notifyList.end()) {
296 itNotify->second.mNotify->setInt32("reason", TIMER_REASON_REACHED);
297 itNotify->second.mNotify->post();
298 itNotify = notifyList.erase(itNotify);
299 }
300
301 if (mTimers.empty() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0
302 || nextLapseRealUs == INT64_MAX) {
303 return;
304 }
305
306 sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
307 msg->setInt32("generation", mGeneration);
308 msg->post(nextLapseRealUs);
309 }
310
updateAnchorTimesAndPlaybackRate_l(int64_t anchorTimeMediaUs,int64_t anchorTimeRealUs,float playbackRate)311 void MediaClock::updateAnchorTimesAndPlaybackRate_l(int64_t anchorTimeMediaUs,
312 int64_t anchorTimeRealUs, float playbackRate) {
313 if (mAnchorTimeMediaUs != anchorTimeMediaUs
314 || mAnchorTimeRealUs != anchorTimeRealUs
315 || mPlaybackRate != playbackRate) {
316 mAnchorTimeMediaUs = anchorTimeMediaUs;
317 mAnchorTimeRealUs = anchorTimeRealUs;
318 mPlaybackRate = playbackRate;
319 notifyDiscontinuity_l();
320 }
321 }
322
setNotificationMessage(const sp<AMessage> & msg)323 void MediaClock::setNotificationMessage(const sp<AMessage> &msg) {
324 Mutex::Autolock autoLock(mLock);
325 mNotify = msg;
326 }
327
notifyDiscontinuity_l()328 void MediaClock::notifyDiscontinuity_l() {
329 if (mNotify != nullptr) {
330 sp<AMessage> msg = mNotify->dup();
331 msg->setInt64("anchor-media-us", mAnchorTimeMediaUs);
332 msg->setInt64("anchor-real-us", mAnchorTimeRealUs);
333 msg->setFloat("playback-rate", mPlaybackRate);
334 msg->post();
335 }
336 }
337
338 } // namespace android
339