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

Commit 590e385e authored by Brian Lindahl's avatar Brian Lindahl
Browse files

Add atom reporting for MediaCodecRendered

Since statsd doesn't allow one to create metrics off of arbitrary
indexes into repeated fields (only first, second last), the freeze
and judder scores are fabricated device-side based on configurable
tables that would mimic what we'd have on server side if statsd
supported this feature.

Bug: 234833109
Test: statsd_testdrive while playing YouTube videos
Test: atest VideoRenderQualityTracker_test
Change-Id: Ic1138abce75f0cdfa751b4adf258fe07347ed369
parent 3b5d4b2e
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -255,7 +255,6 @@ cc_library {
        "MediaCodecSource.cpp",
        "MediaExtractor.cpp",
        "MediaExtractorFactory.cpp",
        "MediaHistogram.cpp",
        "MediaSource.cpp",
        "MediaSync.cpp",
        "MediaTrack.cpp",
+73 −49
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <set>
#include <random>
#include <stdlib.h>

#include <inttypes.h>
#include <stdlib.h>
#include <dlfcn.h>
@@ -108,7 +107,9 @@ static const char *kCodecModeAudio = "audio";
static const char *kCodecModeImage = "image";
static const char *kCodecModeUnknown = "unknown";
static const char *kCodecEncoder = "android.media.mediacodec.encoder"; /* 0,1 */
static const char *kCodecHardware = "android.media.mediacodec.hardware"; /* 0,1 */
static const char *kCodecSecure = "android.media.mediacodec.secure";   /* 0, 1 */
static const char *kCodecTunneled = "android.media.mediacodec.tunneled"; /* 0,1 */
static const char *kCodecWidth = "android.media.mediacodec.width";     /* 0..n */
static const char *kCodecHeight = "android.media.mediacodec.height";   /* 0..n */
static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees";  /* 0/90/180/270 */
@@ -172,9 +173,9 @@ static const char *kCodecConfigColorTransfer = "android.media.mediacodec.config-
static const char *kCodecParsedColorStandard = "android.media.mediacodec.parsed-color-standard";
static const char *kCodecParsedColorRange = "android.media.mediacodec.parsed-color-range";
static const char *kCodecParsedColorTransfer = "android.media.mediacodec.parsed-color-transfer";
static const char *kCodecHDRStaticInfo = "android.media.mediacodec.hdr-static-info";
static const char *kCodecHDR10PlusInfo = "android.media.mediacodec.hdr10-plus-info";
static const char *kCodecHDRFormat = "android.media.mediacodec.hdr-format";
static const char *kCodecHdrStaticInfo = "android.media.mediacodec.hdr-static-info";
static const char *kCodecHdr10PlusInfo = "android.media.mediacodec.hdr10-plus-info";
static const char *kCodecHdrFormat = "android.media.mediacodec.hdr-format";
// array/sync/async/block modes
static const char *kCodecArrayMode = "android.media.mediacodec.array-mode";
static const char *kCodecOperationMode = "android.media.mediacodec.operation-mode";
@@ -195,35 +196,44 @@ static const char *kCodecRecentLatencyMin = "android.media.mediacodec.recent.min
static const char *kCodecRecentLatencyAvg = "android.media.mediacodec.recent.avg";      /* in us */
static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n";
static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist";    /* in us */
static const char *kCodecPlaybackDurationSec =
        "android.media.mediacodec.playback-duration-sec"; /* in sec */

static const char *kCodecFramesReleased = "android.media.mediacodec.frames.released";
static const char *kCodecFramesRendered = "android.media.mediacodec.frames.rendered";
static const char *kCodecFramesSkipped = "android.media.mediacodec.frames.skipped";
static const char *kCodecFramesDropped = "android.media.mediacodec.frames.dropped";
static const char *kCodecFramerateContent = "android.media.mediacodec.framerate.content";
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";

static const char *kCodecJudderCount = "android.media.mediacodec.judder.count";
static const char *kCodecJudderScoreAverage = "android.media.mediacodec.judder.average";
static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder.max";
static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder.histogram";

/* -1: shaper disabled
   >=0: number of fields changed */
static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped";

// Render metrics
static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec";
static const char *kCodecFirstRenderTimeUs = "android.media.mediacodec.first-render-time-us";
static const char *kCodecFramesReleased = "android.media.mediacodec.frames-released";
static const char *kCodecFramesRendered = "android.media.mediacodec.frames-rendered";
static const char *kCodecFramesDropped = "android.media.mediacodec.frames-dropped";
static const char *kCodecFramesSkipped = "android.media.mediacodec.frames-skipped";
static const char *kCodecFramerateContent = "android.media.mediacodec.framerate-content";
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 *kCodecFreezeScore = "android.media.mediacodec.freeze-score";
static const char *kCodecFreezeRate = "android.media.mediacodec.freeze-rate";
static const char *kCodecFreezeDurationMsAvg = "android.media.mediacodec.freeze-duration-ms-avg";
static const char *kCodecFreezeDurationMsMax = "android.media.mediacodec.freeze-duration-ms-max";
static const char *kCodecFreezeDurationMsHistogram =
        "android.media.mediacodec.freeze-duration-ms-histogram";
static const char *kCodecFreezeDurationMsHistogramBuckets =
        "android.media.mediacodec.freeze-duration-ms-histogram-buckets";
static const char *kCodecFreezeDistanceMsAvg = "android.media.mediacodec.freeze-distance-ms-avg";
static const char *kCodecFreezeDistanceMsHistogram =
        "android.media.mediacodec.freeze-distance-ms-histogram";
static const char *kCodecFreezeDistanceMsHistogramBuckets =
        "android.media.mediacodec.freeze-distance-ms-histogram-buckets";
static const char *kCodecJudderCount = "android.media.mediacodec.judder-count";
static const char *kCodecJudderScore = "android.media.mediacodec.judder-score";
static const char *kCodecJudderRate = "android.media.mediacodec.judder-rate";
static const char *kCodecJudderScoreAvg = "android.media.mediacodec.judder-score-avg";
static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder-score-max";
static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder-score-histogram";
static const char *kCodecJudderScoreHistogramBuckets =
        "android.media.mediacodec.judder-score-histogram-buckets";

// XXX suppress until we get our representation right
static bool kEmitHistogram = false;

@@ -1120,6 +1130,7 @@ void MediaCodec::updateMediametrics() {
    {
        const VideoRenderQualityMetrics &m = mVideoRenderQualityTracker.getMetrics();
        if (m.frameRenderedCount > 0) {
            mediametrics_setInt64(mMetricsHandle, kCodecFirstRenderTimeUs, m.firstRenderTimeUs);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesReleased, m.frameReleasedCount);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesRendered, m.frameRenderedCount);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesSkipped, m.frameSkippedCount);
@@ -1129,23 +1140,33 @@ void MediaCodec::updateMediametrics() {
            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());
            const MediaHistogram<int32_t> &h = m.freezeDurationMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeScore, m.freezeScore);
            mediametrics_setDouble(mMetricsHandle, kCodecFreezeRate, m.freezeRate);
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, h.getCount());
            mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsAvg, h.getAvg());
            mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsMax, h.getMax());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogram, h.emit());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogramBuckets,
                                   h.emitBuckets());
        }
        if (m.freezeDistanceMsHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.freezeDistanceMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDistanceAverage, histogram.getAvg());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceHistogram, histogram.emit());
            const MediaHistogram<int32_t> &h = m.freezeDistanceMsHistogram;
            mediametrics_setInt32(mMetricsHandle, kCodecFreezeDistanceMsAvg, h.getAvg());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogram, h.emit());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogramBuckets,
                                   h.emitBuckets());
        }
        if (m.judderScoreHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.judderScoreHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, histogram.getCount());
            mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreAverage, histogram.getAvg());
            mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreMax, histogram.getMax());
            mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, histogram.emit());
            const MediaHistogram<int32_t> &h = m.judderScoreHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecJudderScore, m.judderScore);
            mediametrics_setDouble(mMetricsHandle, kCodecJudderRate, m.judderRate);
            mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, h.getCount());
            mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreAvg, h.getAvg());
            mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreMax, h.getMax());
            mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, h.emit());
            mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogramBuckets,
                                   h.emitBuckets());
        }
    }

