summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp32
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp116
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h2
3 files changed, 117 insertions, 33 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 4e6b4d8..bcf8e99 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -270,7 +270,14 @@ status_t NuPlayerDriver::start() {
mPositionUs = -1;
} else {
mPlayer->resume();
- mPositionUs -= ALooper::GetNowUs() - mPauseStartedTimeUs;
+ if (mNotifyTimeRealUs != -1) {
+ // Pause time must be set if here by setPauseStartedTimeIfNeeded().
+ //CHECK(mPauseStartedTimeUs != -1);
+
+ // if no seek occurs, adjust our notify time so that getCurrentPosition()
+ // is continuous if read immediately after calling start().
+ mNotifyTimeRealUs += ALooper::GetNowUs() - mPauseStartedTimeUs;
+ }
}
break;
}
@@ -378,15 +385,36 @@ status_t NuPlayerDriver::getCurrentPosition(int *msec) {
Mutex::Autolock autoLock(mLock);
if (mPositionUs < 0) {
+ // mPositionUs is the media time.
+ // It is negative under these cases
+ // (1) == -1 after reset, or very first playback, no stream notification yet.
+ // (2) == -1 start after end of stream, no stream notification yet.
+ // (3) == large negative # after ~292,471 years of continuous playback.
+
+ //CHECK_EQ(mPositionUs, -1);
*msec = 0;
} else if (mNotifyTimeRealUs == -1) {
+ // A seek has occurred just occurred, no stream notification yet.
+ // mPositionUs (>= 0) is the new media position.
*msec = mPositionUs / 1000;
} else {
+ // mPosition must be valid (i.e. >= 0) by the first check above.
+ // We're either playing or have pause time set: mPauseStartedTimeUs is >= 0
+ //LOG_ALWAYS_FATAL_IF(
+ // !isPlaying() && mPauseStartedTimeUs < 0,
+ // "Player in non-playing mState(%d) and mPauseStartedTimeUs(%lld) < 0",
+ // mState, (long long)mPauseStartedTimeUs);
+ ALOG_ASSERT(mNotifyTimeRealUs >= 0);
int64_t nowUs =
(isPlaying() ? ALooper::GetNowUs() : mPauseStartedTimeUs);
*msec = (mPositionUs + nowUs - mNotifyTimeRealUs + 500ll) / 1000;
+ // It is possible for *msec to be negative if the media position is > 596 hours.
+ // but we turn on this checking in NDEBUG == 0 mode.
+ ALOG_ASSERT(*msec >= 0);
+ ALOGV("getCurrentPosition nowUs(%lld)", (long long)nowUs);
}
-
+ ALOGV("getCurrentPosition returning(%d) mPositionUs(%lld) mNotifyRealTimeUs(%lld)",
+ *msec, (long long)mPositionUs, (long long)mNotifyTimeRealUs);
return OK;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 8ab777a..7674616 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -45,7 +45,7 @@ NuPlayer::Renderer::Renderer(
mDrainVideoQueuePending(false),
mAudioQueueGeneration(0),
mVideoQueueGeneration(0),
- mFirstAudioTimeUs(-1),
+ mFirstAnchorTimeMediaUs(-1),
mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
mFlushingAudio(false),
@@ -54,12 +54,12 @@ NuPlayer::Renderer::Renderer(
mHasVideo(false),
mSyncQueues(false),
mPaused(false),
+ mVideoSampleReceived(false),
mVideoRenderingStarted(false),
mVideoRenderingStartGeneration(0),
mAudioRenderingStartGeneration(0),
mLastPositionUpdateUs(-1ll),
- mVideoLateByUs(0ll),
- mVideoSampleReceived(false) {
+ mVideoLateByUs(0ll) {
}
NuPlayer::Renderer::~Renderer() {
@@ -115,6 +115,7 @@ void NuPlayer::Renderer::signalTimeDiscontinuity() {
Mutex::Autolock autoLock(mLock);
// CHECK(mAudioQueue.empty());
// CHECK(mVideoQueue.empty());
+ mFirstAnchorTimeMediaUs = -1;
mAnchorTimeMediaUs = -1;
mAnchorTimeRealUs = -1;
mSyncQueues = false;
@@ -339,17 +340,16 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
- if (mFirstAudioTimeUs == -1) {
- mFirstAudioTimeUs = mediaTimeUs;
+ if (mFirstAnchorTimeMediaUs == -1) {
+ mFirstAnchorTimeMediaUs = mediaTimeUs;
}
- // TODO: figure out how to calculate initial latency if
- // getTimestamp is not available. Otherwise, the initial time
- // is not correct till the first sample is played.
int64_t nowUs = ALooper::GetNowUs();
mAnchorTimeMediaUs =
- mFirstAudioTimeUs + getPlayedOutAudioDurationUs(nowUs);
+ mFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs);
mAnchorTimeRealUs = nowUs;
+
+ notifyPosition();
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -372,10 +372,6 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
notifyIfMediaRenderingStarted();
}
- if (sizeCopied != 0) {
- notifyPosition();
- }
-
if (hasEOS) {
(new AMessage(kWhatStopAudioSink, id()))->post();
}
@@ -424,10 +420,15 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+ if (mFirstAnchorTimeMediaUs == -1) {
+ mFirstAnchorTimeMediaUs = mediaTimeUs;
+ }
mAnchorTimeMediaUs = mediaTimeUs;
int64_t nowUs = ALooper::GetNowUs();
mAnchorTimeRealUs = nowUs + getPendingAudioPlayoutDurationUs(nowUs);
+
+ notifyPosition();
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -476,8 +477,6 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
break;
}
}
- notifyPosition();
-
return !mAudioQueue.empty();
}
@@ -517,12 +516,16 @@ void NuPlayer::Renderer::postDrainVideoQueue() {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+ if (mFirstAnchorTimeMediaUs == -1 && !mHasAudio) {
+ mFirstAnchorTimeMediaUs = mediaTimeUs;
+ }
if (mAnchorTimeMediaUs < 0) {
delayUs = 0;
if (!mHasAudio) {
mAnchorTimeMediaUs = mediaTimeUs;
mAnchorTimeRealUs = ALooper::GetNowUs();
+ notifyPosition();
}
} else {
int64_t realTimeUs =
@@ -554,8 +557,6 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
entry = NULL;
mVideoLateByUs = 0ll;
-
- notifyPosition();
return;
}
@@ -601,8 +602,6 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
}
notifyIfMediaRenderingStarted();
}
-
- notifyPosition();
}
void NuPlayer::Renderer::notifyVideoRenderingStart() {
@@ -779,7 +778,7 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
prepareForMediaRenderingStart();
if (offloadingAudio()) {
- mFirstAudioTimeUs = -1;
+ mFirstAnchorTimeMediaUs = -1;
}
}
@@ -867,9 +866,11 @@ void NuPlayer::Renderer::onDisableOffloadAudio() {
}
void NuPlayer::Renderer::notifyPosition() {
- if (mAnchorTimeRealUs < 0 || mAnchorTimeMediaUs < 0) {
- return;
- }
+ // notifyPosition() must be called only after setting mAnchorTimeRealUs
+ // and mAnchorTimeMediaUs, and must not be paused as it extrapolates position.
+ //CHECK_GE(mAnchorTimeRealUs, 0);
+ //CHECK_GE(mAnchorTimeMediaUs, 0);
+ //CHECK(!mPaused || !mHasAudio); // video-only does display in paused mode.
int64_t nowUs = ALooper::GetNowUs();
@@ -881,6 +882,18 @@ void NuPlayer::Renderer::notifyPosition() {
int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs;
+ //ALOGD("notifyPosition: positionUs(%lld) nowUs(%lld) mAnchorTimeRealUs(%lld)"
+ // " mAnchorTimeMediaUs(%lld) mFirstAnchorTimeMediaUs(%lld)",
+ // (long long)positionUs, (long long)nowUs, (long long)mAnchorTimeRealUs,
+ // (long long)mAnchorTimeMediaUs, (long long)mFirstAnchorTimeMediaUs);
+
+ // Due to adding the latency to mAnchorTimeRealUs in onDrainAudioQueue(),
+ // positionUs may be less than the first media time. This is avoided
+ // here to prevent potential retrograde motion of the position bar
+ // when starting up after a seek.
+ if (positionUs < mFirstAnchorTimeMediaUs) {
+ positionUs = mFirstAnchorTimeMediaUs;
+ }
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatPosition);
notify->setInt64("positionUs", positionUs);
@@ -933,33 +946,76 @@ void NuPlayer::Renderer::onResume() {
}
}
+// TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs()
+// as it acquires locks and may query the audio driver.
+//
+// Some calls are not needed since notifyPosition() doesn't always deliver a message.
+// Some calls could conceivably retrieve extrapolated data instead of
+// accessing getTimestamp() or getPosition() every time a data buffer with
+// a media time is received.
+//
int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) {
- // FIXME: getTimestamp sometimes returns negative frame count.
- // Since we do not handle the rollover at this point (which can
- // happen every 14 hours), simply treat the timestamp as signed.
uint32_t numFramesPlayed;
int64_t numFramesPlayedAt;
AudioTimestamp ts;
+ static const int64_t kStaleTimestamp100ms = 100000;
+
status_t res = mAudioSink->getTimestamp(ts);
- if (res == OK) {
+ if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
numFramesPlayed = ts.mPosition;
numFramesPlayedAt =
ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
- } else {
+ const int64_t timestampAge = nowUs - numFramesPlayedAt;
+ if (timestampAge > kStaleTimestamp100ms) {
+ // This is an audio FIXME.
+ // getTimestamp returns a timestamp which may come from audio mixing threads.
+ // After pausing, the MixerThread may go idle, thus the mTime estimate may
+ // become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms,
+ // the max latency should be about 25ms with an average around 12ms (to be verified).
+ // For safety we use 100ms.
+ ALOGW("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
+ (long long)nowUs, (long long)numFramesPlayedAt);
+ numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
+ }
+ //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAt);
+ } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
+ numFramesPlayed = 0;
+ numFramesPlayedAt = nowUs;
+ //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
+ // numFramesPlayed, (long long)numFramesPlayedAt);
+ } else { // case 3: transitory at new track or audio fast tracks.
res = mAudioSink->getPosition(&numFramesPlayed);
CHECK_EQ(res, (status_t)OK);
numFramesPlayedAt = nowUs;
numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */
+ //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt);
}
- return (int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()
+
+ // 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 = (int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()
+ nowUs - numFramesPlayedAt;
+ if (durationUs < 0) {
+ // Occurs when numFramesPlayed position is very small and the following:
+ // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
+ // numFramesPlayedAt is greater than nowUs by time more than numFramesPlayed.
+ // (2) In case 3, using getPosition and adding mAudioSink->latency() to
+ // numFramesPlayedAt, by a time amount greater than numFramesPlayed.
+ //
+ // Both of these are transitory conditions.
+ ALOGW("getPlayedOutAudioDurationUs: negative timestamp %lld set to zero", (long long)durationUs);
+ durationUs = 0;
+ }
+ ALOGV("getPlayedOutAudioDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
+ (long long)durationUs, (long long)nowUs, numFramesPlayed, (long long)numFramesPlayedAt);
+ return durationUs;
}
void NuPlayer::Renderer::onAudioOffloadTearDown() {
int64_t firstAudioTimeUs;
{
Mutex::Autolock autoLock(mLock);
- firstAudioTimeUs = mFirstAudioTimeUs;
+ firstAudioTimeUs = mFirstAnchorTimeMediaUs;
}
int64_t currentPositionUs =
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 2b32c48..97fdae7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -106,7 +106,7 @@ private:
int32_t mAudioQueueGeneration;
int32_t mVideoQueueGeneration;
- int64_t mFirstAudioTimeUs;
+ int64_t mFirstAnchorTimeMediaUs;
int64_t mAnchorTimeMediaUs;
int64_t mAnchorTimeRealUs;