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

Commit c4e9deef authored by Brian Lindahl's avatar Brian Lindahl
Browse files

Add histogram metrics for video playback freezes

Bug: 234833109
Test: atest VideoRenderQualityTracker_test
Change-Id: I5e3a8b02336bbcbe639aad37f0914ba0f98d8d44
parent ae8311b1
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -86,6 +86,11 @@ void mediametrics_setRate(mediametrics_handle_t handle, attr_t attr,
    if (item != NULL) item->setRate(attr, count, duration);
}

void mediametrics_setString(mediametrics_handle_t handle, attr_t attr,
                                 const std::string &string) {
    mediametrics_setCString(handle, attr, string.c_str());
}

void mediametrics_setCString(mediametrics_handle_t handle, attr_t attr,
                                 const char *value) {
    Item *item = (Item *) handle;
@@ -152,6 +157,14 @@ bool mediametrics_getRate(mediametrics_handle_t handle, attr_t attr,
    return item->getRate(attr, count, duration, rate);
}

bool mediametrics_getString(mediametrics_handle_t handle, attr_t attr,
                                 std::string *string) {
    Item *item = (Item *) handle;
    if (item == NULL) return false;

    return item->getString(attr, string);
}

// NB: caller owns the string that comes back, is responsible for freeing it
bool mediametrics_getCString(mediametrics_handle_t handle, attr_t attr,
                                 char **value) {
+8 −1
Original line number Diff line number Diff line
@@ -95,4 +95,11 @@ bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, siz

__END_DECLS

#ifdef __cplusplus
#include <string>
void mediametrics_setString(mediametrics_handle_t handle, attr_t attr,
                            const std::string &value);
bool mediametrics_getString(mediametrics_handle_t handle, attr_t attr, std::string *value);
#endif // __cplusplus

#endif
+3 −3
Original line number Diff line number Diff line
@@ -1048,6 +1048,9 @@ public:
        }
        return true;
    }
    bool getString(const char *key, std::string *value) const {
        return get(key, value);
    }
    // Caller owns the returned string
    bool getCString(const char *key, char **value) const {
        std::string s;
@@ -1057,9 +1060,6 @@ public:
        }
        return false;
    }
    bool getString(const char *key, std::string *value) const {
        return get(key, value);
    }

    const Prop::Elem* get(const char *key) const {
        const Prop *prop = findProp(key);
+21 −0
Original line number Diff line number Diff line
@@ -206,6 +206,15 @@ static const char *kCodecFramerateContent = "android.media.mediacodec.framerate.
static const char *kCodecFramerateDesired = "android.media.mediacodec.framerate.desired";
static const char *kCodecFramerateActual = "android.media.mediacodec.framerate.actual";

static const char *kCodecFreezeCount = "android.media.mediacodec.freeze.count";
static const char *kCodecFreezeDurationAverage = "android.media.mediacodec.freeze.duration.average";
static const char *kCodecFreezeDurationMax = "android.media.mediacodec.freeze.duration.max";
static const char *kCodecFreezeDurationHistogram =
        "android.media.mediacodec.freeze.duration.histogram";
static const char *kCodecFreezeDistanceAverage = "android.media.mediacodec.freeze.distance.average";
static const char *kCodecFreezeDistanceHistogram =
        "android.media.mediacodec.freeze.distance.histogram";

/* -1: shaper disabled
   >=0: number of fields changed */
static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped";
@@ -1114,6 +1123,18 @@ void MediaCodec::updateMediametrics() {
            mediametrics_setDouble(mMetricsHandle, kCodecFramerateDesired, m.desiredFrameRate);
            mediametrics_setDouble(mMetricsHandle, kCodecFramerateActual, m.actualFrameRate);
        }
        if (m.freezeDurationMsHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.freezeDurationMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, histogram.getCount());
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationAverage, histogram.getAvg());
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationMax, histogram.getMax());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDurationHistogram, histogram.emit());
        }
        if (m.freezeDistanceMsHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.freezeDistanceMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDistanceAverage, histogram.getAvg());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceHistogram, histogram.emit());
        }
    }

    if (mLatencyHist.getCount() != 0 ) {
+96 −41
Original line number Diff line number Diff line
@@ -21,57 +21,104 @@

#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <sstream>
#include <stdio.h>

namespace android {


MediaHistogram::MediaHistogram() {
    mBuckets = nullptr;
    mBucketLimits = nullptr;
    mBucketCount = 0;
    mFloor = mCeiling = mWidth = 0;
    mBelow = 0;
    mAbove = 0;
    mSum = 0;
    mCount = 0;
    mMin = INT64_MAX;
    mMax = INT64_MIN;
}

void MediaHistogram::clear() {
    if (mBuckets != nullptr) {
        free(mBuckets);
        mBuckets = nullptr;
    }
    if (mBucketLimits != nullptr) {
        free(mBucketLimits);
        mBucketLimits = nullptr;
    }
    mBucketCount = 0;
}

bool MediaHistogram::setup(int bucketCount, int64_t width, int64_t floor)
{
    if (bucketCount <= 0 || width <= 0) {
        return false;
    }

    // get histogram buckets
    if (bucketCount == mBucketCount && mBuckets != NULL) {
        // reuse our existing buffer
        memset(mBuckets, 0, sizeof(*mBuckets) * mBucketCount);
    } else {
        // get a new pre-zeroed buffer
        int64_t *newbuckets = (int64_t *)calloc(bucketCount, sizeof (*mBuckets));
        if (newbuckets == NULL) {
            goto bad;
        }
        if (mBuckets != NULL)
            free(mBuckets);
        mBuckets = newbuckets;
    if (!allocate(bucketCount, false)) {
        return false;
    }

    mWidth = width;
    mFloor = floor;
    mCeiling = floor + bucketCount * width;
    mBucketCount = bucketCount;

    mMin = INT64_MAX;
    mMax = INT64_MIN;
    mSum = 0;
    mCount = 0;
    mBelow = mAbove = 0;

    return true;
}

  bad:
    if (mBuckets != NULL) {
        free(mBuckets);
        mBuckets = NULL;
bool MediaHistogram::setup(const std::vector<int64_t> &bucketLimits) {
    if (bucketLimits.size() <= 1) {
        return false;
    }
    int bucketCount = bucketLimits.size() - 1;
    if (!allocate(bucketCount, true)) {
        return false;
    }

    mWidth = -1;
    mFloor = bucketLimits[0];
    for (int i = 0; i < bucketCount; ++i) {
        mBucketLimits[i] = bucketLimits[i + 1];
    }
    mCeiling = bucketLimits[bucketCount];
    mMin = INT64_MAX;
    mMax = INT64_MIN;
    mSum = 0;
    mCount = 0;
    mBelow = mAbove = 0;
    return true;
}

bool MediaHistogram::allocate(int bucketCount, bool withBucketLimits) {
    assert(bucketCount > 0);
    if (bucketCount != mBucketCount) {
        clear();
        mBuckets = (int64_t *) calloc(bucketCount, sizeof(*mBuckets));
        if (mBuckets == nullptr) {
            return false;
        }
    }
    if (withBucketLimits && mBucketLimits == nullptr) {
        mBucketLimits = (int64_t *) calloc(bucketCount, sizeof(*mBucketLimits));
        if (mBucketLimits == nullptr) {
            clear();
            return false;
        }
    }
    mBucketCount = bucketCount;
    memset(mBuckets, 0, sizeof(*mBuckets) * mBucketCount);
    return true;
}

void MediaHistogram::insert(int64_t sample)
{
    // histogram is not set up
    if (mBuckets == NULL) {
    if (mBuckets == nullptr) {
        return;
    }

@@ -84,35 +131,43 @@ void MediaHistogram::insert(int64_t sample)
        mBelow++;
    } else if (sample >= mCeiling) {
        mAbove++;
    } else {
    } else if (mBucketLimits == nullptr) {
        int64_t slot = (sample - mFloor) / mWidth;
        assert(slot < mBucketCount);
        mBuckets[slot]++;
    } else {
        // A binary search might be more efficient for large number of buckets, but it is expected
        // that there will never be a large amount of buckets, so keep the code simple.
        for (int slot = 0; slot < mBucketCount; ++slot) {
            if (sample < mBucketLimits[slot]) {
                mBuckets[slot]++;
                break;
            }
        }
    }
    return;
}

std::string MediaHistogram::emit()
std::string MediaHistogram::emit() const
{
    std::string value;
    char buffer[64];

    // emits:  width,Below{bucket0,bucket1,...., bucketN}above
    // unconfigured will emit: 0,0{}0
    // emits:  floor,width,below{bucket0,bucket1,...., bucketN}above
    // or.. emits:  below{bucket0,bucket1,...., bucketN}above
    // unconfigured will emit: 0,0,0{}0
    // XXX: is this best representation?
    snprintf(buffer, sizeof(buffer), "%" PRId64 ",%" PRId64 ",%" PRId64 "{",
             mFloor, mWidth, mBelow);
    value = buffer;
    std::stringstream ss;
    if (mBucketLimits == nullptr) {
        ss << mFloor << "," << mWidth << "," << mBelow << "{";
    } else {
        ss << mBelow << "{";
    }
    for (int i = 0; i < mBucketCount; i++) {
        if (i != 0) {
            value = value + ",";
            ss << ",";
        }
        snprintf(buffer, sizeof(buffer), "%" PRId64, mBuckets[i]);
        value = value + buffer;
        ss << mBuckets[i];
    }
    snprintf(buffer, sizeof(buffer), "}%" PRId64 , mAbove);
    value = value + buffer;
    return value;
    ss << "}" << mAbove;
    return ss.str();
}

} // android
Loading