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> &notify, 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> &notify, 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