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         if (nowMediaUs < oldNowMediaUs
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> &notify, 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 < 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