Implement server side playback timestamps with 64 bit accuracy
Provide server timestamps if the HAL doesn't provide it.
Provide monotonic - boottime translation.
Integrate record timestamps and playback timestamps together.
Bug: 17472992
Bug: 22871200
Bug: 26400089
Bug: 26682703
Change-Id: If1974f94232fcce7ba0bbcdf63d9e54ed51918ff
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 536581c..a67693f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -362,7 +362,6 @@
mAuxEffectId(0), mHasVolumeController(false),
mPresentationCompleteFrames(0),
mFrameMap(16 /* sink-frame-to-track-frame map memory */),
- mSinkTimestampValid(false),
// mSinkTimestamp
mFastIndex(-1),
mCachedVolume(1.0),
@@ -591,23 +590,18 @@
return mAudioTrackServerProxy->framesReady();
}
-size_t AudioFlinger::PlaybackThread::Track::framesReleased() const
+int64_t AudioFlinger::PlaybackThread::Track::framesReleased() const
{
return mAudioTrackServerProxy->framesReleased();
}
-void AudioFlinger::PlaybackThread::Track::onTimestamp(const AudioTimestamp ×tamp)
+void AudioFlinger::PlaybackThread::Track::onTimestamp(const ExtendedTimestamp ×tamp)
{
// This call comes from a FastTrack and should be kept lockless.
// The server side frames are already translated to client frames.
+ mAudioTrackServerProxy->setTimestamp(timestamp);
- ExtendedTimestamp ets;
- ets.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
- timestamp.mTime.tv_sec * 1000000000LL + timestamp.mTime.tv_nsec;
- ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = timestamp.mPosition;
-
- // Caution, this doesn't set the timebase for BOOTTIME properly, but is ignored right now.
- mAudioTrackServerProxy->setTimestamp(ets);
+ // We do not set drained here, as FastTrack timestamp may not go to very last frame.
}
// Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -872,9 +866,8 @@
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
- // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
- if (isFastTrack()) {
- return INVALID_OPERATION;
+ if (!isOffloaded() && !isDirect()) {
+ return INVALID_OPERATION; // normal tracks handled through SSQ
}
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
@@ -883,33 +876,7 @@
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
-
- if (isOffloaded() || isDirect()) {
- return playbackThread->getTimestamp_l(timestamp);
- }
-
- if (!mFrameMap.hasData()) {
- // WOULD_BLOCK is consistent with AudioTrack::getTimestamp() in the
- // FLUSHED and STOPPED state. We should only return INVALID_OPERATION
- // when this method is not permitted due to configuration or device.
- return WOULD_BLOCK;
- }
- status_t result = OK;
- if (!mSinkTimestampValid) { // if no sink position, try to fetch again
- result = playbackThread->getTimestamp_l(mSinkTimestamp);
- }
-
- if (result == OK) {
- // Lookup the track frame corresponding to the sink frame position.
- timestamp.mPosition = mFrameMap.findX(mSinkTimestamp.mPosition);
- timestamp.mTime = mSinkTimestamp.mTime;
- // ALOGD("track (server-side) timestamp: mPosition(%u) mTime(%llu)",
- // timestamp.mPosition, TIME_TO_NANOS(timestamp.mTime));
- }
- // (Possible) FIXME: mSinkTimestamp is updated only when the track is on
- // the Thread active list. If the track is no longer on the thread active
- // list should we use current time?
- return result;
+ return playbackThread->getTimestamp_l(timestamp);
}
status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
@@ -972,9 +939,12 @@
mAuxBuffer = buffer;
}
-bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWritten,
- size_t audioHalFrames)
+bool AudioFlinger::PlaybackThread::Track::presentationComplete(
+ int64_t framesWritten, size_t audioHalFrames)
{
+ // TODO: improve this based on FrameMap if it exists, to ensure full drain.
+ // This assists in proper timestamp computation as well as wakelock management.
+
// a track is considered presented when the total number of frames written to audio HAL
// corresponds to the number of frames written when presentationComplete() is called for the
// first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time.
@@ -982,15 +952,17 @@
// to detect when all frames have been played. In this case framesWritten isn't
// useful because it doesn't always reflect whether there is data in the h/w
// buffers, particularly if a track has been paused and resumed during draining
- ALOGV("presentationComplete() mPresentationCompleteFrames %d framesWritten %d",
- mPresentationCompleteFrames, framesWritten);
+ ALOGV("presentationComplete() mPresentationCompleteFrames %lld framesWritten %lld",
+ (long long)mPresentationCompleteFrames, (long long)framesWritten);
if (mPresentationCompleteFrames == 0) {
mPresentationCompleteFrames = framesWritten + audioHalFrames;
- ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d",
- mPresentationCompleteFrames, audioHalFrames);
+ ALOGV("presentationComplete() reset: mPresentationCompleteFrames %lld audioHalFrames %zu",
+ (long long)mPresentationCompleteFrames, audioHalFrames);
}
- if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) {
+ if ((!isOffloaded() && !isDirect() && !isFastTrack()
+ && framesWritten >= mPresentationCompleteFrames
+ && mAudioTrackServerProxy->isDrained()) || isOffloaded()) {
triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
mAudioTrackServerProxy->setStreamEndDone();
return true;
@@ -1101,14 +1073,34 @@
//To be called with thread lock held
void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
- uint32_t trackFramesReleased, uint32_t sinkFramesWritten, AudioTimestamp *timeStamp) {
+ int64_t trackFramesReleased, int64_t sinkFramesWritten,
+ const ExtendedTimestamp &timeStamp) {
+ //update frame map
mFrameMap.push(trackFramesReleased, sinkFramesWritten);
- if (timeStamp == NULL) {
- mSinkTimestampValid = false;
- } else {
- mSinkTimestampValid = true;
- mSinkTimestamp = *timeStamp;
+
+ // adjust server times and set drained state.
+ //
+ // Our timestamps are only updated when the track is on the Thread active list.
+ // We need to ensure that tracks are not removed before full drain.
+ ExtendedTimestamp local = timeStamp;
+ bool checked = false;
+ for (int i = ExtendedTimestamp::LOCATION_MAX - 1;
+ i >= ExtendedTimestamp::LOCATION_SERVER; --i) {
+ // Lookup the track frame corresponding to the sink frame position.
+ if (local.mTimeNs[i] > 0) {
+ local.mPosition[i] = mFrameMap.findX(local.mPosition[i]);
+ // check drain state from the latest stage in the pipeline.
+ if (!checked) {
+ mAudioTrackServerProxy->setDrained(
+ local.mPosition[i] >= mAudioTrackServerProxy->framesReleased());
+ checked = true;
+ }
+ }
}
+ if (!checked) { // no server info, assume drained.
+ mAudioTrackServerProxy->setDrained(true);
+ }
+ mServerProxy->setTimestamp(local);
}
// ----------------------------------------------------------------------------