AudioFlinger: Add patch latency for direct record/playback
This relies on being able to obtain presentation position and
capture position accurately from the HAL, in the presence
of potential underruns or overruns.
Test: MSD hal dumpsys
Bug: 112428710
Change-Id: I9aad574baaff60b5e0c5d8c39a2147d19ee613f5
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index ada8572..7b165a1 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -547,14 +547,46 @@
// reverse due to internal biases).
//
// TODO: is this stable enough? Consider a PatchTrack synchronized version of this.
- double recordServerLatencyMs;
- if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) != OK) return INVALID_OPERATION;
- double playbackTrackLatencyMs;
- if (playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) != OK) return INVALID_OPERATION;
+ // For PCM tracks get server latency.
+ if (audio_is_linear_pcm(recordTrack->format())) {
+ double recordServerLatencyMs, playbackTrackLatencyMs;
+ if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) == OK
+ && playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) == OK) {
+ *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs;
+ return OK;
+ }
+ }
- *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs;
- return OK;
+ // See if kernel latencies are available.
+ // If so, do a frame diff and time difference computation to estimate
+ // the total patch latency. This requires that frame counts are reported by the
+ // HAL are matched properly in the case of record overruns and playback underruns.
+ ThreadBase::TrackBase::FrameTime recordFT{}, playFT{};
+ recordTrack->getKernelFrameTime(&recordFT);
+ playbackTrack->getKernelFrameTime(&playFT);
+ if (recordFT.timeNs > 0 && playFT.timeNs > 0) {
+ const int64_t frameDiff = recordFT.frames - playFT.frames;
+ const int64_t timeDiffNs = recordFT.timeNs - playFT.timeNs;
+
+ // It is possible that the patch track and patch record have a large time disparity because
+ // one thread runs but another is stopped. We arbitrarily choose the maximum timestamp
+ // time difference based on how often we expect the timestamps to update in normal operation
+ // (typical should be no more than 50 ms).
+ //
+ // If the timestamps aren't sampled close enough, the patch latency is not
+ // considered valid.
+ //
+ // TODO: change this based on more experiments.
+ constexpr int64_t maxValidTimeDiffNs = 200 * NANOS_PER_MILLISECOND;
+ if (std::abs(timeDiffNs) < maxValidTimeDiffNs) {
+ *latencyMs = frameDiff * 1e3 / recordTrack->sampleRate()
+ - timeDiffNs * 1e-6;
+ return OK;
+ }
+ }
+
+ return INVALID_OPERATION;
}
String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 95da9d7..a43cb75 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -187,6 +187,19 @@
return status;
}
+ // TODO: Consider making this external.
+ struct FrameTime {
+ int64_t frames;
+ int64_t timeNs;
+ };
+
+ // KernelFrameTime is updated per "mix" period even for non-pcm tracks.
+ void getKernelFrameTime(FrameTime *ft) const {
+ *ft = mKernelFrameTime.load();
+ }
+
+ audio_format_t format() const { return mFormat; }
+
protected:
DISALLOW_COPY_AND_ASSIGN(TrackBase);
@@ -198,8 +211,6 @@
// but putting it in TrackBase avoids the complexity of virtual inheritance
virtual size_t framesReady() const { return SIZE_MAX; }
- audio_format_t format() const { return mFormat; }
-
uint32_t channelCount() const { return mChannelCount; }
audio_channel_mask_t channelMask() const { return mChannelMask; }
@@ -307,6 +318,7 @@
bool mServerLatencySupported = false;
std::atomic<bool> mServerLatencyFromTrack{}; // latency from track or server timestamp.
std::atomic<double> mServerLatencyMs{}; // last latency pushed from server thread.
+ std::atomic<FrameTime> mKernelFrameTime{}; // last frame time on kernel side.
};
// PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8ad06f0..78e6c6c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1258,6 +1258,16 @@
void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
int64_t trackFramesReleased, int64_t sinkFramesWritten,
uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) {
+ // Make the kernel frametime available.
+ const FrameTime ft{
+ timeStamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
+ timeStamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]};
+ // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs);
+ mKernelFrameTime.store(ft);
+ if (!audio_is_linear_pcm(mFormat)) {
+ return;
+ }
+
//update frame map
mFrameMap.push(trackFramesReleased, sinkFramesWritten);
@@ -1886,6 +1896,16 @@
int64_t trackFramesReleased, int64_t sourceFramesRead,
uint32_t halSampleRate, const ExtendedTimestamp ×tamp)
{
+ // Make the kernel frametime available.
+ const FrameTime ft{
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]};
+ // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs);
+ mKernelFrameTime.store(ft);
+ if (!audio_is_linear_pcm(mFormat)) {
+ return;
+ }
+
ExtendedTimestamp local = timestamp;
// Convert HAL frames to server-side track frames at track sample rate.