Loading services/audioflinger/PlaybackTracks.h +4 −6 Original line number Diff line number Diff line Loading @@ -94,9 +94,8 @@ public: virtual bool isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; } virtual double bufferLatencyMs() { return isStatic() ? 0. : (double)mAudioTrackServerProxy->framesReadySafe() * 1000 / sampleRate(); double bufferLatencyMs() const override { return isStatic() ? 0. : TrackBase::bufferLatencyMs(); } // implement volume handling. Loading Loading @@ -152,7 +151,7 @@ protected: bool isResumePending(); void resumeAck(); void updateTrackFrameInfo(int64_t trackFramesReleased, int64_t sinkFramesWritten, const ExtendedTimestamp &timeStamp); uint32_t halSampleRate, const ExtendedTimestamp &timeStamp); sp<IMemory> sharedBuffer() const { return mSharedBuffer; } Loading Loading @@ -200,7 +199,6 @@ protected: sp<media::VolumeHandler> mVolumeHandler; // handles multiple VolumeShaper configs and operations bool mDumpLatency = false; // true if track supports latency dumps. private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; Loading services/audioflinger/Threads.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -3265,6 +3265,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() t->updateTrackFrameInfo( t->mAudioTrackServerProxy->framesReleased(), mFramesWritten, mSampleRate, mTimestamp); } } Loading Loading @@ -5059,8 +5060,10 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); if (latencyMs > 0.) { if (latencyMs != 0.) { dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); } else { dprintf(fd, " NormalMixer latency ms: unavail\n"); } if (hasFastMixer()) { Loading services/audioflinger/TrackBase.h +84 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,86 @@ public: } #endif /** returns the buffer contents size converted to time in milliseconds * for PCM Playback or Record streaming tracks. The return value is zero for * PCM static tracks and not defined for non-PCM tracks. * * This may be called without the thread lock. */ virtual double bufferLatencyMs() const { return mServerProxy->framesReadySafe() * 1000 / sampleRate(); } /** returns whether the track supports server latency computation. * This is set in the constructor and constant throughout the track lifetime. */ bool isServerLatencySupported() const { return mServerLatencySupported; } /** computes the server latency for PCM Playback or Record track * to the device sink/source. This is the time for the next frame in the track buffer * written or read from the server thread to the device source or sink. * * This may be called without the thread lock, but latencyMs and fromTrack * may be not be synchronized. For example PatchPanel may not obtain the * thread lock before calling. * * \param latencyMs on success is set to the latency in milliseconds of the * next frame written/read by the server thread to/from the track buffer * from the device source/sink. * \param fromTrack on success is set to true if latency was computed directly * from the track timestamp; otherwise set to false if latency was * estimated from the server timestamp. * fromTrack may be nullptr or omitted if not required. * * \returns OK or INVALID_OPERATION on failure. */ status_t getServerLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { if (!isServerLatencySupported()) { return INVALID_OPERATION; } // if no thread lock is acquired, these atomics are not // synchronized with each other, considered a benign race. const double serverLatencyMs = mServerLatencyMs.load(); if (serverLatencyMs == 0.) { return INVALID_OPERATION; } if (fromTrack != nullptr) { *fromTrack = mServerLatencyFromTrack.load(); } *latencyMs = serverLatencyMs; return OK; } /** computes the total client latency for PCM Playback or Record tracks * for the next client app access to the device sink/source; i.e. the * server latency plus the buffer latency. * * This may be called without the thread lock, but latencyMs and fromTrack * may be not be synchronized. For example PatchPanel may not obtain the * thread lock before calling. * * \param latencyMs on success is set to the latency in milliseconds of the * next frame written/read by the client app to/from the track buffer * from the device sink/source. * \param fromTrack on success is set to true if latency was computed directly * from the track timestamp; otherwise set to false if latency was * estimated from the server timestamp. * fromTrack may be nullptr or omitted if not required. * * \returns OK or INVALID_OPERATION on failure. */ status_t getTrackLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { double serverLatencyMs; status_t status = getServerLatencyMs(&serverLatencyMs, fromTrack); if (status == OK) { *latencyMs = serverLatencyMs + bufferLatencyMs(); } return status; } protected: DISALLOW_COPY_AND_ASSIGN(TrackBase); Loading Loading @@ -222,6 +302,10 @@ protected: audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to audio_port_handle_t mPortId; // unique ID for this track used by audio policy bool mIsInvalid; // non-resettable latch, set by invalidate() bool mServerLatencySupported = false; std::atomic<bool> mServerLatencyFromTrack{}; // latency from track or server timestamp. std::atomic<double> mServerLatencyMs{}; // last latency pushed from server thread. }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. Loading services/audioflinger/Tracks.cpp +33 −16 Original line number Diff line number Diff line Loading @@ -435,7 +435,7 @@ AudioFlinger::PlaybackThread::Track::Track( } mName = TRACK_NAME_PENDING; mDumpLatency = thread->type() == ThreadBase::MIXER; mServerLatencySupported = thread->type() == ThreadBase::MIXER; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId) + Loading Loading @@ -497,7 +497,7 @@ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) "ST L dB R dB VS dB " " Server FrmCnt FrmRdy F Underruns Flushed" "%s\n", mDumpLatency ? " Latency" : ""); isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active) Loading Loading @@ -593,7 +593,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ mFormat, mChannelMask, mAudioTrackServerProxy->getSampleRate(), sampleRate(), mStreamType, 20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))), Loading @@ -610,14 +610,16 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ nowInUnderrun, (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000 ); if (mDumpLatency) { double latencyMs = mAudioTrackServerProxy->getTimestamp().getOutputServerLatencyMs(mSampleRate); if (latencyMs > 0.) { latencyMs += bufferLatencyMs(); result.appendFormat(" %7.3f", latencyMs); if (isServerLatencySupported()) { double latencyMs; bool fromTrack; if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) { // Show latency in msec, followed by 't' if from track timestamp (the most accurate) // or 'k' if estimated from kernel because track frames haven't been presented yet. result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k'); } else { result.appendFormat(" Unknown"); result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new"); } } result.append("\n"); Loading Loading @@ -677,6 +679,13 @@ void AudioFlinger::PlaybackThread::Track::onTimestamp(const ExtendedTimestamp &t mAudioTrackServerProxy->setTimestamp(timestamp); // We do not set drained here, as FastTrack timestamp may not go to very last frame. // Compute latency. // TODO: Consider whether the server latency may be passed in by FastMixer // as a constant for all active FastTracks. const double latencyMs = timestamp.getOutputServerLatencyMs(sampleRate()); mServerLatencyFromTrack.store(true); mServerLatencyMs.store(latencyMs); } // Don't call for fast tracks; the framesReady() could result in priority inversion Loading Loading @@ -1241,7 +1250,7 @@ void AudioFlinger::PlaybackThread::Track::resumeAck() { //To be called with thread lock held void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( int64_t trackFramesReleased, int64_t sinkFramesWritten, const ExtendedTimestamp &timeStamp) { uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) { //update frame map mFrameMap.push(trackFramesReleased, sinkFramesWritten); Loading @@ -1250,6 +1259,7 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( // 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 drained = true; // default assume drained, if no server info found bool checked = false; for (int i = ExtendedTimestamp::LOCATION_MAX - 1; i >= ExtendedTimestamp::LOCATION_SERVER; --i) { Loading @@ -1258,18 +1268,25 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( local.mPosition[i] = mFrameMap.findX(local.mPosition[i]); // check drain state from the latest stage in the pipeline. if (!checked && i <= ExtendedTimestamp::LOCATION_KERNEL) { mAudioTrackServerProxy->setDrained( local.mPosition[i] >= mAudioTrackServerProxy->framesReleased()); drained = local.mPosition[i] >= mAudioTrackServerProxy->framesReleased(); checked = true; } } } if (!checked) { // no server info, assume drained. mAudioTrackServerProxy->setDrained(true); } mAudioTrackServerProxy->setDrained(drained); // Set correction for flushed frames that are not accounted for in released. local.mFlushed = mAudioTrackServerProxy->framesFlushed(); mServerProxy->setTimestamp(local); // Compute latency info. const bool useTrackTimestamp = !drained; const double latencyMs = useTrackTimestamp ? local.getOutputServerLatencyMs(sampleRate()) : timeStamp.getOutputServerLatencyMs(halSampleRate); mServerLatencyFromTrack.store(useTrackTimestamp); mServerLatencyMs.store(latencyMs); } // ---------------------------------------------------------------------------- Loading Loading
services/audioflinger/PlaybackTracks.h +4 −6 Original line number Diff line number Diff line Loading @@ -94,9 +94,8 @@ public: virtual bool isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; } virtual double bufferLatencyMs() { return isStatic() ? 0. : (double)mAudioTrackServerProxy->framesReadySafe() * 1000 / sampleRate(); double bufferLatencyMs() const override { return isStatic() ? 0. : TrackBase::bufferLatencyMs(); } // implement volume handling. Loading Loading @@ -152,7 +151,7 @@ protected: bool isResumePending(); void resumeAck(); void updateTrackFrameInfo(int64_t trackFramesReleased, int64_t sinkFramesWritten, const ExtendedTimestamp &timeStamp); uint32_t halSampleRate, const ExtendedTimestamp &timeStamp); sp<IMemory> sharedBuffer() const { return mSharedBuffer; } Loading Loading @@ -200,7 +199,6 @@ protected: sp<media::VolumeHandler> mVolumeHandler; // handles multiple VolumeShaper configs and operations bool mDumpLatency = false; // true if track supports latency dumps. private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; Loading
services/audioflinger/Threads.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -3265,6 +3265,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() t->updateTrackFrameInfo( t->mAudioTrackServerProxy->framesReleased(), mFramesWritten, mSampleRate, mTimestamp); } } Loading Loading @@ -5059,8 +5060,10 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); if (latencyMs > 0.) { if (latencyMs != 0.) { dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); } else { dprintf(fd, " NormalMixer latency ms: unavail\n"); } if (hasFastMixer()) { Loading
services/audioflinger/TrackBase.h +84 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,86 @@ public: } #endif /** returns the buffer contents size converted to time in milliseconds * for PCM Playback or Record streaming tracks. The return value is zero for * PCM static tracks and not defined for non-PCM tracks. * * This may be called without the thread lock. */ virtual double bufferLatencyMs() const { return mServerProxy->framesReadySafe() * 1000 / sampleRate(); } /** returns whether the track supports server latency computation. * This is set in the constructor and constant throughout the track lifetime. */ bool isServerLatencySupported() const { return mServerLatencySupported; } /** computes the server latency for PCM Playback or Record track * to the device sink/source. This is the time for the next frame in the track buffer * written or read from the server thread to the device source or sink. * * This may be called without the thread lock, but latencyMs and fromTrack * may be not be synchronized. For example PatchPanel may not obtain the * thread lock before calling. * * \param latencyMs on success is set to the latency in milliseconds of the * next frame written/read by the server thread to/from the track buffer * from the device source/sink. * \param fromTrack on success is set to true if latency was computed directly * from the track timestamp; otherwise set to false if latency was * estimated from the server timestamp. * fromTrack may be nullptr or omitted if not required. * * \returns OK or INVALID_OPERATION on failure. */ status_t getServerLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { if (!isServerLatencySupported()) { return INVALID_OPERATION; } // if no thread lock is acquired, these atomics are not // synchronized with each other, considered a benign race. const double serverLatencyMs = mServerLatencyMs.load(); if (serverLatencyMs == 0.) { return INVALID_OPERATION; } if (fromTrack != nullptr) { *fromTrack = mServerLatencyFromTrack.load(); } *latencyMs = serverLatencyMs; return OK; } /** computes the total client latency for PCM Playback or Record tracks * for the next client app access to the device sink/source; i.e. the * server latency plus the buffer latency. * * This may be called without the thread lock, but latencyMs and fromTrack * may be not be synchronized. For example PatchPanel may not obtain the * thread lock before calling. * * \param latencyMs on success is set to the latency in milliseconds of the * next frame written/read by the client app to/from the track buffer * from the device sink/source. * \param fromTrack on success is set to true if latency was computed directly * from the track timestamp; otherwise set to false if latency was * estimated from the server timestamp. * fromTrack may be nullptr or omitted if not required. * * \returns OK or INVALID_OPERATION on failure. */ status_t getTrackLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { double serverLatencyMs; status_t status = getServerLatencyMs(&serverLatencyMs, fromTrack); if (status == OK) { *latencyMs = serverLatencyMs + bufferLatencyMs(); } return status; } protected: DISALLOW_COPY_AND_ASSIGN(TrackBase); Loading Loading @@ -222,6 +302,10 @@ protected: audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to audio_port_handle_t mPortId; // unique ID for this track used by audio policy bool mIsInvalid; // non-resettable latch, set by invalidate() bool mServerLatencySupported = false; std::atomic<bool> mServerLatencyFromTrack{}; // latency from track or server timestamp. std::atomic<double> mServerLatencyMs{}; // last latency pushed from server thread. }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. Loading
services/audioflinger/Tracks.cpp +33 −16 Original line number Diff line number Diff line Loading @@ -435,7 +435,7 @@ AudioFlinger::PlaybackThread::Track::Track( } mName = TRACK_NAME_PENDING; mDumpLatency = thread->type() == ThreadBase::MIXER; mServerLatencySupported = thread->type() == ThreadBase::MIXER; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId) + Loading Loading @@ -497,7 +497,7 @@ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) "ST L dB R dB VS dB " " Server FrmCnt FrmRdy F Underruns Flushed" "%s\n", mDumpLatency ? " Latency" : ""); isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active) Loading Loading @@ -593,7 +593,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ mFormat, mChannelMask, mAudioTrackServerProxy->getSampleRate(), sampleRate(), mStreamType, 20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))), Loading @@ -610,14 +610,16 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ nowInUnderrun, (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000 ); if (mDumpLatency) { double latencyMs = mAudioTrackServerProxy->getTimestamp().getOutputServerLatencyMs(mSampleRate); if (latencyMs > 0.) { latencyMs += bufferLatencyMs(); result.appendFormat(" %7.3f", latencyMs); if (isServerLatencySupported()) { double latencyMs; bool fromTrack; if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) { // Show latency in msec, followed by 't' if from track timestamp (the most accurate) // or 'k' if estimated from kernel because track frames haven't been presented yet. result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k'); } else { result.appendFormat(" Unknown"); result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new"); } } result.append("\n"); Loading Loading @@ -677,6 +679,13 @@ void AudioFlinger::PlaybackThread::Track::onTimestamp(const ExtendedTimestamp &t mAudioTrackServerProxy->setTimestamp(timestamp); // We do not set drained here, as FastTrack timestamp may not go to very last frame. // Compute latency. // TODO: Consider whether the server latency may be passed in by FastMixer // as a constant for all active FastTracks. const double latencyMs = timestamp.getOutputServerLatencyMs(sampleRate()); mServerLatencyFromTrack.store(true); mServerLatencyMs.store(latencyMs); } // Don't call for fast tracks; the framesReady() could result in priority inversion Loading Loading @@ -1241,7 +1250,7 @@ void AudioFlinger::PlaybackThread::Track::resumeAck() { //To be called with thread lock held void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( int64_t trackFramesReleased, int64_t sinkFramesWritten, const ExtendedTimestamp &timeStamp) { uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) { //update frame map mFrameMap.push(trackFramesReleased, sinkFramesWritten); Loading @@ -1250,6 +1259,7 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( // 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 drained = true; // default assume drained, if no server info found bool checked = false; for (int i = ExtendedTimestamp::LOCATION_MAX - 1; i >= ExtendedTimestamp::LOCATION_SERVER; --i) { Loading @@ -1258,18 +1268,25 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( local.mPosition[i] = mFrameMap.findX(local.mPosition[i]); // check drain state from the latest stage in the pipeline. if (!checked && i <= ExtendedTimestamp::LOCATION_KERNEL) { mAudioTrackServerProxy->setDrained( local.mPosition[i] >= mAudioTrackServerProxy->framesReleased()); drained = local.mPosition[i] >= mAudioTrackServerProxy->framesReleased(); checked = true; } } } if (!checked) { // no server info, assume drained. mAudioTrackServerProxy->setDrained(true); } mAudioTrackServerProxy->setDrained(drained); // Set correction for flushed frames that are not accounted for in released. local.mFlushed = mAudioTrackServerProxy->framesFlushed(); mServerProxy->setTimestamp(local); // Compute latency info. const bool useTrackTimestamp = !drained; const double latencyMs = useTrackTimestamp ? local.getOutputServerLatencyMs(sampleRate()) : timeStamp.getOutputServerLatencyMs(halSampleRate); mServerLatencyFromTrack.store(useTrackTimestamp); mServerLatencyMs.store(latencyMs); } // ---------------------------------------------------------------------------- Loading