From 9816016afb2a13c6a866cd047d57020566a8b9a9 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 4 Feb 2015 17:01:11 -0800 Subject: mediaplayer: support dynamic playback rate Bug: 19196501 Change-Id: I856b1507d5fa2cedfb645706d2435683a7d3e050 --- include/media/IMediaPlayer.h | 1 + include/media/MediaPlayerInterface.h | 1 + include/media/mediaplayer.h | 2 + media/libmedia/IMediaPlayer.cpp | 15 ++++ media/libmedia/mediaplayer.cpp | 19 +++++ media/libmediaplayerservice/MediaPlayerService.cpp | 8 +++ media/libmediaplayerservice/MediaPlayerService.h | 1 + .../libmediaplayerservice/nuplayer/MediaClock.cpp | 80 ++++++++++++---------- media/libmediaplayerservice/nuplayer/MediaClock.h | 23 +++++-- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 20 ++++++ media/libmediaplayerservice/nuplayer/NuPlayer.h | 3 + .../nuplayer/NuPlayerDriver.cpp | 5 ++ .../nuplayer/NuPlayerDriver.h | 1 + .../nuplayer/NuPlayerRenderer.cpp | 73 +++++++++++++------- .../nuplayer/NuPlayerRenderer.h | 6 ++ 15 files changed, 190 insertions(+), 68 deletions(-) diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index db62cd5..4153c25 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -56,6 +56,7 @@ public: virtual status_t stop() = 0; virtual status_t pause() = 0; virtual status_t isPlaying(bool* state) = 0; + virtual status_t setPlaybackRate(float rate) = 0; virtual status_t seekTo(int msec) = 0; virtual status_t getCurrentPosition(int* msec) = 0; virtual status_t getDuration(int* msec) = 0; diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 4a6bf28..482b85f 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -156,6 +156,7 @@ public: virtual status_t stop() = 0; virtual status_t pause() = 0; virtual bool isPlaying() = 0; + virtual status_t setPlaybackRate(float rate) { return INVALID_OPERATION; } virtual status_t seekTo(int msec) = 0; virtual status_t getCurrentPosition(int *msec) = 0; virtual status_t getDuration(int *msec) = 0; diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 5830933..808e893 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -220,6 +220,7 @@ public: status_t stop(); status_t pause(); bool isPlaying(); + status_t setPlaybackRate(float rate); status_t getVideoWidth(int *w); status_t getVideoHeight(int *h); status_t seekTo(int msec); @@ -274,6 +275,7 @@ private: int mVideoWidth; int mVideoHeight; int mAudioSessionId; + float mPlaybackRate; float mSendLevel; struct sockaddr_in mRetransmitEndpoint; bool mRetransmitEndpointValid; diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 7f3e5cc..dcd5670 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -39,6 +39,7 @@ enum { START, STOP, IS_PLAYING, + SET_PLAYBACK_RATE, PAUSE, SEEK_TO, GET_CURRENT_POSITION, @@ -164,6 +165,15 @@ public: return reply.readInt32(); } + status_t setPlaybackRate(float rate) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeFloat(rate); + remote()->transact(SET_PLAYBACK_RATE, data, &reply); + return reply.readInt32(); + } + status_t pause() { Parcel data, reply; @@ -426,6 +436,11 @@ status_t BnMediaPlayer::onTransact( reply->writeInt32(ret); return NO_ERROR; } break; + case SET_PLAYBACK_RATE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setPlaybackRate(data.readFloat())); + return NO_ERROR; + } break; case PAUSE: { CHECK_INTERFACE(IMediaPlayer, data, reply); reply->writeInt32(pause()); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 432ecda..d1d51cc 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -59,6 +59,7 @@ MediaPlayer::MediaPlayer() mLoop = false; mLeftVolume = mRightVolume = 1.0; mVideoWidth = mVideoHeight = 0; + mPlaybackRate = 1.0; mLockThreadId = 0; mAudioSessionId = AudioSystem::newAudioUniqueId(); AudioSystem::acquireAudioSessionId(mAudioSessionId, -1); @@ -378,6 +379,24 @@ bool MediaPlayer::isPlaying() return false; } +status_t MediaPlayer::setPlaybackRate(float rate) +{ + ALOGV("setPlaybackRate: %f", rate); + if (rate <= 0.0) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + if (mPlaybackRate == rate) { + return NO_ERROR; + } + mPlaybackRate = rate; + return mPlayer->setPlaybackRate(rate); + } + ALOGV("setPlaybackRate: no active player"); + return INVALID_OPERATION; +} + status_t MediaPlayer::getVideoWidth(int *w) { ALOGV("getVideoWidth"); diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 694f1a4..0b18ae0 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -961,6 +961,14 @@ status_t MediaPlayerService::Client::isPlaying(bool* state) return NO_ERROR; } +status_t MediaPlayerService::Client::setPlaybackRate(float rate) +{ + ALOGV("[%d] setPlaybackRate(%f)", mConnId, rate); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->setPlaybackRate(rate); +} + status_t MediaPlayerService::Client::getCurrentPosition(int *msec) { ALOGV("getCurrentPosition"); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index fad3447..7320311 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -261,6 +261,7 @@ private: virtual status_t stop(); virtual status_t pause(); virtual status_t isPlaying(bool* state); + virtual status_t setPlaybackRate(float rate); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int* msec); virtual status_t getDuration(int* msec); diff --git a/media/libmediaplayerservice/nuplayer/MediaClock.cpp b/media/libmediaplayerservice/nuplayer/MediaClock.cpp index 7bfff13..9152da1 100644 --- a/media/libmediaplayerservice/nuplayer/MediaClock.cpp +++ b/media/libmediaplayerservice/nuplayer/MediaClock.cpp @@ -20,19 +20,17 @@ #include "MediaClock.h" +#include #include namespace android { -// Maximum time change between two updates. -static const int64_t kMaxAnchorFluctuationUs = 1000ll; - MediaClock::MediaClock() : mAnchorTimeMediaUs(-1), mAnchorTimeRealUs(-1), mMaxTimeMediaUs(INT64_MAX), mStartingTimeMediaUs(-1), - mPaused(false) { + mPlaybackRate(1.0) { } MediaClock::~MediaClock() { @@ -58,14 +56,14 @@ void MediaClock::updateAnchor( return; } + Mutex::Autolock autoLock(mLock); int64_t nowUs = ALooper::GetNowUs(); - int64_t nowMediaUs = anchorTimeMediaUs + nowUs - anchorTimeRealUs; + int64_t nowMediaUs = + anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate; if (nowMediaUs < 0) { ALOGW("reject anchor time since it leads to negative media time."); return; } - - Mutex::Autolock autoLock(mLock); mAnchorTimeRealUs = nowUs; mAnchorTimeMediaUs = nowMediaUs; mMaxTimeMediaUs = maxTimeMediaUs; @@ -76,60 +74,66 @@ void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) { mMaxTimeMediaUs = maxTimeMediaUs; } -void MediaClock::pause() { +void MediaClock::setPlaybackRate(float rate) { + CHECK_GE(rate, 0.0); Mutex::Autolock autoLock(mLock); - if (mPaused) { - return; - } - - mPaused = true; if (mAnchorTimeRealUs == -1) { + mPlaybackRate = rate; return; } int64_t nowUs = ALooper::GetNowUs(); - mAnchorTimeMediaUs += nowUs - mAnchorTimeRealUs; + mAnchorTimeMediaUs += (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate; if (mAnchorTimeMediaUs < 0) { - ALOGW("anchor time should not be negative, set to 0."); + ALOGW("setRate: anchor time should not be negative, set to 0."); mAnchorTimeMediaUs = 0; } mAnchorTimeRealUs = nowUs; + mPlaybackRate = rate; } -void MediaClock::resume() { +status_t MediaClock::getMediaTime( + int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) { Mutex::Autolock autoLock(mLock); - if (!mPaused) { - return; - } + return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime); +} - mPaused = false; +status_t MediaClock::getMediaTime_l( + int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) { if (mAnchorTimeRealUs == -1) { - return; + return NO_INIT; } - mAnchorTimeRealUs = ALooper::GetNowUs(); + int64_t mediaUs = mAnchorTimeMediaUs + + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate; + if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) { + mediaUs = mMaxTimeMediaUs; + } + if (mediaUs < mStartingTimeMediaUs) { + mediaUs = mStartingTimeMediaUs; + } + if (mediaUs < 0) { + mediaUs = 0; + } + *outMediaUs = mediaUs; + return OK; } -int64_t MediaClock::getTimeMedia(int64_t realUs, bool allowPastMaxTime) { +status_t MediaClock::getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs) { Mutex::Autolock autoLock(mLock); - if (mAnchorTimeRealUs == -1) { - return -1ll; + if (mPlaybackRate == 0.0) { + return NO_INIT; } - if (mPaused) { - realUs = mAnchorTimeRealUs; - } - int64_t currentMediaUs = mAnchorTimeMediaUs + realUs - mAnchorTimeRealUs; - if (currentMediaUs > mMaxTimeMediaUs && !allowPastMaxTime) { - currentMediaUs = mMaxTimeMediaUs; - } - if (currentMediaUs < mStartingTimeMediaUs) { - currentMediaUs = mStartingTimeMediaUs; - } - if (currentMediaUs < 0) { - currentMediaUs = 0; + int64_t nowUs = ALooper::GetNowUs(); + int64_t nowMediaUs; + status_t status = + getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */); + if (status != OK) { + return status; } - return currentMediaUs; + *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs; + return OK; } } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/MediaClock.h b/media/libmediaplayerservice/nuplayer/MediaClock.h index d005993..660764f 100644 --- a/media/libmediaplayerservice/nuplayer/MediaClock.h +++ b/media/libmediaplayerservice/nuplayer/MediaClock.h @@ -32,9 +32,8 @@ struct MediaClock : public RefBase { void setStartingTimeMedia(int64_t startingTimeMediaUs); void clearAnchor(); - // It's highly recommended to use timestamp of just rendered frame as - // anchor time, especially in paused state. Such restriction will be - // required when dynamic playback rate is supported in the future. + // It's required to use timestamp of just rendered frame as + // anchor time in paused state. void updateAnchor( int64_t anchorTimeMediaUs, int64_t anchorTimeRealUs, @@ -42,15 +41,25 @@ struct MediaClock : public RefBase { void updateMaxTimeMedia(int64_t maxTimeMediaUs); - void pause(); - void resume(); + void setPlaybackRate(float rate); - int64_t getTimeMedia(int64_t realUs, bool allowPastMaxTime = false); + // query media time corresponding to real time |realUs|, and save the + // result in |outMediaUs|. + status_t getMediaTime(int64_t realUs, + int64_t *outMediaUs, + bool allowPastMaxTime = false); + // query real time corresponding to media time |targetMediaUs|. + // The result is saved in |outRealUs|. + status_t getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs); protected: virtual ~MediaClock(); private: + status_t getMediaTime_l(int64_t realUs, + int64_t *outMediaUs, + bool allowPastMaxTime); + Mutex mLock; int64_t mAnchorTimeMediaUs; @@ -58,7 +67,7 @@ private: int64_t mMaxTimeMediaUs; int64_t mStartingTimeMediaUs; - bool mPaused; + float mPlaybackRate; DISALLOW_EVIL_CONSTRUCTORS(MediaClock); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index fb8dbce..0d19fe9 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -180,6 +180,7 @@ NuPlayer::NuPlayer() mFlushingVideo(NONE), mResumePending(false), mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW), + mPlaybackRate(1.0), mStarted(false), mPaused(false), mPausedByClient(false) { @@ -314,6 +315,12 @@ void NuPlayer::start() { (new AMessage(kWhatStart, id()))->post(); } +void NuPlayer::setPlaybackRate(float rate) { + sp msg = new AMessage(kWhatSetRate, id()); + msg->setFloat("rate", rate); + msg->post(); +} + void NuPlayer::pause() { (new AMessage(kWhatPause, id()))->post(); } @@ -604,6 +611,16 @@ void NuPlayer::onMessageReceived(const sp &msg) { break; } + case kWhatSetRate: + { + ALOGV("kWhatSetRate"); + CHECK(msg->findFloat("rate", &mPlaybackRate)); + if (mRenderer != NULL) { + mRenderer->setPlaybackRate(mPlaybackRate); + } + break; + } + case kWhatScanSources: { int32_t generation; @@ -1048,6 +1065,9 @@ void NuPlayer::onStart() { ++mRendererGeneration; notify->setInt32("generation", mRendererGeneration); mRenderer = new Renderer(mAudioSink, notify, flags); + if (mPlaybackRate != 1.0) { + mRenderer->setPlaybackRate(mPlaybackRate); + } mRendererLooper = new ALooper; mRendererLooper->setName("NuPlayerRenderer"); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 57eaf74..a2cb53e 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -51,6 +51,7 @@ struct NuPlayer : public AHandler { const sp &bufferProducer); void setAudioSink(const sp &sink); + void setPlaybackRate(float rate); void start(); void pause(); @@ -104,6 +105,7 @@ private: kWhatSetVideoNativeWindow = '=NaW', kWhatSetAudioSink = '=AuS', kWhatMoreDataQueued = 'more', + kWhatSetRate = 'setR', kWhatStart = 'strt', kWhatScanSources = 'scan', kWhatVideoNotify = 'vidN', @@ -175,6 +177,7 @@ private: int32_t mVideoScalingMode; + float mPlaybackRate; bool mStarted; // Actual pause state, either as requested by client or due to buffering. diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index abfa4d3..5887e50 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -341,6 +341,11 @@ bool NuPlayerDriver::isPlaying() { return mState == STATE_RUNNING && !mAtEOS; } +status_t NuPlayerDriver::setPlaybackRate(float rate) { + mPlayer->setPlaybackRate(rate); + return OK; +} + status_t NuPlayerDriver::seekTo(int msec) { ALOGD("seekTo(%p) %d ms", this, msec); Mutex::Autolock autoLock(mLock); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 5cba7d9..e53abcd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -47,6 +47,7 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t stop(); virtual status_t pause(); virtual bool isPlaying(); + virtual status_t setPlaybackRate(float rate); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int *msec); virtual status_t getDuration(int *msec); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 7f8680d..d21884b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -67,6 +67,7 @@ NuPlayer::Renderer::Renderer( mVideoQueueGeneration(0), mAudioDrainGeneration(0), mVideoDrainGeneration(0), + mPlaybackRate(1.0), mAudioFirstAnchorTimeMediaUs(-1), mAnchorTimeMediaUs(-1), mAnchorNumFramesWritten(-1), @@ -121,6 +122,12 @@ void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) { msg->post(); } +void NuPlayer::Renderer::setPlaybackRate(float rate) { + sp msg = new AMessage(kWhatSetRate, id()); + msg->setFloat("rate", rate); + msg->post(); +} + void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { { Mutex::Autolock autoLock(mLock); @@ -172,12 +179,7 @@ void NuPlayer::Renderer::setVideoFrameRate(float fps) { // Called on any threads. status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) { - int64_t currentTimeUs = mMediaClock->getTimeMedia(ALooper::GetNowUs()); - if (currentTimeUs == -1) { - return NO_INIT; - } - *mediaUs = currentTimeUs; - return OK; + return mMediaClock->getMediaTime(ALooper::GetNowUs(), mediaUs); } void NuPlayer::Renderer::clearAudioFirstAnchorTime_l() { @@ -361,6 +363,16 @@ void NuPlayer::Renderer::onMessageReceived(const sp &msg) { break; } + case kWhatSetRate: + { + CHECK(msg->findFloat("rate", &mPlaybackRate)); + int32_t ratePermille = (int32_t)(0.5f + 1000 * mPlaybackRate); + mPlaybackRate = ratePermille / 1000.0f; + mMediaClock->setPlaybackRate(mPlaybackRate); + mAudioSink->setPlaybackRatePermille(ratePermille); + break; + } + case kWhatFlush: { onFlush(msg); @@ -541,10 +553,10 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { if (mAudioFirstAnchorTimeMediaUs >= 0) { int64_t nowUs = ALooper::GetNowUs(); + int64_t nowMediaUs = + mAudioFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs); // we don't know how much data we are queueing for offloaded tracks. - mMediaClock->updateAnchor(mAudioFirstAnchorTimeMediaUs, - nowUs - getPlayedOutAudioDurationUs(nowUs), - INT64_MAX); + mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX); } if (hasEOS) { @@ -670,21 +682,27 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { return !mAudioQueue.empty(); } +int64_t NuPlayer::Renderer::getDurationUsIfPlayedAtSampleRate(uint32_t numFrames) { + int32_t sampleRate = offloadingAudio() ? + mCurrentOffloadInfo.sample_rate : mCurrentPcmInfo.mSampleRate; + // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours. + return (int64_t)((int32_t)numFrames * 1000000LL / sampleRate); +} + +// Calculate duration of pending samples if played at normal rate (i.e., 1.0). int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) { - int64_t writtenAudioDurationUs = - mNumFramesWritten * 1000LL * mAudioSink->msecsPerFrame(); + int64_t writtenAudioDurationUs = getDurationUsIfPlayedAtSampleRate(mNumFramesWritten); return writtenAudioDurationUs - getPlayedOutAudioDurationUs(nowUs); } int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) { - int64_t currentPositionUs = - mMediaClock->getTimeMedia(nowUs, true /* allowPastMaxTime */); - if (currentPositionUs == -1) { + int64_t realUs; + if (mMediaClock->getRealTimeFor(mediaTimeUs, &realUs) != OK) { // If failed to get current position, e.g. due to audio clock is // not ready, then just play out video immediately without delay. return nowUs; } - return (mediaTimeUs - currentPositionUs) + nowUs; + return realUs; } void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs) { @@ -696,9 +714,8 @@ void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs) { } setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs); int64_t nowUs = ALooper::GetNowUs(); - mMediaClock->updateAnchor(mediaTimeUs, - nowUs + getPendingAudioPlayoutDurationUs(nowUs), - mediaTimeUs); + int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs); + mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs); mAnchorTimeMediaUs = mediaTimeUs; } @@ -828,9 +845,11 @@ void NuPlayer::Renderer::onDrainVideoQueue() { ALOGV("video late by %lld us (%.2f secs)", mVideoLateByUs, mVideoLateByUs / 1E6); } else { + int64_t mediaUs = 0; + mMediaClock->getMediaTime(realTimeUs, &mediaUs); ALOGV("rendering video at media time %.2f secs", (mFlags & FLAG_REAL_TIME ? realTimeUs : - mMediaClock->getTimeMedia(realTimeUs)) / 1E6); + mediaUs) / 1E6); } } else { setVideoLateByUs(0); @@ -1153,7 +1172,7 @@ void NuPlayer::Renderer::onPause() { ++mVideoDrainGeneration; prepareForMediaRenderingStart_l(); mPaused = true; - mMediaClock->pause(); + mMediaClock->setPlaybackRate(0.0); } mDrainAudioQueuePending = false; @@ -1181,7 +1200,7 @@ void NuPlayer::Renderer::onResume() { { Mutex::Autolock autoLock(mLock); mPaused = false; - mMediaClock->resume(); + mMediaClock->setPlaybackRate(mPlaybackRate); if (!mAudioQueue.empty()) { postDrainAudioQueue_l(); @@ -1222,6 +1241,7 @@ bool NuPlayer::Renderer::getSyncQueues() { // accessing getTimestamp() or getPosition() every time a data buffer with // a media time is received. // +// Calculate duration of played samples if played at normal rate (i.e., 1.0). int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { uint32_t numFramesPlayed; int64_t numFramesPlayedAt; @@ -1259,9 +1279,8 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt); } - // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours. //CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test - int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()) + int64_t durationUs = getDurationUsIfPlayedAtSampleRate(numFramesPlayed) + nowUs - numFramesPlayedAt; if (durationUs < 0) { // Occurs when numFramesPlayed position is very small and the following: @@ -1400,6 +1419,10 @@ status_t NuPlayer::Renderer::onOpenAudioSink( &offloadInfo); if (err == OK) { + if (mPlaybackRate != 1.0) { + mAudioSink->setPlaybackRatePermille( + (int32_t)(mPlaybackRate * 1000 + 0.5f)); + } // If the playback is offloaded to h/w, we pass // the HAL some metadata information. // We don't want to do this for PCM because it @@ -1455,6 +1478,10 @@ status_t NuPlayer::Renderer::onOpenAudioSink( return err; } mCurrentPcmInfo = info; + if (mPlaybackRate != 1.0) { + mAudioSink->setPlaybackRatePermille( + (int32_t)(mPlaybackRate * 1000 + 0.5f)); + } mAudioSink->start(); } if (audioSinkChanged) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index faf3b3f..38843d5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -48,6 +48,8 @@ struct NuPlayer::Renderer : public AHandler { void queueEOS(bool audio, status_t finalResult); + void setPlaybackRate(float rate); + void flush(bool audio, bool notifyComplete); void signalTimeDiscontinuity(); @@ -100,6 +102,7 @@ private: kWhatPostDrainVideoQueue = 'pDVQ', kWhatQueueBuffer = 'queB', kWhatQueueEOS = 'qEOS', + kWhatSetRate = 'setR', kWhatFlush = 'flus', kWhatPause = 'paus', kWhatResume = 'resm', @@ -138,6 +141,7 @@ private: int32_t mVideoDrainGeneration; sp mMediaClock; + float mPlaybackRate; int64_t mAudioFirstAnchorTimeMediaUs; int64_t mAnchorTimeMediaUs; int64_t mAnchorNumFramesWritten; @@ -243,6 +247,8 @@ private: void startAudioOffloadPauseTimeout(); void cancelAudioOffloadPauseTimeout(); + int64_t getDurationUsIfPlayedAtSampleRate(uint32_t numFrames); + DISALLOW_EVIL_CONSTRUCTORS(Renderer); }; -- cgit v1.1