Merge "PlaylistFetcher:don't signal a/v eos on subttitle eos" into lmp-dev
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 72e51f9..b5256f0 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -580,7 +580,14 @@
* Caution: calling this method too often may be inefficient;
* if you need a high resolution mapping between frame position and presentation time,
* consider implementing that at application level, based on the low resolution timestamps.
- * Returns NO_ERROR if timestamp is valid.
+ * Returns NO_ERROR if timestamp is valid.
+ * WOULD_BLOCK if called in STOPPED or FLUSHED state, or if called immediately after
+ * start/ACTIVE, when the number of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * INVALID_OPERATION if called on a FastTrack, wrong state, or some other error.
+ *
* The timestamp parameter is undefined on return, if status is not NO_ERROR.
*/
status_t getTimestamp(AudioTimestamp& timestamp);
@@ -747,6 +754,8 @@
// reset by stop() but continues monotonically
// after new IAudioTrack to restore mPosition,
// and could be easily widened to uint64_t
+ int64_t mStartUs; // the start time after flush or stop.
+ // only used for offloaded and direct tracks.
audio_output_flags_t mFlags;
// const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 87717da..cf18a45 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -27,6 +27,7 @@
#include <media/mediaplayer.h>
#include <media/AudioSystem.h>
+#include <media/AudioTimestamp.h>
#include <media/Metadata.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -97,6 +98,7 @@
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
virtual status_t getPosition(uint32_t *position) const = 0;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual int getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index ff7da83..762dca5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -37,6 +37,19 @@
namespace android {
// ---------------------------------------------------------------------------
+static int64_t convertTimespecToUs(const struct timespec &tv)
+{
+ return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
+}
+
+// current monotonic time in microseconds.
+static int64_t getNowUs()
+{
+ struct timespec tv;
+ (void) clock_gettime(CLOCK_MONOTONIC, &tv);
+ return convertTimespecToUs(tv);
+}
+
// static
status_t AudioTrack::getMinFrameCount(
size_t* frameCount,
@@ -420,6 +433,7 @@
mServer = 0;
mPosition = 0;
mReleased = 0;
+ mStartUs = 0;
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
@@ -451,6 +465,12 @@
// reset current position as seen by client to 0
mPosition = 0;
mReleased = 0;
+ // For offloaded tracks, we don't know if the hardware counters are really zero here,
+ // since the flush is asynchronous and stop may not fully drain.
+ // We save the time when the track is started to later verify whether
+ // the counters are realistic (i.e. start from zero after this time).
+ mStartUs = getNowUs();
+
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
mRefreshRemaining = true;
@@ -587,9 +607,18 @@
if (isOffloaded_l()) {
if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ // An offload output can be re-used between two audio tracks having
+ // the same configuration. A timestamp query for a paused track
+ // while the other is running would return an incorrect time.
+ // To fix this, cache the playback position on a pause() and return
+ // this time when requested until the track is resumed.
+
+ // OffloadThread sends HAL pause in its threadLoop. Time saved
+ // here can be slightly off.
+
+ // TODO: check return code for getRenderPosition.
+
uint32_t halFrames;
- // OffloadThread sends HAL pause in its threadLoop.. time saved
- // here can be slightly off
AudioSystem::getRenderPosition(mOutput, &halFrames, &mPausedPosition);
ALOGV("AudioTrack::pause for offload, cache current position %u", mPausedPosition);
}
@@ -825,6 +854,8 @@
uint32_t halFrames;
AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames);
}
+ // FIXME: dspFrames may not be zero in (mState == STATE_STOPPED || mState == STATE_FLUSHED)
+ // due to hardware latency. We leave this behavior for now.
*position = dspFrames;
} else {
// IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
@@ -1881,13 +1912,70 @@
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
return INVALID_OPERATION;
}
- if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
- return INVALID_OPERATION;
+
+ switch (mState) {
+ case STATE_ACTIVE:
+ case STATE_PAUSED:
+ break; // handle below
+ case STATE_FLUSHED:
+ case STATE_STOPPED:
+ return WOULD_BLOCK;
+ case STATE_STOPPING:
+ case STATE_PAUSED_STOPPING:
+ if (!isOffloaded_l()) {
+ return INVALID_OPERATION;
+ }
+ break; // offloaded tracks handled below
+ default:
+ LOG_ALWAYS_FATAL("Invalid mState in getTimestamp(): %d", mState);
+ break;
}
+
// The presented frame count must always lag behind the consumed frame count.
// To avoid a race, read the presented frames first. This ensures that presented <= consumed.
status_t status = mAudioTrack->getTimestamp(timestamp);
- if (status == NO_ERROR) {
+ if (status != NO_ERROR) {
+ ALOGW_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status);
+ return status;
+ }
+ if (isOffloadedOrDirect_l()) {
+ if (isOffloaded_l() && (mState == STATE_PAUSED || mState == STATE_PAUSED_STOPPING)) {
+ // use cached paused position in case another offloaded track is running.
+ timestamp.mPosition = mPausedPosition;
+ clock_gettime(CLOCK_MONOTONIC, ×tamp.mTime);
+ return NO_ERROR;
+ }
+
+ // Check whether a pending flush or stop has completed, as those commands may
+ // be asynchronous or return near finish.
+ if (mStartUs != 0 && mSampleRate != 0) {
+ static const int kTimeJitterUs = 100000; // 100 ms
+ static const int k1SecUs = 1000000;
+
+ const int64_t timeNow = getNowUs();
+
+ if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+ const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
+ if (timestampTimeUs < mStartUs) {
+ return WOULD_BLOCK; // stale timestamp time, occurs before start.
+ }
+ const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+ const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
+
+ if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
+ // Verify that the counter can't count faster than the sample rate
+ // since the start time. If greater, then that means we have failed
+ // to completely flush or stop the previous playing track.
+ ALOGW("incomplete flush or stop:"
+ " deltaTimeUs(%lld) deltaPositionUs(%lld) tsmPosition(%u)",
+ (long long)deltaTimeUs, (long long)deltaPositionByUs,
+ timestamp.mPosition);
+ return WOULD_BLOCK;
+ }
+ }
+ mStartUs = 0; // no need to check again, start timestamp has either expired or unneeded.
+ }
+ } else {
// Update the mapping between local consumed (mPosition) and server consumed (mServer)
(void) updateAndGetPosition_l();
// Server consumed (mServer) and presented both use the same server time base,
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index c8cb7ed..8eb1269 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -43,6 +43,7 @@
#include <utils/Errors.h> // for status_t
#include <utils/String8.h>
#include <utils/SystemClock.h>
+#include <utils/Timers.h>
#include <utils/Vector.h>
#include <media/IMediaHTTPService.h>
@@ -1496,6 +1497,12 @@
return mTrack->getPosition(position);
}
+status_t MediaPlayerService::AudioOutput::getTimestamp(AudioTimestamp &ts) const
+{
+ if (mTrack == 0) return NO_INIT;
+ return mTrack->getTimestamp(ts);
+}
+
status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
{
if (mTrack == 0) return NO_INIT;
@@ -1971,6 +1978,15 @@
return NO_ERROR;
}
+status_t MediaPlayerService::AudioCache::getTimestamp(AudioTimestamp &ts) const
+{
+ ts.mPosition = mSize / mFrameSize;
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ ts.mTime.tv_sec = now / 1000000000LL;
+ ts.mTime.tv_nsec = now - (1000000000LL * ts.mTime.tv_sec);
+ return NO_ERROR;
+}
+
status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const
{
if (written == 0) return BAD_VALUE;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 4fe7075..3b96e88 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -85,6 +85,7 @@
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position) const;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual int getSessionId() const;
virtual uint32_t getSampleRate() const;
@@ -198,6 +199,7 @@
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position) const;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual int getSessionId() const;
virtual uint32_t getSampleRate() const;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index bd67cbd..142107d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -106,6 +106,10 @@
return OK;
}
+sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const {
+ return mFileMeta;
+}
+
status_t NuPlayer::GenericSource::initFromDataSource() {
sp<MediaExtractor> extractor;
@@ -144,10 +148,10 @@
checkDrmStatus(mDataSource);
}
- sp<MetaData> fileMeta = extractor->getMetaData();
- if (fileMeta != NULL) {
+ mFileMeta = extractor->getMetaData();
+ if (mFileMeta != NULL) {
int64_t duration;
- if (fileMeta->findInt64(kKeyDuration, &duration)) {
+ if (mFileMeta->findInt64(kKeyDuration, &duration)) {
mDurationUs = duration;
}
}
@@ -1256,6 +1260,8 @@
sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs);
track->mPackets->queueAccessUnit(buffer);
+ formatChange = false;
+ seeking = false;
++numBuffers;
} else if (err == WOULD_BLOCK) {
break;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 454edeb..24bb6af 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -59,6 +59,8 @@
virtual status_t feedMoreTSData();
+ virtual sp<MetaData> getFileFormatMeta() const;
+
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
virtual status_t getDuration(int64_t *durationUs);
@@ -125,6 +127,7 @@
sp<DataSource> mDataSource;
sp<NuCachedSource2> mCachedSource;
sp<WVMExtractor> mWVMExtractor;
+ sp<MetaData> mFileMeta;
DrmManagerClient *mDrmManagerClient;
sp<DecryptHandle> mDecryptHandle;
bool mStarted;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 7c6d576..5321deb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1730,6 +1730,10 @@
return err;
}
+sp<MetaData> NuPlayer::getFileMeta() {
+ return mSource->getFileFormatMeta();
+}
+
void NuPlayer::schedulePollDuration() {
sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
msg->setInt32("generation", mPollDurationGeneration);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 2e951bd..7197e5f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -67,6 +67,8 @@
status_t getSelectedTrack(int32_t type, Parcel* reply) const;
status_t selectTrack(size_t trackIndex, bool select);
+ sp<MetaData> getFileMeta();
+
static const size_t kAggregateBufferSizeBytes;
protected:
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 4e6b4d8..7ec9876 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -45,6 +45,7 @@
mPlayerFlags(0),
mAtEOS(false),
mLooping(false),
+ mAutoLoop(false),
mStartupSeekTimeUs(-1) {
mLooper->setName("NuPlayerDriver Looper");
@@ -270,7 +271,14 @@
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 +386,36 @@
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;
}
@@ -505,6 +534,7 @@
void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
mPlayer->setAudioSink(audioSink);
+ mAudioSink = audioSink;
}
status_t NuPlayerDriver::setParameter(
@@ -634,7 +664,8 @@
case MEDIA_PLAYBACK_COMPLETE:
{
if (mState != STATE_RESET_IN_PROGRESS) {
- if (mLooping) {
+ if (mLooping || (mAutoLoop
+ && (mAudioSink == NULL || mAudioSink->realtime()))) {
mPlayer->seekToAsync(0);
break;
}
@@ -700,6 +731,13 @@
}
}
+ sp<MetaData> meta = mPlayer->getFileMeta();
+ int32_t loop;
+ if (meta != NULL
+ && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
+ mAutoLoop = true;
+ }
+
mCondition.broadcast();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index e81d605..f2bd431 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -114,10 +114,12 @@
sp<ALooper> mLooper;
sp<NuPlayer> mPlayer;
+ sp<AudioSink> mAudioSink;
uint32_t mPlayerFlags;
bool mAtEOS;
bool mLooping;
+ bool mAutoLoop;
int64_t mStartupSeekTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 067784b..7674616 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -45,7 +45,7 @@
mDrainVideoQueuePending(false),
mAudioQueueGeneration(0),
mVideoQueueGeneration(0),
- mFirstAudioTimeUs(-1),
+ mFirstAnchorTimeMediaUs(-1),
mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
mFlushingAudio(false),
@@ -54,12 +54,12 @@
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 @@
Mutex::Autolock autoLock(mLock);
// CHECK(mAudioQueue.empty());
// CHECK(mVideoQueue.empty());
+ mFirstAnchorTimeMediaUs = -1;
mAnchorTimeMediaUs = -1;
mAnchorTimeRealUs = -1;
mSyncQueues = false;
@@ -339,19 +340,16 @@
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;
}
- uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+ int64_t nowUs = ALooper::GetNowUs();
+ mAnchorTimeMediaUs =
+ mFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs);
+ mAnchorTimeRealUs = nowUs;
- // TODO: figure out how to calculate initial latency.
- // Otherwise, the initial time is not correct till the first sample
- // is played.
- mAnchorTimeMediaUs = mFirstAudioTimeUs
- + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
- mAnchorTimeRealUs = ALooper::GetNowUs();
+ notifyPosition();
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -374,10 +372,6 @@
notifyIfMediaRenderingStarted();
}
- if (sizeCopied != 0) {
- notifyPosition();
- }
-
if (hasEOS) {
(new AMessage(kWhatStopAudioSink, id()))->post();
}
@@ -413,7 +407,7 @@
// EOS
int64_t postEOSDelayUs = 0;
if (mAudioSink->needsTrailingPadding()) {
- postEOSDelayUs = getAudioPendingPlayoutUs() + 1000 * mAudioSink->latency();
+ postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
}
notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs);
@@ -426,10 +420,15 @@
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;
- mAnchorTimeRealUs = ALooper::GetNowUs()
- + getAudioPendingPlayoutUs() + 1000 * mAudioSink->latency() / 2;
+ int64_t nowUs = ALooper::GetNowUs();
+ mAnchorTimeRealUs = nowUs + getPendingAudioPlayoutDurationUs(nowUs);
+
+ notifyPosition();
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -478,17 +477,13 @@
break;
}
}
- notifyPosition();
-
return !mAudioQueue.empty();
}
-int64_t NuPlayer::Renderer::getAudioPendingPlayoutUs() {
- uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
-
- uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed;
- return numFramesPendingPlayout * mAudioSink->msecsPerFrame() * 1000;
+int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) {
+ int64_t writtenAudioDurationUs =
+ mNumFramesWritten * 1000LL * mAudioSink->msecsPerFrame();
+ return writtenAudioDurationUs - getPlayedOutAudioDurationUs(nowUs);
}
void NuPlayer::Renderer::postDrainVideoQueue() {
@@ -521,12 +516,16 @@
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 =
@@ -558,8 +557,6 @@
entry = NULL;
mVideoLateByUs = 0ll;
-
- notifyPosition();
return;
}
@@ -605,8 +602,6 @@
}
notifyIfMediaRenderingStarted();
}
-
- notifyPosition();
}
void NuPlayer::Renderer::notifyVideoRenderingStart() {
@@ -783,7 +778,7 @@
prepareForMediaRenderingStart();
if (offloadingAudio()) {
- mFirstAudioTimeUs = -1;
+ mFirstAnchorTimeMediaUs = -1;
}
}
@@ -871,9 +866,11 @@
}
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();
@@ -885,6 +882,18 @@
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);
@@ -937,17 +946,80 @@
}
}
-void NuPlayer::Renderer::onAudioOffloadTearDown() {
+// 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) {
uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+ int64_t numFramesPlayedAt;
+ AudioTimestamp ts;
+ static const int64_t kStaleTimestamp100ms = 100000;
+ status_t res = mAudioSink->getTimestamp(ts);
+ 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;
+ 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);
+ }
+
+ // 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 = firstAudioTimeUs
- + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
+
+ int64_t currentPositionUs =
+ firstAudioTimeUs + getPlayedOutAudioDurationUs(ALooper::GetNowUs());
mAudioSink->stop();
mAudioSink->flush();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 5c7d2d7..97fdae7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -106,7 +106,7 @@
int32_t mAudioQueueGeneration;
int32_t mVideoQueueGeneration;
- int64_t mFirstAudioTimeUs;
+ int64_t mFirstAnchorTimeMediaUs;
int64_t mAnchorTimeMediaUs;
int64_t mAnchorTimeRealUs;
@@ -130,7 +130,8 @@
size_t fillAudioBuffer(void *buffer, size_t size);
bool onDrainAudioQueue();
- int64_t getAudioPendingPlayoutUs();
+ int64_t getPendingAudioPlayoutDurationUs(int64_t nowUs);
+ int64_t getPlayedOutAudioDurationUs(int64_t nowUs);
void postDrainAudioQueue_l(int64_t delayUs = 0);
void onDrainVideoQueue();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 7d994fa..2f06c31 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -76,6 +76,7 @@
virtual sp<AMessage> getFormat(bool audio);
virtual sp<MetaData> getFormatMeta(bool /* audio */) { return NULL; }
+ virtual sp<MetaData> getFileFormatMeta() const { return NULL; }
virtual status_t dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit) = 0;
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 4569c1c..456be89 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -705,7 +705,9 @@
}
// Discard input buffer.
- inHeader->nFilledLen = 0;
+ if (inHeader) {
+ inHeader->nFilledLen = 0;
+ }
aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
@@ -736,7 +738,7 @@
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
- if (inHeader->nFilledLen == 0) {
+ if (inHeader && inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
mInputBufferCount++;
inQueue.erase(inQueue.begin());
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3d17c89..818bb05 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3991,8 +3991,6 @@
minFrames = 1;
}
- ALOGI("prepareTracks_l minFrames %d state %d frames ready %d, ",
- minFrames, track->mState, track->framesReady());
if ((track->framesReady() >= minFrames) && track->isReady() && !track->isPaused() &&
!track->isStopping_2() && !track->isStopped())
{
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index d5f6c1e..95ac070 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2667,59 +2667,69 @@
continue;
}
+ if ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ continue;
+ }
audio_devices_t profileType = outProfile->mSupportedDevices.types();
if ((profileType & mDefaultOutputDevice->mDeviceType) != AUDIO_DEVICE_NONE) {
profileType = mDefaultOutputDevice->mDeviceType;
} else {
- profileType = outProfile->mSupportedDevices[0]->mDeviceType;
- }
- if ((profileType & outputDeviceTypes) &&
- ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
-
- outputDesc->mDevice = profileType;
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = outputDesc->mSamplingRate;
- config.channel_mask = outputDesc->mChannelMask;
- config.format = outputDesc->mFormat;
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,
- &output,
- &config,
- &outputDesc->mDevice,
- String8(""),
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- if (status != NO_ERROR) {
- ALOGW("Cannot open output stream for device %08x on hw module %s",
- outputDesc->mDevice,
- mHwModules[i]->mName);
- } else {
- outputDesc->mSamplingRate = config.sample_rate;
- outputDesc->mChannelMask = config.channel_mask;
- outputDesc->mFormat = config.format;
-
- for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) {
- audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType;
- ssize_t index =
- mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]);
- // give a valid ID to an attached device once confirmed it is reachable
- if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
- mAvailableOutputDevices[index]->mId = nextUniqueId();
- mAvailableOutputDevices[index]->mModule = mHwModules[i];
- }
+ // chose first device present in mSupportedDevices also part of
+ // outputDeviceTypes
+ for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) {
+ profileType = outProfile->mSupportedDevices[k]->mDeviceType;
+ if ((profileType & outputDeviceTypes) != 0) {
+ break;
}
- if (mPrimaryOutput == 0 &&
- outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
- mPrimaryOutput = output;
- }
- addOutput(output, outputDesc);
- setOutputDevice(output,
- outputDesc->mDevice,
- true);
}
}
+ if ((profileType & outputDeviceTypes) == 0) {
+ continue;
+ }
+ sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
+
+ outputDesc->mDevice = profileType;
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.sample_rate = outputDesc->mSamplingRate;
+ config.channel_mask = outputDesc->mChannelMask;
+ config.format = outputDesc->mFormat;
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,
+ &output,
+ &config,
+ &outputDesc->mDevice,
+ String8(""),
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ if (status != NO_ERROR) {
+ ALOGW("Cannot open output stream for device %08x on hw module %s",
+ outputDesc->mDevice,
+ mHwModules[i]->mName);
+ } else {
+ outputDesc->mSamplingRate = config.sample_rate;
+ outputDesc->mChannelMask = config.channel_mask;
+ outputDesc->mFormat = config.format;
+
+ for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) {
+ audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType;
+ ssize_t index =
+ mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]);
+ // give a valid ID to an attached device once confirmed it is reachable
+ if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
+ mAvailableOutputDevices[index]->mId = nextUniqueId();
+ mAvailableOutputDevices[index]->mModule = mHwModules[i];
+ }
+ }
+ if (mPrimaryOutput == 0 &&
+ outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
+ mPrimaryOutput = output;
+ }
+ addOutput(output, outputDesc);
+ setOutputDevice(output,
+ outputDesc->mDevice,
+ true);
+ }
}
// open input streams needed to access attached devices to validate
// mAvailableInputDevices list
@@ -2731,45 +2741,53 @@
ALOGW("Input profile contains no device on module %s", mHwModules[i]->mName);
continue;
}
-
- audio_devices_t profileType = inProfile->mSupportedDevices[0]->mDeviceType;
- if (profileType & inputDeviceTypes) {
- sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile);
-
- inputDesc->mInputSource = AUDIO_SOURCE_MIC;
- inputDesc->mDevice = profileType;
-
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = inputDesc->mSamplingRate;
- config.channel_mask = inputDesc->mChannelMask;
- config.format = inputDesc->mFormat;
- audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle,
- &input,
- &config,
- &inputDesc->mDevice,
- String8(""),
- AUDIO_SOURCE_MIC,
- AUDIO_INPUT_FLAG_NONE);
-
- if (status == NO_ERROR) {
- for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) {
- audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType;
- ssize_t index =
- mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]);
- // give a valid ID to an attached device once confirmed it is reachable
- if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
- mAvailableInputDevices[index]->mId = nextUniqueId();
- mAvailableInputDevices[index]->mModule = mHwModules[i];
- }
- }
- mpClientInterface->closeInput(input);
- } else {
- ALOGW("Cannot open input stream for device %08x on hw module %s",
- inputDesc->mDevice,
- mHwModules[i]->mName);
+ // chose first device present in mSupportedDevices also part of
+ // inputDeviceTypes
+ audio_devices_t profileType = AUDIO_DEVICE_NONE;
+ for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) {
+ profileType = inProfile->mSupportedDevices[k]->mDeviceType;
+ if (profileType & inputDeviceTypes) {
+ break;
}
}
+ if ((profileType & inputDeviceTypes) == 0) {
+ continue;
+ }
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile);
+
+ inputDesc->mInputSource = AUDIO_SOURCE_MIC;
+ inputDesc->mDevice = profileType;
+
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.sample_rate = inputDesc->mSamplingRate;
+ config.channel_mask = inputDesc->mChannelMask;
+ config.format = inputDesc->mFormat;
+ audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+ status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle,
+ &input,
+ &config,
+ &inputDesc->mDevice,
+ String8(""),
+ AUDIO_SOURCE_MIC,
+ AUDIO_INPUT_FLAG_NONE);
+
+ if (status == NO_ERROR) {
+ for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) {
+ audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType;
+ ssize_t index =
+ mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]);
+ // give a valid ID to an attached device once confirmed it is reachable
+ if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
+ mAvailableInputDevices[index]->mId = nextUniqueId();
+ mAvailableInputDevices[index]->mModule = mHwModules[i];
+ }
+ }
+ mpClientInterface->closeInput(input);
+ } else {
+ ALOGW("Cannot open input stream for device %08x on hw module %s",
+ inputDesc->mDevice,
+ mHwModules[i]->mName);
+ }
}
}
// make sure all attached devices have been allocated a unique ID