MediaClock.cpp revision 17648f365e5914fa772bbfebaf9f5c6a37ddfb99
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
27namespace android {
28
29// Maximum allowed time backwards from anchor change.
30// If larger than this threshold, it's treated as discontinuity.
31static const int64_t kAnchorFluctuationAllowedUs = 10000ll;
32
33MediaClock::Timer::Timer(const sp<AMessage> &notify, int64_t mediaTimeUs, int64_t adjustRealUs)
34    : mNotify(notify),
35      mMediaTimeUs(mediaTimeUs),
36      mAdjustRealUs(adjustRealUs) {
37}
38
39MediaClock::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
53void MediaClock::init() {
54    mLooper->registerHandler(this);
55}
56
57MediaClock::~MediaClock() {
58    reset();
59    if (mLooper != NULL) {
60        mLooper->unregisterHandler(id());
61        mLooper->stop();
62    }
63}
64
65void 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    mAnchorTimeMediaUs = -1;
74    mAnchorTimeRealUs = -1;
75    mMaxTimeMediaUs = INT64_MAX;
76    mStartingTimeMediaUs = -1;
77    mPlaybackRate = 1.0;
78    ++mGeneration;
79}
80
81void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
82    Mutex::Autolock autoLock(mLock);
83    mStartingTimeMediaUs = startingTimeMediaUs;
84}
85
86void MediaClock::clearAnchor() {
87    Mutex::Autolock autoLock(mLock);
88    mAnchorTimeMediaUs = -1;
89    mAnchorTimeRealUs = -1;
90}
91
92void MediaClock::updateAnchor(
93        int64_t anchorTimeMediaUs,
94        int64_t anchorTimeRealUs,
95        int64_t maxTimeMediaUs) {
96    if (anchorTimeMediaUs < 0 || anchorTimeRealUs < 0) {
97        ALOGW("reject anchor time since it is negative.");
98        return;
99    }
100
101    Mutex::Autolock autoLock(mLock);
102    int64_t nowUs = ALooper::GetNowUs();
103    int64_t nowMediaUs =
104        anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate;
105    if (nowMediaUs < 0) {
106        ALOGW("reject anchor time since it leads to negative media time.");
107        return;
108    }
109
110    if (maxTimeMediaUs != -1) {
111        mMaxTimeMediaUs = maxTimeMediaUs;
112    }
113    if (mAnchorTimeRealUs != -1) {
114        int64_t oldNowMediaUs =
115            mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
116        if (nowMediaUs < oldNowMediaUs
117                && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) {
118            return;
119        }
120    }
121    mAnchorTimeRealUs = nowUs;
122    mAnchorTimeMediaUs = nowMediaUs;
123
124    ++mGeneration;
125    processTimers_l();
126}
127
128void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
129    Mutex::Autolock autoLock(mLock);
130    mMaxTimeMediaUs = maxTimeMediaUs;
131}
132
133void 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    mAnchorTimeMediaUs += (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
143    if (mAnchorTimeMediaUs < 0) {
144        ALOGW("setRate: anchor time should not be negative, set to 0.");
145        mAnchorTimeMediaUs = 0;
146    }
147    mAnchorTimeRealUs = nowUs;
148    mPlaybackRate = rate;
149
150    if (rate > 0.0) {
151        ++mGeneration;
152        processTimers_l();
153    }
154}
155
156float MediaClock::getPlaybackRate() const {
157    Mutex::Autolock autoLock(mLock);
158    return mPlaybackRate;
159}
160
161status_t MediaClock::getMediaTime(
162        int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
163    if (outMediaUs == NULL) {
164        return BAD_VALUE;
165    }
166
167    Mutex::Autolock autoLock(mLock);
168    return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime);
169}
170
171status_t MediaClock::getMediaTime_l(
172        int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
173    if (mAnchorTimeRealUs == -1) {
174        return NO_INIT;
175    }
176
177    int64_t mediaUs = mAnchorTimeMediaUs
178            + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
179    if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) {
180        mediaUs = mMaxTimeMediaUs;
181    }
182    if (mediaUs < mStartingTimeMediaUs) {
183        mediaUs = mStartingTimeMediaUs;
184    }
185    if (mediaUs < 0) {
186        mediaUs = 0;
187    }
188    *outMediaUs = mediaUs;
189    return OK;
190}
191
192status_t MediaClock::getRealTimeFor(
193        int64_t targetMediaUs, int64_t *outRealUs) const {
194    if (outRealUs == NULL) {
195        return BAD_VALUE;
196    }
197
198    Mutex::Autolock autoLock(mLock);
199    if (mPlaybackRate == 0.0) {
200        return NO_INIT;
201    }
202
203    int64_t nowUs = ALooper::GetNowUs();
204    int64_t nowMediaUs;
205    status_t status =
206            getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);
207    if (status != OK) {
208        return status;
209    }
210    *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;
211    return OK;
212}
213
214void MediaClock::addTimer(const sp<AMessage> &notify, int64_t mediaTimeUs,
215                          int64_t adjustRealUs) {
216    Mutex::Autolock autoLock(mLock);
217
218    bool updateTimer = (mPlaybackRate != 0.0);
219    if (updateTimer) {
220        auto it = mTimers.begin();
221        while (it != mTimers.end()) {
222            if (((it->mAdjustRealUs - (double)adjustRealUs) * (double)mPlaybackRate
223                + (it->mMediaTimeUs - mediaTimeUs)) <= 0) {
224                updateTimer = false;
225                break;
226            }
227            ++it;
228        }
229    }
230
231    mTimers.emplace_back(notify, mediaTimeUs, adjustRealUs);
232
233    if (updateTimer) {
234        ++mGeneration;
235        processTimers_l();
236    }
237}
238
239void MediaClock::onMessageReceived(const sp<AMessage> &msg) {
240    switch (msg->what()) {
241        case kWhatTimeIsUp:
242        {
243            int32_t generation;
244            CHECK(msg->findInt32("generation", &generation));
245
246            Mutex::Autolock autoLock(mLock);
247            if (generation != mGeneration) {
248                break;
249            }
250            processTimers_l();
251            break;
252        }
253
254        default:
255            TRESPASS();
256            break;
257    }
258}
259
260void MediaClock::processTimers_l() {
261    int64_t nowMediaTimeUs;
262    status_t status = getMediaTime_l(
263            ALooper::GetNowUs(), &nowMediaTimeUs, false /* allowPastMaxTime */);
264
265    if (status != OK) {
266        return;
267    }
268
269    int64_t nextLapseRealUs = INT64_MAX;
270    std::multimap<int64_t, Timer> notifyList;
271    auto it = mTimers.begin();
272    while (it != mTimers.end()) {
273        double diff = it->mAdjustRealUs * (double)mPlaybackRate
274            + it->mMediaTimeUs - nowMediaTimeUs;
275        int64_t diffMediaUs;
276        if (diff > (double)INT64_MAX) {
277            diffMediaUs = INT64_MAX;
278        } else if (diff < (double)INT64_MIN) {
279            diffMediaUs = INT64_MIN;
280        } else {
281            diffMediaUs = diff;
282        }
283
284        if (diffMediaUs <= 0) {
285            notifyList.emplace(diffMediaUs, *it);
286            it = mTimers.erase(it);
287        } else {
288            if (mPlaybackRate != 0.0
289                && (double)diffMediaUs < INT64_MAX * (double)mPlaybackRate) {
290                int64_t targetRealUs = diffMediaUs / (double)mPlaybackRate;
291                if (targetRealUs < nextLapseRealUs) {
292                    nextLapseRealUs = targetRealUs;
293                }
294            }
295            ++it;
296        }
297    }
298
299    auto itNotify = notifyList.begin();
300    while (itNotify != notifyList.end()) {
301        itNotify->second.mNotify->setInt32("reason", TIMER_REASON_REACHED);
302        itNotify->second.mNotify->post();
303        itNotify = notifyList.erase(itNotify);
304    }
305
306    if (mTimers.empty() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0
307        || nextLapseRealUs == INT64_MAX) {
308        return;
309    }
310
311    sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
312    msg->setInt32("generation", mGeneration);
313    msg->post(nextLapseRealUs);
314}
315
316}  // namespace android
317