@@ -1227,14 +1248,14 @@ void MediaCodec::updateHdrMetrics(bool isConfig) {
            && ColorUtils::isHDRStaticInfoValid(&info)) {
        mHdrInfoFlags |= kFlagHasHdrStaticInfo;
    }
    mediametrics_setInt32(mMetricsHandle, kCodecHDRStaticInfo,
    mediametrics_setInt32(mMetricsHandle, kCodecHdrStaticInfo,
            (mHdrInfoFlags & kFlagHasHdrStaticInfo) ? 1 : 0);
    sp<ABuffer> hdr10PlusInfo;
    if (mOutputFormat->findBuffer("hdr10-plus-info", &hdr10PlusInfo)
            && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0) {
        mHdrInfoFlags |= kFlagHasHdr10PlusInfo;
    }
    mediametrics_setInt32(mMetricsHandle, kCodecHDR10PlusInfo,
    mediametrics_setInt32(mMetricsHandle, kCodecHdr10PlusInfo,
            (mHdrInfoFlags & kFlagHasHdr10PlusInfo) ? 1 : 0);

    // hdr format
@@ -1247,7 +1268,7 @@ void MediaCodec::updateHdrMetrics(bool isConfig) {
            && codedFormat->findInt32(KEY_PROFILE, &profile)
            && colorTransfer != -1) {
        hdr_format hdrFormat = getHdrFormat(mime, profile, colorTransfer);
        mediametrics_setInt32(mMetricsHandle, kCodecHDRFormat, static_cast<int>(hdrFormat));
        mediametrics_setInt32(mMetricsHandle, kCodecHdrFormat, static_cast<int>(hdrFormat));
    }
}

@@ -1355,9 +1376,8 @@ void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) {
        return;
    }

    MediaHistogram recentHist;

    // build an empty histogram
    MediaHistogram<int64_t> recentHist;
    recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);

    // stuff it with the samples in the ring buffer
