Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb6cc755 authored by Andy Hung's avatar Andy Hung
Browse files

TrackMetrics: Fix volume computation

Average volume should be weighted based on active time
not total lifetime.

Improve the numerical accuracy of average volume using
West's algorithm.

Remove unneeded variables.

Test: youtube, play/pause, adb shell dumpsys media.metrics
Bug: 233409482
Merged-In: I9fadb7287bd0bccbf5c74aad5a07eab277303d02
Change-Id: I9fadb7287bd0bccbf5c74aad5a07eab277303d02
(cherry picked from commit a81a4b4c)
parent bb52d577
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -397,6 +397,8 @@ protected:
    int64_t             mLogStartFrames = 0;    // Timestamp frames at start()
    double              mLogLatencyMs = 0.;     // Track the last log latency

    bool                mLogForceVolumeUpdate = true; // force volume update to TrackMetrics.

    TrackMetrics        mTrackMetrics;

    bool                mServerLatencySupported = false;
+27 −28
Original line number Diff line number Diff line
@@ -64,7 +64,6 @@ public:
                    AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str());
        }
        ++mIntervalCount;
        mIntervalStartTimeNs = systemTime();
    }

    void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
@@ -90,11 +89,9 @@ public:
    // Called when we are removed from the Thread.
    void logEndInterval() {
        std::lock_guard l(mLock);
        if (mIntervalStartTimeNs != 0) {
            const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
            mIntervalStartTimeNs = 0;
            mCumulativeTimeNs += elapsedTimeNs;
            mDeviceTimeNs += elapsedTimeNs;
        if (mLastVolumeChangeTimeNs != 0) {
            logVolume_l(mVolume); // flush out the last volume.
            mLastVolumeChangeTimeNs = 0;
        }
    }

@@ -133,20 +130,8 @@ public:

    // may be called multiple times during an interval
    void logVolume(float volume) {
        const int64_t timeNs = systemTime();
        std::lock_guard l(mLock);
        if (mStartVolumeTimeNs == 0) {
            mDeviceVolume = mVolume = volume;
            mLastVolumeChangeTimeNs = mStartVolumeTimeNs = timeNs;
            updateMinMaxVolume(0, mVolume);
            return;
        }
        const int64_t durationNs = timeNs - mLastVolumeChangeTimeNs;
        updateMinMaxVolume(durationNs, mVolume);
        mDeviceVolume = (mDeviceVolume * (mLastVolumeChangeTimeNs - mStartVolumeTimeNs) +
            mVolume * durationNs) / (timeNs - mStartVolumeTimeNs);
        mVolume = volume;
        mLastVolumeChangeTimeNs = timeNs;
        logVolume_l(volume);
    }

    // Use absolute numbers returned by AudioTrackShared.
@@ -158,6 +143,7 @@ public:
    }

private:

    // no lock required - all arguments and constants.
    void deliverDeviceMetrics(const char *eventName, const char *devices) const {
        mediametrics::LogItem(mMetricsId)
@@ -167,6 +153,23 @@ private:
           .record();
    }

    void logVolume_l(float volume) REQUIRES(mLock) {
        const int64_t timeNs = systemTime();
        const int64_t durationNs = mLastVolumeChangeTimeNs == 0
                ? 0 : timeNs - mLastVolumeChangeTimeNs;
        if (durationNs > 0) {
            // See West's algorithm for weighted averages
            // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
            mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
                      / (durationNs + mDeviceTimeNs);
            mDeviceTimeNs += durationNs;
            mCumulativeTimeNs += durationNs;
        }
        updateMinMaxVolume(durationNs, mVolume); // always update.
        mVolume = volume;
        mLastVolumeChangeTimeNs = timeNs;
    }

    void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
        if (mIntervalCount > 0) {
            mediametrics::LogItem item(mMetricsId);
@@ -199,14 +202,12 @@ private:
        // mDevices is not reset by resetIntervalGroupMetrics.

        mIntervalCount = 0;
        mIntervalStartTimeNs = 0;
        // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
        mDeviceTimeNs = 0;

        mVolume = 0.f;
        mDeviceVolume = 0.f;
        mStartVolumeTimeNs = 0;
        mLastVolumeChangeTimeNs = 0;
        mLastVolumeChangeTimeNs = 0;  // last time volume logged, cleared on endInterval
        mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
        mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
        mMinVolumeDurationNs = 0;
@@ -230,14 +231,12 @@ private:

    // Number of intervals and playing time
    int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
    int64_t           mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
    int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0;
    int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;
    int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
    int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;     // time on device.

    // Average volume
    double            mVolume GUARDED_BY(mLock) = 0.f;
    double            mDeviceVolume GUARDED_BY(mLock) = 0.f;
    int64_t           mStartVolumeTimeNs GUARDED_BY(mLock) = 0;
    double            mVolume GUARDED_BY(mLock) = 0.f;       // last set volume.
    double            mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
    int64_t           mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;

    // Min/Max volume
+6 −1
Original line number Diff line number Diff line
@@ -1123,6 +1123,7 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev
                    .mPosition[ExtendedTimestamp::LOCATION_KERNEL];
            mLogLatencyMs = 0.;
        }
        mLogForceVolumeUpdate = true;  // at least one volume logged for metrics when starting.

        if (status == NO_ERROR || status == ALREADY_EXISTS) {
            // for streaming tracks, remove the buffer read stop limit.
@@ -1394,7 +1395,11 @@ void AudioFlinger::PlaybackThread::Track::setFinalVolume(float volume)
    if (mFinalVolume != volume) { // Compare to an epsilon if too many meaningless updates
        mFinalVolume = volume;
        setMetadataHasChanged();
        mTrackMetrics.logVolume(volume);
        mLogForceVolumeUpdate = true;
    }
    if (mLogForceVolumeUpdate) {
        mLogForceVolumeUpdate = false;
        mTrackMetrics.logVolume(mFinalVolume);
    }
}