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
21#include <media/stagefright/MediaClock.h>
22
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/foundation/ALooper.h>
25
26namespace android {
27
28// Maximum allowed time backwards from anchor change.
29// If larger than this threshold, it's treated as discontinuity.
30static const int64_t kAnchorFluctuationAllowedUs = 10000ll;
31
32MediaClock::MediaClock()
33    : mAnchorTimeMediaUs(-1),
34      mAnchorTimeRealUs(-1),
35      mMaxTimeMediaUs(INT64_MAX),
36      mStartingTimeMediaUs(-1),
37      mPlaybackRate(1.0) {
38}
39
40MediaClock::~MediaClock() {
41}
42
43void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
44    Mutex::Autolock autoLock(mLock);
45    mStartingTimeMediaUs = startingTimeMediaUs;
46}
47
48void MediaClock::clearAnchor() {
49    Mutex::Autolock autoLock(mLock);
50    mAnchorTimeMediaUs = -1;
51    mAnchorTimeRealUs = -1;
52}
53
54void MediaClock::updateAnchor(
55        int64_t anchorTimeMediaUs,
56        int64_t anchorTimeRealUs,
57        int64_t maxTimeMediaUs) {
58    if (anchorTimeMediaUs < 0 || anchorTimeRealUs < 0) {
59        ALOGW("reject anchor time since it is negative.");
60        return;
61    }
62
63    Mutex::Autolock autoLock(mLock);
64    int64_t nowUs = ALooper::GetNowUs();
65    int64_t nowMediaUs =
66        anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate;
67    if (nowMediaUs < 0) {
68        ALOGW("reject anchor time since it leads to negative media time.");
69        return;
70    }
71
72    if (maxTimeMediaUs != -1) {
73        mMaxTimeMediaUs = maxTimeMediaUs;
74    }
75    if (mAnchorTimeRealUs != -1) {
76        int64_t oldNowMediaUs =
77            mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
78        if (nowMediaUs < oldNowMediaUs
79                && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) {
80            return;
81        }
82    }
83    mAnchorTimeRealUs = nowUs;
84    mAnchorTimeMediaUs = nowMediaUs;
85}
86
87void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
88    Mutex::Autolock autoLock(mLock);
89    mMaxTimeMediaUs = maxTimeMediaUs;
90}
91
92void MediaClock::setPlaybackRate(float rate) {
93    CHECK_GE(rate, 0.0);
94    Mutex::Autolock autoLock(mLock);
95    if (mAnchorTimeRealUs == -1) {
96        mPlaybackRate = rate;
97        return;
98    }
99
100    int64_t nowUs = ALooper::GetNowUs();
101    mAnchorTimeMediaUs += (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
102    if (mAnchorTimeMediaUs < 0) {
103        ALOGW("setRate: anchor time should not be negative, set to 0.");
104        mAnchorTimeMediaUs = 0;
105    }
106    mAnchorTimeRealUs = nowUs;
107    mPlaybackRate = rate;
108}
109
110float MediaClock::getPlaybackRate() const {
111    Mutex::Autolock autoLock(mLock);
112    return mPlaybackRate;
113}
114
115status_t MediaClock::getMediaTime(
116        int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
117    if (outMediaUs == NULL) {
118        return BAD_VALUE;
119    }
120
121    Mutex::Autolock autoLock(mLock);
122    return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime);
123}
124
125status_t MediaClock::getMediaTime_l(
126        int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
127    if (mAnchorTimeRealUs == -1) {
128        return NO_INIT;
129    }
130
131    int64_t mediaUs = mAnchorTimeMediaUs
132            + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
133    if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) {
134        mediaUs = mMaxTimeMediaUs;
135    }
136    if (mediaUs < mStartingTimeMediaUs) {
137        mediaUs = mStartingTimeMediaUs;
138    }
139    if (mediaUs < 0) {
140        mediaUs = 0;
141    }
142    *outMediaUs = mediaUs;
143    return OK;
144}
145
146status_t MediaClock::getRealTimeFor(
147        int64_t targetMediaUs, int64_t *outRealUs) const {
148    if (outRealUs == NULL) {
149        return BAD_VALUE;
150    }
151
152    Mutex::Autolock autoLock(mLock);
153    if (mPlaybackRate == 0.0) {
154        return NO_INIT;
155    }
156
157    int64_t nowUs = ALooper::GetNowUs();
158    int64_t nowMediaUs;
159    status_t status =
160            getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);
161    if (status != OK) {
162        return status;
163    }
164    *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;
165    return OK;
166}
167
168}  // namespace android
169