@@ -3593,8 +3613,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

                                setState(UNINITIALIZED);
                            } else {
                                setState(
                                        (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
                                setState((mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
                            }
                            break;
                        }
@@ -3719,6 +3738,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0);
                    }

                    mediametrics_setInt32(mMetricsHandle, kCodecHardware,
                                          MediaCodecList::isSoftwareCodec(mComponentName) ? 0 : 1);

                    mResourceManagerProxy->addResource(MediaResource::CodecResource(
                            mFlags & kFlagIsSecure, toMediaResourceSubType(mDomain)));

@@ -4133,6 +4155,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    if (mIsSurfaceToDisplay) {
                        mVideoRenderQualityTracker.resetForDiscontinuity();
                    }

                    // Notify the RM that the codec has been stopped.
                    ClientConfigParcel clientConfig;
                    initClientConfigParcel(clientConfig);
@@ -4443,6 +4466,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            } else {
                mTunneled = false;
            }
            mediametrics_setInt32(mMetricsHandle, kCodecTunneled, mTunneled ? 1 : 0);

            int32_t background = 0;
            if (format->findInt32("android._background-mode", &background) && background) {
+0 −173
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "MediaHistogram"
#include <utils/Log.h>

#include <media/stagefright/MediaHistogram.h>

#include <assert.h>
#include <inttypes.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;
    }
    if (!allocate(bucketCount, false)) {
        return false;
    }
    mWidth = width;
    mFloor = floor;
    mCeiling = floor + bucketCount * width;
    mMin = INT64_MAX;
    mMax = INT64_MIN;
    mSum = 0;
    mCount = 0;
    mBelow = mAbove = 0;
    return true;
}

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 == nullptr) {
        return;
    }

    mCount++;
    mSum += sample;
    if (mMin > sample) mMin = sample;
    if (mMax < sample) mMax = sample;

    if (sample < mFloor) {
        mBelow++;
    } else if (sample >= mCeiling) {
        mAbove++;
    } 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() const
{
    // 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?
    std::stringstream ss;
    if (mBucketLimits == nullptr) {
        ss << mFloor << "," << mWidth << "," << mBelow << "{";
    } else {
        ss << mBelow << "{";
    }
    for (int i = 0; i < mBucketCount; i++) {
        if (i != 0) {
            ss << ",";
        }
        ss << mBuckets[i];
    }
    ss << "}" << mAbove;
    return ss.str();
}

} // android
+75 −7
Original line number Diff line number Diff line
@@ -25,8 +25,16 @@

