Home | History | Annotate | Download | only in libstagefright
      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 
     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 
     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 
     53 void MediaClock::init() {
     54     mLooper->registerHandler(this);
     55 }
     56 
     57 MediaClock::~MediaClock() {
     58     reset();
     59     if (mLooper != NULL) {
     60         mLooper->unregisterHandler(id());
     61         mLooper->stop();
     62     }
     63 }
     64 
     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 
     79 void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
     80     Mutex::Autolock autoLock(mLock);
     81     mStartingTimeMediaUs = startingTimeMediaUs;
     82 }
     83 
     84 void MediaClock::clearAnchor() {
     85     Mutex::Autolock autoLock(mLock);
     86     updateAnchorTimesAndPlaybackRate_l(-1, -1, mPlaybackRate);
     87 }
     88 
     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 
    124 void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
    125     Mutex::Autolock autoLock(mLock);
    126     mMaxTimeMediaUs = maxTimeMediaUs;
    127 }
    128 
    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 
    151 float MediaClock::getPlaybackRate() const {
    152     Mutex::Autolock autoLock(mLock);
    153     return mPlaybackRate;
    154 }
    155 
    156 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 
    166 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 
    187 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 
    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 
    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 
    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 
    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 
    323 void MediaClock::setNotificationMessage(const sp<AMessage> &msg) {
    324     Mutex::Autolock autoLock(mLock);
    325     mNotify = msg;
    326 }
    327 
    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