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> ¬ify, 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> ¬ify, 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