namespace android {

static constexpr float FRAME_RATE_UNDETERMINED = VideoRenderQualityMetrics::FRAME_RATE_UNDETERMINED;
static constexpr float FRAME_RATE_24_3_2_PULLDOWN =
        VideoRenderQualityMetrics::FRAME_RATE_24_3_2_PULLDOWN;

VideoRenderQualityMetrics::VideoRenderQualityMetrics() {
    firstFrameRenderTimeUs = 0;
    clear();
}

void VideoRenderQualityMetrics::clear() {
    firstRenderTimeUs = 0;
    frameReleasedCount = 0;
    frameRenderedCount = 0;
    frameDroppedCount = 0;
@@ -34,9 +42,14 @@ VideoRenderQualityMetrics::VideoRenderQualityMetrics() {
    contentFrameRate = FRAME_RATE_UNDETERMINED;
    desiredFrameRate = FRAME_RATE_UNDETERMINED;
    actualFrameRate = FRAME_RATE_UNDETERMINED;
    freezeDurationMsHistogram.clear();
    freezeDistanceMsHistogram.clear();
    judderScoreHistogram.clear();
}

VideoRenderQualityTracker::Configuration::Configuration() {
    enabled = true;

    // Assume that the app is skipping frames because it's detected that the frame couldn't be
    // rendered in time.
    areSkippedFramesDropped = true;
@@ -53,26 +66,32 @@ VideoRenderQualityTracker::Configuration::Configuration() {

    // Freeze configuration
    freezeDurationMsHistogramBuckets = {1, 20, 40, 60, 80, 100, 120, 150, 175, 225, 300, 400, 500};
    freezeDurationMsHistogramToScore = {1,  1,  1,  1,  1,   1,   1,   1,   1,   1,   1,   1,   1};
    freezeDistanceMsHistogramBuckets = {0, 20, 100, 400, 1000, 2000, 3000, 4000, 8000, 15000, 30000,
                                        60000};

    // Judder configuration
    judderErrorToleranceUs = 2000;
    judderScoreHistogramBuckets = {1, 4, 5, 9, 11, 20, 30, 40, 50, 60, 70, 80};
    judderScoreHistogramToScore = {1, 1, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1};
}

VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) {
    configureHistograms(mMetrics, mConfiguration);
    resetForDiscontinuity();
    clear();
}

VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) :
        mConfiguration(configuration) {
    configureHistograms(mMetrics, mConfiguration);
    resetForDiscontinuity();
    clear();
}

void VideoRenderQualityTracker::onFrameSkipped(int64_t contentTimeUs) {
    if (!mConfiguration.enabled) {
        return;
    }

    // Frames skipped at the beginning shouldn't really be counted as skipped frames, since the
    // app might be seeking to a starting point that isn't the first key frame.
    if (mLastRenderTimeUs == -1) {
@@ -90,6 +109,10 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs) {

void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs,
                                                int64_t desiredRenderTimeNs) {
    if (!mConfiguration.enabled) {
        return;
    }

    int64_t desiredRenderTimeUs = desiredRenderTimeNs / 1000;
    resetIfDiscontinuity(contentTimeUs, desiredRenderTimeUs);
    mMetrics.frameReleasedCount++;
@@ -98,8 +121,15 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs,
}

void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t actualRenderTimeNs) {
    if (!mConfiguration.enabled) {
        return;
    }

    int64_t actualRenderTimeUs = actualRenderTimeNs / 1000;

    if (mLastRenderTimeUs != -1) {
        mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
    }
    // Now that a frame has been rendered, the previously skipped frames can be processed as skipped
    // frames since the app is not skipping them to terminate playback.
    for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) {
@@ -131,10 +161,47 @@ void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t a
    mLastRenderTimeUs = actualRenderTimeUs;
}

const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() const {
const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() {
    if (!mConfiguration.enabled) {
        return mMetrics;
    }

    mMetrics.freezeScore = 0;
    if (mConfiguration.freezeDurationMsHistogramToScore.size() ==
        mMetrics.freezeDurationMsHistogram.size()) {
        for (int i = 0; i < mMetrics.freezeDurationMsHistogram.size(); ++i) {
            int32_t count = 0;
            for (int j = i; j < mMetrics.freezeDurationMsHistogram.size(); ++j) {
                count += mMetrics.freezeDurationMsHistogram[j];
            }
            mMetrics.freezeScore += count / mConfiguration.freezeDurationMsHistogramToScore[i];
        }
    }
    mMetrics.freezeRate = float(double(mMetrics.freezeDurationMsHistogram.getSum()) /
            mRenderDurationMs);

    mMetrics.judderScore = 0;
    if (mConfiguration.judderScoreHistogramToScore.size() == mMetrics.judderScoreHistogram.size()) {
        for (int i = 0; i < mMetrics.judderScoreHistogram.size(); ++i) {
            int32_t count = 0;
            for (int j = i; j < mMetrics.judderScoreHistogram.size(); ++j) {
                count += mMetrics.judderScoreHistogram[j];
            }
            mMetrics.judderScore += count / mConfiguration.judderScoreHistogramToScore[i];
        }
    }
    mMetrics.judderRate = float(double(mMetrics.judderScoreHistogram.getCount()) /
            (mMetrics.frameReleasedCount + mMetrics.frameSkippedCount));

    return mMetrics;
}

void VideoRenderQualityTracker::clear() {
    mRenderDurationMs = 0;
    mMetrics.clear();
    resetForDiscontinuity();
}

void VideoRenderQualityTracker::resetForDiscontinuity() {
    mLastContentTimeUs = -1;
    mLastRenderTimeUs = -1;
@@ -220,11 +287,12 @@ void VideoRenderQualityTracker::processMetricsForRenderedFrame(int64_t contentTi
                                                               int64_t desiredRenderTimeUs,
                                                               int64_t actualRenderTimeUs) {
    // Capture the timestamp at which the first frame was rendered
    if (mMetrics.firstFrameRenderTimeUs == 0) {
        mMetrics.firstFrameRenderTimeUs = actualRenderTimeUs;
    if (mMetrics.firstRenderTimeUs == 0) {
        mMetrics.firstRenderTimeUs = actualRenderTimeUs;
    }

    mMetrics.frameRenderedCount++;

    // The content time is -1 when it was rendered after a discontinuity (e.g. seek) was detected.
    // So, even though a frame was rendered, it's impact on the user is insignificant, so don't do
    // anything other than count it as a rendered frame.
@@ -352,7 +420,7 @@ float VideoRenderQualityTracker::detectFrameRate(const FrameDurationUs &duration
    // Only determine frame rate if the render durations are stable across 3 frames
    if (abs(durationUs[0] - durationUs[1]) > c.frameRateDetectionToleranceUs ||
        abs(durationUs[0] - durationUs[2]) > c.frameRateDetectionToleranceUs) {
        return is32pulldown(durationUs, c) ? FRAME_RATE_24HZ_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED;
        return is32pulldown(durationUs, c) ? FRAME_RATE_24_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED;
    }
    return 1000.0 * 1000.0 / durationUs[0];
}
+1 −1
Original line number Diff line number Diff line
@@ -715,7 +715,7 @@ private:
    int mRecentHead;
    Mutex mRecentLock;

    MediaHistogram mLatencyHist;
    MediaHistogram<int64_t> mLatencyHist;

    // An unique ID for the codec - Used by the metrics.
    uint64_t mCodecId = 0;
Loading