MediaClock.cpp revision 47afe0a1a6f37e7807a81acb21ef588aec0a3518
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    mMaxTimeMediaUs = INT64_MAX;
74    mStartingTimeMediaUs = -1;
75    updateAnchorTimesAndPlaybackRate_l(-1, -1, 1.0);
76    ++mGeneration;
77}
78
79void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
80    Mutex::Autolock autoLock(mLock);
81    mStartingTimeMediaUs = startingTimeMediaUs;
82}
83
84void MediaClock::clearAnchor() {
85    Mutex::Autolock autoLock(mLock);
86    updateAnchorTimesAndPlaybackRate_l(-1, -1, mPlaybackRate);
87}
88
89void 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
124void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
125    Mutex::Autolock autoLock(mLock);
126    mMaxTimeMediaUs = maxTimeMediaUs;
127}
128
129void 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
151float MediaClock::getPlaybackRate() const {
152    Mutex::Autolock autoLock(mLock);
153    return mPlaybackRate;
154}
155
156status_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
166status_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
187status_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
209void 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
234void 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
255void 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
311void 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
323void MediaClock::setNotificationMessage(const sp<AMessage> &msg) {
324    Mutex::Autolock autoLock(mLock);
325    mNotify = msg;
326}
327
328void 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