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/include/media/ExtendedAudioBufferProvider.h b/include/media/ExtendedAudioBufferProvider.h
index 2539ed3..168ceed 100644
--- a/include/media/ExtendedAudioBufferProvider.h
+++ b/include/media/ExtendedAudioBufferProvider.h
@@ -27,11 +27,11 @@
virtual size_t framesReady() const = 0; // see description at AudioFlinger.h
// Return the total number of frames that have been obtained and released
- virtual size_t framesReleased() const { return 0; }
+ virtual int64_t framesReleased() const { return 0; }
// Invoked by buffer consumer when a new timestamp is available.
// Default implementation ignores the timestamp.
- virtual void onTimestamp(const AudioTimestamp& timestamp) { }
+ virtual void onTimestamp(const ExtendedTimestamp& timestamp) { }
};
} // namespace android
diff --git a/include/media/nbaio/AudioStreamInSource.h b/include/media/nbaio/AudioStreamInSource.h
index eaea63c..a6e7992 100644
--- a/include/media/nbaio/AudioStreamInSource.h
+++ b/include/media/nbaio/AudioStreamInSource.h
@@ -38,8 +38,8 @@
// NBAIO_Sink interface
//virtual size_t framesRead() const;
- virtual size_t framesOverrun();
- virtual size_t overruns() { (void) framesOverrun(); return mOverruns; }
+ virtual int64_t framesOverrun();
+ virtual int64_t overruns() { (void) framesOverrun(); return mOverruns; }
// This is an over-estimate, and could dupe the caller into making a blocking read()
// FIXME Use an audio HAL API to query the buffer filling status when it's available.
@@ -56,8 +56,8 @@
private:
audio_stream_in * const mStream;
size_t mStreamBufferSizeBytes; // as reported by get_buffer_size()
- size_t mFramesOverrun;
- size_t mOverruns;
+ int64_t mFramesOverrun;
+ int64_t mOverruns;
};
} // namespace android
diff --git a/include/media/nbaio/AudioStreamOutSink.h b/include/media/nbaio/AudioStreamOutSink.h
index 0998d45..e86b018 100644
--- a/include/media/nbaio/AudioStreamOutSink.h
+++ b/include/media/nbaio/AudioStreamOutSink.h
@@ -47,7 +47,7 @@
virtual ssize_t write(const void *buffer, size_t count);
- virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual status_t getTimestamp(ExtendedTimestamp ×tamp);
// NBAIO_Sink end
diff --git a/include/media/nbaio/MonoPipe.h b/include/media/nbaio/MonoPipe.h
index df9cafe..d2cd218 100644
--- a/include/media/nbaio/MonoPipe.h
+++ b/include/media/nbaio/MonoPipe.h
@@ -23,7 +23,7 @@
namespace android {
-typedef SingleStateQueue<AudioTimestamp> AudioTimestampSingleStateQueue;
+typedef SingleStateQueue<ExtendedTimestamp> ExtendedTimestampSingleStateQueue;
// MonoPipe is similar to Pipe except:
// - supports only a single reader, called MonoPipeReader
@@ -51,9 +51,9 @@
// NBAIO_Sink interface
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
+ //virtual int64_t framesWritten() const;
+ //virtual int64_t framesUnderrun() const;
+ //virtual int64_t underruns() const;
virtual ssize_t availableToWrite() const;
virtual ssize_t write(const void *buffer, size_t count);
@@ -77,7 +77,7 @@
bool isShutdown();
// Return NO_ERROR if there is a timestamp available
- status_t getTimestamp(AudioTimestamp& timestamp);
+ status_t getTimestamp(ExtendedTimestamp ×tamp);
private:
const size_t mReqFrames; // as requested in constructor, unrounded
@@ -97,9 +97,9 @@
bool mIsShutdown; // whether shutdown(true) was called, no barriers are needed
- AudioTimestampSingleStateQueue::Shared mTimestampShared;
- AudioTimestampSingleStateQueue::Mutator mTimestampMutator;
- AudioTimestampSingleStateQueue::Observer mTimestampObserver;
+ ExtendedTimestampSingleStateQueue::Shared mTimestampShared;
+ ExtendedTimestampSingleStateQueue::Mutator mTimestampMutator;
+ ExtendedTimestampSingleStateQueue::Observer mTimestampObserver;
};
} // namespace android
diff --git a/include/media/nbaio/MonoPipeReader.h b/include/media/nbaio/MonoPipeReader.h
index 4a7c3c5..b3c891d 100644
--- a/include/media/nbaio/MonoPipeReader.h
+++ b/include/media/nbaio/MonoPipeReader.h
@@ -49,7 +49,7 @@
virtual ssize_t read(void *buffer, size_t count);
- virtual void onTimestamp(const AudioTimestamp& timestamp);
+ virtual void onTimestamp(const ExtendedTimestamp ×tamp);
// NBAIO_Source end
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index 2f7e291..120de4f 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -145,13 +145,13 @@
// 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically.
// Return the number of frames written successfully since construction.
- virtual size_t framesWritten() const { return mFramesWritten; }
+ virtual int64_t framesWritten() const { return mFramesWritten; }
// Number of frames lost due to underrun since construction.
- virtual size_t framesUnderrun() const { return 0; }
+ virtual int64_t framesUnderrun() const { return 0; }
// Number of underruns since construction, where a set of contiguous lost frames is one event.
- virtual size_t underruns() const { return 0; }
+ virtual int64_t underruns() const { return 0; }
// Estimate of number of frames that could be written successfully now without blocking.
// When a write() is actually attempted, the implementation is permitted to return a smaller or
@@ -212,7 +212,7 @@
// Returns NO_ERROR if a timestamp is available. The timestamp includes the total number
// of frames presented to an external observer, together with the value of CLOCK_MONOTONIC
// as of this presentation count. The timestamp parameter is undefined if error is returned.
- virtual status_t getTimestamp(AudioTimestamp& timestamp) { return INVALID_OPERATION; }
+ virtual status_t getTimestamp(ExtendedTimestamp ×tamp) { return INVALID_OPERATION; }
protected:
NBAIO_Sink(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0)
@@ -220,7 +220,7 @@
virtual ~NBAIO_Sink() { }
// Implementations are free to ignore these if they don't need them
- size_t mFramesWritten;
+ int64_t mFramesWritten;
};
// Abstract class (interface) representing a non-blocking data source, for use by a data consumer.
@@ -232,15 +232,15 @@
// 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically.
// Number of frames read successfully since construction.
- virtual size_t framesRead() const { return mFramesRead; }
+ virtual int64_t framesRead() const { return mFramesRead; }
// Number of frames lost due to overrun since construction.
// Not const because implementations may need to do I/O.
- virtual size_t framesOverrun() /*const*/ { return 0; }
+ virtual int64_t framesOverrun() /*const*/ { return 0; }
// Number of overruns since construction, where a set of contiguous lost frames is one event.
// Not const because implementations may need to do I/O.
- virtual size_t overruns() /*const*/ { return 0; }
+ virtual int64_t overruns() /*const*/ { return 0; }
// Estimate of number of frames that could be read successfully now.
// When a read() is actually attempted, the implementation is permitted to return a smaller or
@@ -299,7 +299,7 @@
// Invoked asynchronously by corresponding sink when a new timestamp is available.
// Default implementation ignores the timestamp.
- virtual void onTimestamp(const AudioTimestamp& timestamp) { }
+ virtual void onTimestamp(const ExtendedTimestamp& timestamp) { }
protected:
NBAIO_Source(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0)
@@ -307,7 +307,7 @@
virtual ~NBAIO_Source() { }
// Implementations are free to ignore these if they don't need them
- size_t mFramesRead;
+ int64_t mFramesRead;
};
} // namespace android
diff --git a/include/media/nbaio/Pipe.h b/include/media/nbaio/Pipe.h
index eba37bc..cc95ff7 100644
--- a/include/media/nbaio/Pipe.h
+++ b/include/media/nbaio/Pipe.h
@@ -45,9 +45,9 @@
// NBAIO_Sink interface
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
+ //virtual int64_t framesWritten() const;
+ //virtual int64_t framesUnderrun() const;
+ //virtual int64_t underruns() const;
// The write side of a pipe permits overruns; flow control is the caller's responsibility.
// It doesn't return +infinity because that would guarantee an overrun.
diff --git a/include/media/nbaio/PipeReader.h b/include/media/nbaio/PipeReader.h
index 398353b..7c733ad 100644
--- a/include/media/nbaio/PipeReader.h
+++ b/include/media/nbaio/PipeReader.h
@@ -40,8 +40,8 @@
// NBAIO_Source interface
//virtual size_t framesRead() const;
- virtual size_t framesOverrun() { return mFramesOverrun; }
- virtual size_t overruns() { return mOverruns; }
+ virtual int64_t framesOverrun() { return mFramesOverrun; }
+ virtual int64_t overruns() { return mOverruns; }
virtual ssize_t availableToRead();
@@ -56,8 +56,8 @@
private:
Pipe& mPipe;
int32_t mFront; // follows behind mPipe.mRear
- size_t mFramesOverrun;
- size_t mOverruns;
+ int64_t mFramesOverrun;
+ int64_t mOverruns;
};
} // namespace android
diff --git a/include/media/nbaio/SourceAudioBufferProvider.h b/include/media/nbaio/SourceAudioBufferProvider.h
index 29172e1..ae49903 100644
--- a/include/media/nbaio/SourceAudioBufferProvider.h
+++ b/include/media/nbaio/SourceAudioBufferProvider.h
@@ -36,8 +36,8 @@
// ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
- virtual size_t framesReleased() const;
- virtual void onTimestamp(const AudioTimestamp& timestamp);
+ virtual int64_t framesReleased() const;
+ virtual void onTimestamp(const ExtendedTimestamp ×tamp);
private:
const sp<NBAIO_Source> mSource; // the wrapped source
@@ -47,7 +47,7 @@
size_t mOffset; // frame offset within mAllocated of valid data
size_t mRemaining; // frame count within mAllocated of valid data
size_t mGetCount; // buffer.frameCount of the most recent getNextBuffer
- uint32_t mFramesReleased; // counter of the total number of frames released
+ int64_t mFramesReleased; // counter of the total number of frames released
};
} // namespace android
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index aa9e98c..ea8a78e 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -176,6 +176,7 @@
// server write-only, client read
ExtendedTimestampQueue::Shared mExtendedTimestampQueue;
+
public:
volatile int32_t mFlags; // combinations of CBLK_*
@@ -532,7 +533,7 @@
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
: ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
mPlaybackRateObserver(&cblk->mPlaybackRateQueue),
- mUnderrunCount(0), mUnderrunning(false) {
+ mUnderrunCount(0), mUnderrunning(false), mDrained(true) {
mCblk->mSampleRate = sampleRate;
mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
}
@@ -569,6 +570,18 @@
// Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
AudioPlaybackRate getPlaybackRate();
+ // Set the internal drain state of the track buffer from the timestamp received.
+ virtual void setDrained(bool drained) {
+ mDrained.store(drained);
+ }
+
+ // Check if the internal drain state of the track buffer.
+ // This is not a guarantee, but advisory for determining whether the track is
+ // fully played out.
+ virtual bool isDrained() const {
+ return mDrained.load();
+ }
+
private:
AudioPlaybackRate mPlaybackRate; // last observed playback rate
PlaybackRateQueue::Observer mPlaybackRateObserver;
@@ -576,6 +589,8 @@
// The server keeps a copy here where it is safe from the client.
uint32_t mUnderrunCount; // echoed to mCblk
bool mUnderrunning; // used to detect edge of underrun
+
+ std::atomic<bool> mDrained; // is the track buffer drained
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 33dcc57..f0074b6 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -2202,7 +2202,7 @@
// To avoid a race, read the presented frames first. This ensures that presented <= consumed.
status_t status;
- if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) {
+ if (isOffloadedOrDirect_l()) {
// use Binder to get timestamp
status = mAudioTrack->getTimestamp(timestamp);
} else {
diff --git a/media/libnbaio/AudioStreamInSource.cpp b/media/libnbaio/AudioStreamInSource.cpp
index 286e0eb..2dc3050 100644
--- a/media/libnbaio/AudioStreamInSource.cpp
+++ b/media/libnbaio/AudioStreamInSource.cpp
@@ -53,7 +53,7 @@
return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
}
-size_t AudioStreamInSource::framesOverrun()
+int64_t AudioStreamInSource::framesOverrun()
{
uint32_t framesOverrun = mStream->get_input_frames_lost(mStream);
if (framesOverrun > 0) {
diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp
index 3f4e0bb..ee44678 100644
--- a/media/libnbaio/AudioStreamOutSink.cpp
+++ b/media/libnbaio/AudioStreamOutSink.cpp
@@ -66,18 +66,20 @@
return ret;
}
-status_t AudioStreamOutSink::getTimestamp(AudioTimestamp& timestamp)
+status_t AudioStreamOutSink::getTimestamp(ExtendedTimestamp ×tamp)
{
if (mStream->get_presentation_position == NULL) {
return INVALID_OPERATION;
}
- // FIXME position64 won't be needed after AudioTimestamp.mPosition is changed to uint64_t
+
uint64_t position64;
- int ok = mStream->get_presentation_position(mStream, &position64, ×tamp.mTime);
- if (ok != 0) {
+ struct timespec time;
+ if (mStream->get_presentation_position(mStream, &position64, &time) != OK) {
return INVALID_OPERATION;
}
- timestamp.mPosition = position64;
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = position64;
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+ time.tv_sec * 1000000000LL + time.tv_nsec;
return OK;
}
diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp
index aef9834..8d1cb0f 100644
--- a/media/libnbaio/MonoPipe.cpp
+++ b/media/libnbaio/MonoPipe.cpp
@@ -183,9 +183,14 @@
return mIsShutdown;
}
-status_t MonoPipe::getTimestamp(AudioTimestamp& timestamp)
+status_t MonoPipe::getTimestamp(ExtendedTimestamp ×tamp)
{
- if (mTimestampObserver.poll(timestamp)) {
+ ExtendedTimestamp ets;
+ if (mTimestampObserver.poll(ets)) {
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+ ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+ ets.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
return OK;
}
return INVALID_OPERATION;
diff --git a/media/libnbaio/MonoPipeReader.cpp b/media/libnbaio/MonoPipeReader.cpp
index 7e09544..01dc524 100644
--- a/media/libnbaio/MonoPipeReader.cpp
+++ b/media/libnbaio/MonoPipeReader.cpp
@@ -72,7 +72,7 @@
return red;
}
-void MonoPipeReader::onTimestamp(const AudioTimestamp& timestamp)
+void MonoPipeReader::onTimestamp(const ExtendedTimestamp ×tamp)
{
mPipe->mTimestampMutator.push(timestamp);
}
diff --git a/media/libnbaio/SourceAudioBufferProvider.cpp b/media/libnbaio/SourceAudioBufferProvider.cpp
index dc01c0e..d58619f 100644
--- a/media/libnbaio/SourceAudioBufferProvider.cpp
+++ b/media/libnbaio/SourceAudioBufferProvider.cpp
@@ -112,12 +112,12 @@
return avail < 0 ? 0 : (size_t) avail;
}
-size_t SourceAudioBufferProvider::framesReleased() const
+int64_t SourceAudioBufferProvider::framesReleased() const
{
return mFramesReleased;
}
-void SourceAudioBufferProvider::onTimestamp(const AudioTimestamp& timestamp)
+void SourceAudioBufferProvider::onTimestamp(const ExtendedTimestamp ×tamp)
{
mSource->onTimestamp(timestamp);
}
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 1446d19..d31b8d3 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -141,6 +141,10 @@
FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
const size_t frameCount = current->mFrameCount;
+ // update boottime offset, in case it has changed
+ mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
+ mBoottimeOffset.load();
+
// handle state change here, but since we want to diff the state,
// we're prepared for previous == &sInitial the first time through
unsigned previousTrackMask;
@@ -341,21 +345,23 @@
currentTrackMask &= ~(1 << i);
const FastTrack* fastTrack = ¤t->mFastTracks[i];
- // Refresh the per-track timestamp
- if (mTimestampStatus == NO_ERROR) {
- uint32_t trackFramesWrittenButNotPresented =
- mNativeFramesWrittenButNotPresented;
- uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
- // Can't provide an AudioTimestamp before first frame presented,
- // or during the brief 32-bit wraparound window
- if (trackFramesWritten >= trackFramesWrittenButNotPresented) {
- AudioTimestamp perTrackTimestamp;
- perTrackTimestamp.mPosition =
- trackFramesWritten - trackFramesWrittenButNotPresented;
- perTrackTimestamp.mTime = mTimestamp.mTime;
- fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
- }
+ const int64_t trackFramesWrittenButNotPresented =
+ mNativeFramesWrittenButNotPresented;
+ const int64_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
+ ExtendedTimestamp perTrackTimestamp(mTimestamp);
+
+ // Can't provide an ExtendedTimestamp before first frame presented.
+ // Also, timestamp may not go to very last frame on stop().
+ if (trackFramesWritten >= trackFramesWrittenButNotPresented &&
+ perTrackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] > 0) {
+ perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+ trackFramesWritten - trackFramesWrittenButNotPresented;
+ } else {
+ perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0;
+ perTrackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1;
}
+ perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = trackFramesWritten;
+ fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
int name = mFastTrackNames[i];
ALOG_ASSERT(name >= 0);
@@ -449,17 +455,34 @@
mAttemptedWrite = true;
// FIXME count # of writes blocked excessively, CPU usage, etc. for dump
- mTimestampStatus = mOutputSink->getTimestamp(mTimestamp);
- if (mTimestampStatus == NO_ERROR) {
- uint32_t totalNativeFramesPresented = mTimestamp.mPosition;
+ ExtendedTimestamp timestamp; // local
+ status_t status = mOutputSink->getTimestamp(timestamp);
+ if (status == NO_ERROR) {
+ const int64_t totalNativeFramesPresented =
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
if (totalNativeFramesPresented <= mTotalNativeFramesWritten) {
mNativeFramesWrittenButNotPresented =
mTotalNativeFramesWritten - totalNativeFramesPresented;
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
} else {
// HAL reported that more frames were presented than were written
- mTimestampStatus = INVALID_OPERATION;
+ mNativeFramesWrittenButNotPresented = 0;
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1;
+ status = INVALID_OPERATION;
}
}
+ if (status == NO_ERROR) {
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] =
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+ } else {
+ // fetch server time if we can't get timestamp
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] =
+ systemTime(SYSTEM_TIME_MONOTONIC);
+ }
}
}
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index e38878e..3cc7c9f 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -38,7 +38,9 @@
FastMixerStateQueue* sq();
virtual void setMasterMono(bool mono) { mMasterMono.store(mono); /* memory_order_seq_cst */ }
-
+ virtual void setBoottimeOffset(int64_t boottimeOffset) {
+ mBoottimeOffset.store(boottimeOffset); /* memory_order_seq_cst */
+ }
private:
FastMixerStateQueue mSQ;
@@ -79,14 +81,15 @@
unsigned mSampleRate;
int mFastTracksGen;
FastMixerDumpState mDummyFastMixerDumpState;
- uint32_t mTotalNativeFramesWritten; // copied to dumpState->mFramesWritten
+ int64_t mTotalNativeFramesWritten; // copied to dumpState->mFramesWritten
// next 2 fields are valid only when timestampStatus == NO_ERROR
- AudioTimestamp mTimestamp;
- uint32_t mNativeFramesWrittenButNotPresented;
+ ExtendedTimestamp mTimestamp;
+ int64_t mNativeFramesWrittenButNotPresented;
// accessed without lock between multiple threads.
std::atomic_bool mMasterMono;
+ std::atomic_int_fast64_t mBoottimeOffset;
}; // class FastMixer
} // namespace android
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index fe3cc53..fa61af2 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -88,8 +88,8 @@
// ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
- virtual size_t framesReleased() const;
- virtual void onTimestamp(const AudioTimestamp ×tamp);
+ virtual int64_t framesReleased() const;
+ virtual void onTimestamp(const ExtendedTimestamp ×tamp);
bool isPausing() const { return mState == PAUSING; }
bool isPaused() const { return mState == PAUSED; }
@@ -101,15 +101,15 @@
void flushAck();
bool isResumePending();
void resumeAck();
- void updateTrackFrameInfo(uint32_t trackFramesReleased, uint32_t sinkFramesWritten,
- AudioTimestamp *timeStamp = NULL);
+ void updateTrackFrameInfo(int64_t trackFramesReleased, int64_t sinkFramesWritten,
+ const ExtendedTimestamp &timeStamp);
sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
// framesWritten is cumulative, never reset, and is shared all tracks
// audioHalFrames is derived from output latency
// FIXME parameters not needed, could get them from the thread
- bool presentationComplete(size_t framesWritten, size_t audioHalFrames);
+ bool presentationComplete(int64_t framesWritten, size_t audioHalFrames);
public:
void triggerEvents(AudioSystem::sync_event_t type);
@@ -142,9 +142,9 @@
// zero means not monitoring
// access these three variables only when holding thread lock.
- LinearMap<uint32_t> mFrameMap; // track frame to server frame mapping
- bool mSinkTimestampValid; // valid cached timestamp
- AudioTimestamp mSinkTimestamp;
+ LinearMap<int64_t> mFrameMap; // track frame to server frame mapping
+
+ ExtendedTimestamp mSinkTimestamp;
private:
// The following fields are only for fast tracks, and should be in a subclass
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4807400..e0d8f75 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1034,6 +1034,8 @@
mNotifiedBatteryStart = true;
}
gBoottime.acquire(mWakeLockToken);
+ mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
+ gBoottime.getBoottimeOffset();
}
void AudioFlinger::ThreadBase::releaseWakeLock()
@@ -2370,13 +2372,14 @@
if (initCheck() != NO_ERROR) {
return INVALID_OPERATION;
}
- size_t framesWritten = mBytesWritten / mFrameSize;
+ int64_t framesWritten = mBytesWritten / mFrameSize;
*halFrames = framesWritten;
if (isSuspended()) {
// return an estimation of rendered frames when the output is suspended
size_t latencyFrames = (latency_l() * mSampleRate) / 1000;
- *dspFrames = framesWritten >= latencyFrames ? framesWritten - latencyFrames : 0;
+ *dspFrames = (uint32_t)
+ (framesWritten >= (int64_t)latencyFrames ? framesWritten - latencyFrames : 0);
return NO_ERROR;
} else {
status_t status;
@@ -2860,42 +2863,31 @@
// and associate with the sink frames written out. We need
// this to convert the sink timestamp to the track timestamp.
if (mNormalSink != 0) {
- bool updateTracks = true;
- bool cacheTimestamp = false;
- AudioTimestamp timeStamp;
- // FIXME: Use a 64 bit mNormalSink->framesWritten() counter.
- // At this time, we must always use cached timestamps even when
- // going through mPipeSink (which is non-blocking). The reason is that
- // the track may be removed from the active list for many hours and
- // the mNormalSink->framesWritten() will wrap making the linear
- // mapping fail.
- //
- // (Also mAudioTrackServerProxy->framesReleased() needs to be
- // updated to 64 bits for 64 bit frame position.)
- //
- if (true /* see comment above, should be: mNormalSink == mOutputSink */) {
- // If we use a hardware device, we must cache the sink timestamp now.
- // hardware devices can block timestamp access during data writes.
- if (mNormalSink->getTimestamp(timeStamp) == NO_ERROR) {
- cacheTimestamp = true;
- } else {
- updateTracks = false;
- }
- }
- if (updateTracks) {
- // sinkFramesWritten for non-offloaded tracks are contiguous
- // even after standby() is called. This is useful for the track frame
- // to sink frame mapping.
- const uint32_t sinkFramesWritten = mNormalSink->framesWritten();
- const size_t size = mActiveTracks.size();
- for (size_t i = 0; i < size; ++i) {
- sp<Track> t = mActiveTracks[i].promote();
- if (t != 0 && !t->isFastTrack()) {
- t->updateTrackFrameInfo(
- t->mAudioTrackServerProxy->framesReleased(),
- sinkFramesWritten,
- cacheTimestamp ? &timeStamp : NULL);
- }
+ // We always fetch the timestamp here because often the downstream
+ // sink will block whie writing.
+ ExtendedTimestamp timestamp; // use private copy to fetch
+ (void) mNormalSink->getTimestamp(timestamp);
+ // copy over kernel info
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+
+ // sinkFramesWritten for non-offloaded tracks are contiguous
+ // even after standby() is called. This is useful for the track frame
+ // to sink frame mapping.
+ const int64_t sinkFramesWritten = mNormalSink->framesWritten();
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = sinkFramesWritten;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = systemTime();
+
+ const size_t size = mActiveTracks.size();
+ for (size_t i = 0; i < size; ++i) {
+ sp<Track> t = mActiveTracks[i].promote();
+ if (t != 0 && !t->isFastTrack()) {
+ t->updateTrackFrameInfo(
+ t->mAudioTrackServerProxy->framesReleased(),
+ sinkFramesWritten,
+ mTimestamp);
}
}
}
@@ -3209,7 +3201,12 @@
status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
{
if (mNormalSink != 0) {
- return mNormalSink->getTimestamp(timestamp);
+ ExtendedTimestamp ets;
+ status_t status = mNormalSink->getTimestamp(ets);
+ if (status == NO_ERROR) {
+ status = ets.getBestTimestamp(×tamp);
+ }
+ return status;
}
if ((mType == OFFLOAD || mType == DIRECT)
&& mOutput != NULL && mOutput->stream->get_presentation_position) {
@@ -3925,7 +3922,7 @@
{
size_t audioHALFrames =
(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
- size_t framesWritten = mBytesWritten / mFrameSize;
+ int64_t framesWritten = mBytesWritten / mFrameSize;
if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
// track stays in active list until presentation is complete
break;
@@ -4255,7 +4252,7 @@
// TODO: use actual buffer filling status instead of latency when available from
// audio HAL
size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
- size_t framesWritten = mBytesWritten / mFrameSize;
+ int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
track->reset();
@@ -4796,7 +4793,7 @@
audioHALFrames = 0;
}
- size_t framesWritten = mBytesWritten / mFrameSize;
+ int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || !last ||
track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopping_2()) {
@@ -5343,7 +5340,7 @@
track->mState = TrackBase::STOPPED;
size_t audioHALFrames =
(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
- size_t framesWritten =
+ int64_t framesWritten =
mBytesWritten / mOutput->getFrameSize();
track->presentationComplete(framesWritten, audioHALFrames);
track->reset();
@@ -5789,9 +5786,6 @@
}
}
- mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
- gBoottime.getBoottimeOffset();
-
// used to request a deferred sleep, to be executed later while mutex is unlocked
uint32_t sleepUs = 0;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7c92c1c..507f197 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -381,7 +381,7 @@
};
void acquireWakeLock(int uid = -1);
- void acquireWakeLock_l(int uid = -1);
+ virtual void acquireWakeLock_l(int uid = -1);
void releaseWakeLock();
void releaseWakeLock_l();
void updateWakeLockUids(const SortedVector<int> &uids);
@@ -460,6 +460,7 @@
sp<NBLog::Writer> mNBLogWriter;
bool mSystemReady;
bool mNotifiedBatteryStart;
+ ExtendedTimestamp mTimestamp;
};
// --- PlaybackThread ---
@@ -691,9 +692,7 @@
// 'volatile' means accessed via atomic operations and no lock.
volatile int32_t mSuspended;
- // FIXME overflows every 6+ hours at 44.1 kHz stereo 16-bit samples
- // mFramesWritten would be better, or 64-bit even better
- size_t mBytesWritten;
+ int64_t mBytesWritten;
private:
// mMasterMute is in both PlaybackThread and in AudioFlinger. When a
// PlaybackThread needs to find out if master-muted, it checks it's local
@@ -868,6 +867,14 @@
virtual uint32_t suspendSleepTimeUs() const;
virtual void cacheParameters_l();
+ virtual void acquireWakeLock_l(int uid = -1) {
+ PlaybackThread::acquireWakeLock_l(uid);
+ if (hasFastMixer()) {
+ mFastMixer->setBoottimeOffset(
+ mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME]);
+ }
+ }
+
// threadLoop snippets
virtual ssize_t threadLoop_write();
virtual void threadLoop_standby();
@@ -1311,8 +1318,6 @@
// rolling index that is never cleared
int32_t mRsmpInRear; // last filled frame + 1
- ExtendedTimestamp mTimestamp;
-
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
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);
}
// ----------------------------------------------------------------------------