AudioTrack: presentationComplete by time for Direct Tracks.
Test: Directed app - test_directtracks
Bug: 190579093
Change-Id: I604b65b2dbc190011442e6ab45e16b22512f74bd
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 51a41af..0929055 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -222,10 +222,23 @@
sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
+ // presentationComplete checked by frames. (Mixed Tracks).
// 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(int64_t framesWritten, size_t audioHalFrames);
+
+ // presentationComplete checked by time. (Direct Tracks).
+ bool presentationComplete(uint32_t latencyMs);
+
+ void resetPresentationComplete() {
+ mPresentationCompleteFrames = 0;
+ mPresentationCompleteTimeNs = 0;
+ }
+
+ // notifyPresentationComplete is called when presentationComplete() detects
+ // that the track is finished stopping.
+ void notifyPresentationComplete();
+
void signalClientFlag(int32_t flag);
public:
@@ -256,9 +269,6 @@
int32_t *mAuxBuffer;
int mAuxEffectId;
bool mHasVolumeController;
- size_t mPresentationCompleteFrames; // number of frames written to the
- // audio HAL when this track will be fully rendered
- // zero means not monitoring
// access these three variables only when holding thread lock.
LinearMap<int64_t> mFrameMap; // track frame to server frame mapping
@@ -294,6 +304,14 @@
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
};
+ size_t mPresentationCompleteFrames = 0; // (Used for Mixed tracks)
+ // The number of frames written to the
+ // audio HAL when this track is considered fully rendered.
+ // Zero means not monitoring.
+ int64_t mPresentationCompleteTimeNs = 0; // (Used for Direct tracks)
+ // The time when this track is considered fully rendered.
+ // Zero means not monitoring.
+
// The following fields are only for fast tracks, and should be in a subclass
int mFastIndex; // index within FastMixerState::mFastTracks[];
// either mFastIndex == -1 if not isFastTrack()
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index f62082e..9e099ce 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2631,7 +2631,7 @@
}
track->mResetDone = false;
- track->mPresentationCompleteFrames = 0;
+ track->resetPresentationComplete();
mActiveTracks.add(track);
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
@@ -6038,16 +6038,8 @@
track->isStopping_2() || track->isPaused()) {
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
- size_t audioHALFrames;
- if (audio_has_proportional_frames(mFormat)) {
- audioHALFrames = (latency_l() * mSampleRate) / 1000;
- } else {
- audioHALFrames = 0;
- }
-
- int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || !last ||
- track->presentationComplete(framesWritten, audioHALFrames) ||
+ track->presentationComplete(latency_l()) ||
track->isPaused() || mHwPaused) {
if (track->isStopping_2()) {
track->mState = TrackBase::STOPPED;
@@ -6621,14 +6613,7 @@
// Drain has completed or we are in standby, signal presentation complete
if (!(mDrainSequence & 1) || !last || mStandby) {
track->mState = TrackBase::STOPPED;
- uint32_t latency = 0;
- status_t result = mOutput->stream->getLatency(&latency);
- ALOGE_IF(result != OK,
- "Error when retrieving output stream latency: %d", result);
- size_t audioHALFrames = (latency * mSampleRate) / 1000;
- int64_t framesWritten =
- mBytesWritten / mOutput->getFrameSize();
- track->presentationComplete(framesWritten, audioHALFrames);
+ track->presentationComplete(latency_l());
track->reset();
tracksToRemove->add(track);
// OFFLOADED stop resets frame counts.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 57ff0d7..8d98afe 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -650,7 +650,6 @@
mMainBuffer(thread->sinkBuffer()),
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
- mPresentationCompleteFrames(0),
mFrameMap(16 /* sink-frame-to-track-frame map memory */),
mVolumeHandler(new media::VolumeHandler(sampleRate)),
mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(attributionSource, attr, id(),
@@ -1462,6 +1461,7 @@
mAuxBuffer = buffer;
}
+// presentationComplete verified by frames, used by Mixed tracks.
bool AudioFlinger::PlaybackThread::Track::presentationComplete(
int64_t framesWritten, size_t audioHalFrames)
{
@@ -1480,30 +1480,70 @@
(long long)mPresentationCompleteFrames, (long long)framesWritten);
if (mPresentationCompleteFrames == 0) {
mPresentationCompleteFrames = framesWritten + audioHalFrames;
- ALOGV("%s(%d): presentationComplete() reset:"
+ ALOGV("%s(%d): set:"
" mPresentationCompleteFrames %lld audioHalFrames %zu",
__func__, mId,
(long long)mPresentationCompleteFrames, audioHalFrames);
}
bool complete;
- if (isOffloaded()) {
- complete = true;
- } else if (isDirect() || isFastTrack()) { // these do not go through linear map
+ if (isFastTrack()) { // does not go through linear map
complete = framesWritten >= (int64_t) mPresentationCompleteFrames;
+ ALOGV("%s(%d): %s framesWritten:%lld mPresentationCompleteFrames:%lld",
+ __func__, mId, (complete ? "complete" : "waiting"),
+ (long long) framesWritten, (long long) mPresentationCompleteFrames);
} else { // Normal tracks, OutputTracks, and PatchTracks
complete = framesWritten >= (int64_t) mPresentationCompleteFrames
&& mAudioTrackServerProxy->isDrained();
}
if (complete) {
- triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
- mAudioTrackServerProxy->setStreamEndDone();
+ notifyPresentationComplete();
return true;
}
return false;
}
+// presentationComplete checked by time, used by DirectTracks.
+bool AudioFlinger::PlaybackThread::Track::presentationComplete(uint32_t latencyMs)
+{
+ // For Offloaded or Direct tracks.
+
+ // For a direct track, we incorporated time based testing for presentationComplete.
+
+ // For an offloaded track the HAL+h/w delay is variable so a HAL drain() is used
+ // to detect when all frames have been played. In this case latencyMs 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
+
+ constexpr float MIN_SPEED = 0.125f; // min speed scaling allowed for timely response.
+ if (mPresentationCompleteTimeNs == 0) {
+ mPresentationCompleteTimeNs = systemTime() + latencyMs * 1e6 / fmax(mSpeed, MIN_SPEED);
+ ALOGV("%s(%d): set: latencyMs %u mPresentationCompleteTimeNs:%lld",
+ __func__, mId, latencyMs, (long long) mPresentationCompleteTimeNs);
+ }
+
+ bool complete;
+ if (isOffloaded()) {
+ complete = true;
+ } else { // Direct
+ complete = systemTime() >= mPresentationCompleteTimeNs;
+ ALOGV("%s(%d): %s", __func__, mId, (complete ? "complete" : "waiting"));
+ }
+ if (complete) {
+ notifyPresentationComplete();
+ return true;
+ }
+ return false;
+}
+
+void AudioFlinger::PlaybackThread::Track::notifyPresentationComplete()
+{
+ // This only triggers once. TODO: should we enforce this?
+ triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ mAudioTrackServerProxy->setStreamEndDone();
+}
+
void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_t type)
{
for (size_t i = 0; i < mSyncEvents.size();) {