Loading media/libaaudio/src/client/AudioStreamInternalCapture.cpp +6 −2 Original line number Diff line number Diff line Loading @@ -89,7 +89,11 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t if (mAudioEndpoint.isFreeRunning()) { //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter"); // Update data queue based on the timing model. int64_t estimatedRemoteCounter = mClockModel.convertTimeToPosition(currentNanoTime); // Jitter in the DSP can cause late writes to the FIFO. // This might be caused by resampling. // We want to read the FIFO after the latest possible time // that the DSP could have written the data. int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime); // TODO refactor, maybe use setRemoteCounter() mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter); } Loading Loading @@ -139,7 +143,7 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t // the writeCounter might have just advanced in the background, // causing us to sleep until a later burst. int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst; wakeTime = mClockModel.convertPositionToTime(nextPosition); wakeTime = mClockModel.convertPositionToLatestTime(nextPosition); } break; default: Loading media/libaaudio/src/client/IsochronousClockModel.cpp +76 −26 Original line number Diff line number Diff line Loading @@ -19,12 +19,11 @@ #include <log/log.h> #include <stdint.h> #include <algorithm> #include "utility/AudioClock.h" #include "IsochronousClockModel.h" #define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND) using namespace aaudio; IsochronousClockModel::IsochronousClockModel() Loading @@ -32,7 +31,7 @@ IsochronousClockModel::IsochronousClockModel() , mMarkerNanoTime(0) , mSampleRate(48000) , mFramesPerBurst(64) , mMaxLatenessInNanos(0) , mMaxMeasuredLatenessNanos(0) , mState(STATE_STOPPED) { } Loading @@ -41,8 +40,7 @@ IsochronousClockModel::~IsochronousClockModel() { } void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) { ALOGV("setPositionAndTime(%lld, %lld)", (long long) framePosition, (long long) nanoTime); ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime); mMarkerFramePosition = framePosition; mMarkerNanoTime = nanoTime; } Loading @@ -54,7 +52,9 @@ void IsochronousClockModel::start(int64_t nanoTime) { } void IsochronousClockModel::stop(int64_t nanoTime) { ALOGV("stop(nanos = %lld)\n", (long long) nanoTime); ALOGD("stop(nanos = %lld) max lateness = %d micros\n", (long long) nanoTime, (int) (mMaxMeasuredLatenessNanos / 1000)); setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime); // TODO should we set position? mState = STATE_STOPPED; Loading @@ -69,9 +69,10 @@ bool IsochronousClockModel::isRunning() const { } void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) { // ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu", // (long long)framePosition, // (long long)nanoTime); mTimestampCount++; // Log position and time in CSV format so we can import it easily into spreadsheets. //ALOGD("%s() CSV, %d, %lld, %lld", __func__, //mTimestampCount, (long long)framePosition, (long long)nanoTime); int64_t framesDelta = framePosition - mMarkerFramePosition; int64_t nanosDelta = nanoTime - mMarkerNanoTime; if (nanosDelta < 1000) { Loading Loading @@ -112,20 +113,52 @@ void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nano // Or we may be drifting due to a fast HW clock. //int microsDelta = (int) (nanosDelta / 1000); //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000); // ALOGD("processTimestamp() - STATE_RUNNING - %7d < %7d so %4d micros EARLY", // microsDelta, expectedMicrosDelta, (expectedMicrosDelta - microsDelta)); //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY", //__func__, mTimestampCount, expectedMicrosDelta - microsDelta); setPositionAndTime(framePosition, nanoTime); } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) { // Later than expected timestamp. // int microsDelta = (int) (nanosDelta / 1000); // int expectedMicrosDeadline = (int) ((expectedNanosDelta + mMaxLatenessInNanos) / 1000); // ALOGD("processTimestamp() - STATE_RUNNING - %7d > %7d so %4d micros LATE", // microsDelta, expectedMicrosDeadline, (microsDelta - expectedMicrosDeadline)); // When we are late it may be because of preemption in the kernel or // we may be drifting due to a slow HW clock. setPositionAndTime(framePosition, nanoTime - mMaxLatenessInNanos); } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) { // In this case we do not update mMaxMeasuredLatenessNanos because it // would force it too high. // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta); //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE", //__func__, //mTimestampCount, //measuredLatenessNanos / 1000, //mMaxMeasuredLatenessNanos / 1000, //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000 //); // This typically happens when we are modelling a service instead of a DSP. setPositionAndTime(framePosition, nanoTime - (2 * mBurstPeriodNanos)); } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) { //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos; mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta); //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE", //__func__, //mTimestampCount, //mMaxMeasuredLatenessNanos / 1000, //previousLatenessNanos / 1000, //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000 //); // When we are late, it may be because of preemption in the kernel, // or timing jitter caused by resampling in the DSP, // or we may be drifting due to a slow HW clock. // We add slight drift value just in case there is actual long term drift // forward caused by a slower clock. // If the clock is faster than the model will get pushed earlier // by the code in the preceding branch. // The two opposing forces should allow the model to track the real clock // over a long time. int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos; setPositionAndTime(framePosition, driftingTime); //ALOGD("%s() - #%d, max lateness = %d micros", //__func__, //mTimestampCount, //(int) (mMaxMeasuredLatenessNanos / 1000)); } break; default: Loading @@ -145,9 +178,12 @@ void IsochronousClockModel::setFramesPerBurst(int32_t framesPerBurst) { update(); } // Update expected lateness based on sampleRate and framesPerBurst void IsochronousClockModel::update() { int64_t nanosLate = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS; mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate // Timestamps may be late by up to a burst because we are randomly sampling the time period // after the DSP position is actually updated. mMaxMeasuredLatenessNanos = mBurstPeriodNanos; } int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const { Loading Loading @@ -190,11 +226,25 @@ int64_t IsochronousClockModel::convertTimeToPosition(int64_t nanoTime) const { return position; } int32_t IsochronousClockModel::getLateTimeOffsetNanos() const { // This will never be < 0 because mMaxLatenessNanos starts at // mBurstPeriodNanos and only gets bigger. return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos; } int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const { return convertPositionToTime(framePosition) + getLateTimeOffsetNanos(); } int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const { return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos()); } void IsochronousClockModel::dump() const { ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition); ALOGD("mMarkerNanoTime = %lld", (long long) mMarkerNanoTime); ALOGD("mSampleRate = %6d", mSampleRate); ALOGD("mFramesPerBurst = %6d", mFramesPerBurst); ALOGD("mMaxLatenessInNanos = %6d", mMaxLatenessInNanos); ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos); ALOGD("mState = %6d", mState); } media/libaaudio/src/client/IsochronousClockModel.h +36 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H #include <stdint.h> #include "utility/AudioClock.h" namespace aaudio { Loading Loading @@ -78,6 +79,15 @@ public: */ int64_t convertPositionToTime(int64_t framePosition) const; /** * Calculate the latest estimated time that the stream will be at that position. * The more jittery the clock is then the later this will be. * * @param framePosition * @return time in nanoseconds */ int64_t convertPositionToLatestTime(int64_t framePosition) const; /** * Calculate an estimated position where the stream will be at the specified time. * Loading @@ -86,6 +96,18 @@ public: */ int64_t convertTimeToPosition(int64_t nanoTime) const; /** * Calculate the corresponding estimated position based on the specified time being * the latest possible time. * * For the same nanoTime, this may return an earlier position than * convertTimeToPosition(). * * @param nanoTime * @return position in frames */ int64_t convertLatestTimeToPosition(int64_t nanoTime) const; /** * @param framesDelta difference in frames * @return duration in nanoseconds Loading @@ -101,6 +123,9 @@ public: void dump() const; private: int32_t getLateTimeOffsetNanos() const; enum clock_model_state_t { STATE_STOPPED, STATE_STARTING, Loading @@ -108,13 +133,23 @@ private: STATE_RUNNING }; // Amount of time to drift forward when we get a late timestamp. // This value was calculated to allow tracking of a clock with 50 ppm error. static constexpr int32_t kDriftNanos = 10 * 1000; // TODO review value of kExtraLatenessNanos static constexpr int32_t kExtraLatenessNanos = 100 * 1000; int64_t mMarkerFramePosition; int64_t mMarkerNanoTime; int32_t mSampleRate; int32_t mFramesPerBurst; int32_t mMaxLatenessInNanos; int32_t mBurstPeriodNanos; // Includes mBurstPeriodNanos because we sample randomly over time. int32_t mMaxMeasuredLatenessNanos; clock_model_state_t mState; int32_t mTimestampCount = 0; void update(); }; Loading Loading
media/libaaudio/src/client/AudioStreamInternalCapture.cpp +6 −2 Original line number Diff line number Diff line Loading @@ -89,7 +89,11 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t if (mAudioEndpoint.isFreeRunning()) { //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter"); // Update data queue based on the timing model. int64_t estimatedRemoteCounter = mClockModel.convertTimeToPosition(currentNanoTime); // Jitter in the DSP can cause late writes to the FIFO. // This might be caused by resampling. // We want to read the FIFO after the latest possible time // that the DSP could have written the data. int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime); // TODO refactor, maybe use setRemoteCounter() mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter); } Loading Loading @@ -139,7 +143,7 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t // the writeCounter might have just advanced in the background, // causing us to sleep until a later burst. int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst; wakeTime = mClockModel.convertPositionToTime(nextPosition); wakeTime = mClockModel.convertPositionToLatestTime(nextPosition); } break; default: Loading
media/libaaudio/src/client/IsochronousClockModel.cpp +76 −26 Original line number Diff line number Diff line Loading @@ -19,12 +19,11 @@ #include <log/log.h> #include <stdint.h> #include <algorithm> #include "utility/AudioClock.h" #include "IsochronousClockModel.h" #define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND) using namespace aaudio; IsochronousClockModel::IsochronousClockModel() Loading @@ -32,7 +31,7 @@ IsochronousClockModel::IsochronousClockModel() , mMarkerNanoTime(0) , mSampleRate(48000) , mFramesPerBurst(64) , mMaxLatenessInNanos(0) , mMaxMeasuredLatenessNanos(0) , mState(STATE_STOPPED) { } Loading @@ -41,8 +40,7 @@ IsochronousClockModel::~IsochronousClockModel() { } void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) { ALOGV("setPositionAndTime(%lld, %lld)", (long long) framePosition, (long long) nanoTime); ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime); mMarkerFramePosition = framePosition; mMarkerNanoTime = nanoTime; } Loading @@ -54,7 +52,9 @@ void IsochronousClockModel::start(int64_t nanoTime) { } void IsochronousClockModel::stop(int64_t nanoTime) { ALOGV("stop(nanos = %lld)\n", (long long) nanoTime); ALOGD("stop(nanos = %lld) max lateness = %d micros\n", (long long) nanoTime, (int) (mMaxMeasuredLatenessNanos / 1000)); setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime); // TODO should we set position? mState = STATE_STOPPED; Loading @@ -69,9 +69,10 @@ bool IsochronousClockModel::isRunning() const { } void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) { // ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu", // (long long)framePosition, // (long long)nanoTime); mTimestampCount++; // Log position and time in CSV format so we can import it easily into spreadsheets. //ALOGD("%s() CSV, %d, %lld, %lld", __func__, //mTimestampCount, (long long)framePosition, (long long)nanoTime); int64_t framesDelta = framePosition - mMarkerFramePosition; int64_t nanosDelta = nanoTime - mMarkerNanoTime; if (nanosDelta < 1000) { Loading Loading @@ -112,20 +113,52 @@ void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nano // Or we may be drifting due to a fast HW clock. //int microsDelta = (int) (nanosDelta / 1000); //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000); // ALOGD("processTimestamp() - STATE_RUNNING - %7d < %7d so %4d micros EARLY", // microsDelta, expectedMicrosDelta, (expectedMicrosDelta - microsDelta)); //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY", //__func__, mTimestampCount, expectedMicrosDelta - microsDelta); setPositionAndTime(framePosition, nanoTime); } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) { // Later than expected timestamp. // int microsDelta = (int) (nanosDelta / 1000); // int expectedMicrosDeadline = (int) ((expectedNanosDelta + mMaxLatenessInNanos) / 1000); // ALOGD("processTimestamp() - STATE_RUNNING - %7d > %7d so %4d micros LATE", // microsDelta, expectedMicrosDeadline, (microsDelta - expectedMicrosDeadline)); // When we are late it may be because of preemption in the kernel or // we may be drifting due to a slow HW clock. setPositionAndTime(framePosition, nanoTime - mMaxLatenessInNanos); } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) { // In this case we do not update mMaxMeasuredLatenessNanos because it // would force it too high. // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta); //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE", //__func__, //mTimestampCount, //measuredLatenessNanos / 1000, //mMaxMeasuredLatenessNanos / 1000, //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000 //); // This typically happens when we are modelling a service instead of a DSP. setPositionAndTime(framePosition, nanoTime - (2 * mBurstPeriodNanos)); } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) { //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos; mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta); //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE", //__func__, //mTimestampCount, //mMaxMeasuredLatenessNanos / 1000, //previousLatenessNanos / 1000, //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000 //); // When we are late, it may be because of preemption in the kernel, // or timing jitter caused by resampling in the DSP, // or we may be drifting due to a slow HW clock. // We add slight drift value just in case there is actual long term drift // forward caused by a slower clock. // If the clock is faster than the model will get pushed earlier // by the code in the preceding branch. // The two opposing forces should allow the model to track the real clock // over a long time. int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos; setPositionAndTime(framePosition, driftingTime); //ALOGD("%s() - #%d, max lateness = %d micros", //__func__, //mTimestampCount, //(int) (mMaxMeasuredLatenessNanos / 1000)); } break; default: Loading @@ -145,9 +178,12 @@ void IsochronousClockModel::setFramesPerBurst(int32_t framesPerBurst) { update(); } // Update expected lateness based on sampleRate and framesPerBurst void IsochronousClockModel::update() { int64_t nanosLate = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS; mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate // Timestamps may be late by up to a burst because we are randomly sampling the time period // after the DSP position is actually updated. mMaxMeasuredLatenessNanos = mBurstPeriodNanos; } int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const { Loading Loading @@ -190,11 +226,25 @@ int64_t IsochronousClockModel::convertTimeToPosition(int64_t nanoTime) const { return position; } int32_t IsochronousClockModel::getLateTimeOffsetNanos() const { // This will never be < 0 because mMaxLatenessNanos starts at // mBurstPeriodNanos and only gets bigger. return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos; } int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const { return convertPositionToTime(framePosition) + getLateTimeOffsetNanos(); } int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const { return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos()); } void IsochronousClockModel::dump() const { ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition); ALOGD("mMarkerNanoTime = %lld", (long long) mMarkerNanoTime); ALOGD("mSampleRate = %6d", mSampleRate); ALOGD("mFramesPerBurst = %6d", mFramesPerBurst); ALOGD("mMaxLatenessInNanos = %6d", mMaxLatenessInNanos); ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos); ALOGD("mState = %6d", mState); }
media/libaaudio/src/client/IsochronousClockModel.h +36 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H #include <stdint.h> #include "utility/AudioClock.h" namespace aaudio { Loading Loading @@ -78,6 +79,15 @@ public: */ int64_t convertPositionToTime(int64_t framePosition) const; /** * Calculate the latest estimated time that the stream will be at that position. * The more jittery the clock is then the later this will be. * * @param framePosition * @return time in nanoseconds */ int64_t convertPositionToLatestTime(int64_t framePosition) const; /** * Calculate an estimated position where the stream will be at the specified time. * Loading @@ -86,6 +96,18 @@ public: */ int64_t convertTimeToPosition(int64_t nanoTime) const; /** * Calculate the corresponding estimated position based on the specified time being * the latest possible time. * * For the same nanoTime, this may return an earlier position than * convertTimeToPosition(). * * @param nanoTime * @return position in frames */ int64_t convertLatestTimeToPosition(int64_t nanoTime) const; /** * @param framesDelta difference in frames * @return duration in nanoseconds Loading @@ -101,6 +123,9 @@ public: void dump() const; private: int32_t getLateTimeOffsetNanos() const; enum clock_model_state_t { STATE_STOPPED, STATE_STARTING, Loading @@ -108,13 +133,23 @@ private: STATE_RUNNING }; // Amount of time to drift forward when we get a late timestamp. // This value was calculated to allow tracking of a clock with 50 ppm error. static constexpr int32_t kDriftNanos = 10 * 1000; // TODO review value of kExtraLatenessNanos static constexpr int32_t kExtraLatenessNanos = 100 * 1000; int64_t mMarkerFramePosition; int64_t mMarkerNanoTime; int32_t mSampleRate; int32_t mFramesPerBurst; int32_t mMaxLatenessInNanos; int32_t mBurstPeriodNanos; // Includes mBurstPeriodNanos because we sample randomly over time. int32_t mMaxMeasuredLatenessNanos; clock_model_state_t mState; int32_t mTimestampCount = 0; void update(); }; Loading