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
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 09d2802..f1534c9 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -255,7 +255,6 @@
"MediaCodecSource.cpp",
"MediaExtractor.cpp",
"MediaExtractorFactory.cpp",
- "MediaHistogram.cpp",
"MediaSource.cpp",
"MediaSync.cpp",
"MediaTrack.cpp",
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index bd56b18..c02573e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -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 *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 *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 *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;
@@ -1118,8 +1128,9 @@
// Video rendering quality metrics
{
- const VideoRenderQualityMetrics& m = mVideoRenderQualityTracker.getMetrics();
+ 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 @@
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 @@
&& 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 @@
&& 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 @@
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 @@
setState(UNINITIALIZED);
} else {
- setState(
- (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+ setState((mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
}
break;
}
@@ -3719,6 +3738,9 @@
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 @@
if (mIsSurfaceToDisplay) {
mVideoRenderQualityTracker.resetForDiscontinuity();
}
+
// Notify the RM that the codec has been stopped.
ClientConfigParcel clientConfig;
initClientConfigParcel(clientConfig);
@@ -4443,6 +4466,7 @@
} else {
mTunneled = false;
}
+ mediametrics_setInt32(mMetricsHandle, kCodecTunneled, mTunneled ? 1 : 0);
int32_t background = 0;
if (format->findInt32("android._background-mode", &background) && background) {
diff --git a/media/libstagefright/MediaHistogram.cpp b/media/libstagefright/MediaHistogram.cpp
deleted file mode 100644
index 3dd67b8..0000000
--- a/media/libstagefright/MediaHistogram.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index f995e25..1072cdd 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -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 @@
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 @@
// 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,
int64_t desiredRenderTimeNs) {
+ if (!mConfiguration.enabled) {
+ return;
+ }
+
int64_t desiredRenderTimeUs = desiredRenderTimeNs / 1000;
resetIfDiscontinuity(contentTimeUs, desiredRenderTimeUs);
mMetrics.frameReleasedCount++;
@@ -98,8 +121,15 @@
}
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 @@
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 @@
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 @@
// 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];
}
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index e534ad3..52d7d3d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -715,7 +715,7 @@
int mRecentHead;
Mutex mRecentLock;
- MediaHistogram mLatencyHist;
+ MediaHistogram<int64_t> mLatencyHist;
// An unique ID for the codec - Used by the metrics.
uint64_t mCodecId = 0;
diff --git a/media/libstagefright/include/media/stagefright/MediaHistogram.h b/media/libstagefright/include/media/stagefright/MediaHistogram.h
index 3c12901..da8415a 100644
--- a/media/libstagefright/include/media/stagefright/MediaHistogram.h
+++ b/media/libstagefright/include/media/stagefright/MediaHistogram.h
@@ -17,39 +17,197 @@
#ifndef MEDIA_HISTOGRAM_H_
#define MEDIA_HISTOGRAM_H_
+#include <limits>
+#include <sstream>
#include <string>
#include <vector>
namespace android {
+template<typename T>
class MediaHistogram {
- public:
+public:
MediaHistogram();
- ~MediaHistogram() { clear(); };
void clear();
- bool setup(int bucketCount, int64_t width, int64_t floor = 0);
- bool setup(const std::vector<int64_t> &bucketLimits);
- void insert(int64_t sample);
- int64_t getMin() const { return mMin; }
- int64_t getMax() const { return mMax; }
- int64_t getCount() const { return mCount; }
- int64_t getSum() const { return mSum; }
- int64_t getAvg() const { return mSum / (mCount == 0 ? 1 : mCount); }
+ bool setup(int bucketCount, T width, T floor = 0);
+ bool setup(const std::vector<T> &bucketLimits);
+ void insert(T sample);
+ size_t size();
+ int64_t operator[](int);
+ T getMin() const { return mMin; }
+ T getMax() const { return mMax; }
+ T getCount() const { return mCount; }
+ T getSum() const { return mSum; }
+ T getAvg() const { return mSum / (mCount == 0 ? 1 : mCount); }
+ T getPercentile(int) const;
std::string emit() const;
+ std::string emitBuckets() const;
private:
MediaHistogram(const MediaHistogram &); // disallow
bool allocate(int bucketCount, bool withBucketLimits);
- int64_t mFloor, mCeiling, mWidth;
- int64_t mBelow, mAbove;
- int64_t mMin, mMax, mSum, mCount;
-
- int mBucketCount;
- int64_t *mBuckets;
- int64_t *mBucketLimits;
+ T mFloor, mCeiling, mWidth;
+ T mMin, mMax, mSum;
+ int64_t mBelow, mAbove, mCount;
+ std::vector<T> mBuckets;
+ std::vector<T> mBucketLimits;
};
+template<typename T>
+MediaHistogram<T>::MediaHistogram() {
+ mWidth = mCeiling = mFloor = -1;
+ clear();
+}
+
+template<typename T>
+void MediaHistogram<T>::clear() {
+ for (int i = 0; i < mBuckets.size(); ++i) {
+ mBuckets[i] = 0;
+ }
+ mMin = std::numeric_limits<T>::max();
+ mMax = std::numeric_limits<T>::min();
+ mSum = 0;
+ mCount = 0;
+ mBelow = mAbove = 0;
+}
+
+template<typename T>
+bool MediaHistogram<T>::setup(int bucketCount, T width, T floor) {
+ if (bucketCount <= 0 || width <= 0) {
+ return false;
+ }
+ if (!allocate(bucketCount, false)) {
+ return false;
+ }
+ mWidth = width;
+ mFloor = floor;
+ mCeiling = floor + bucketCount * width;
+ clear();
+ return true;
+}
+
+template<typename T>
+bool MediaHistogram<T>::setup(const std::vector<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];
+ clear();
+ return true;
+}
+
+template<typename T>
+bool MediaHistogram<T>::allocate(int bucketCount, bool withBucketLimits) {
+ assert(bucketCount > 0);
+ if (bucketCount != mBuckets.size()) {
+ mBuckets = std::vector<T>(bucketCount, 0);
+ }
+ if (withBucketLimits && mBucketLimits.size() != bucketCount) {
+ mBucketLimits = std::vector<T>(bucketCount, 0);
+ }
+ return true;
+}
+
+template<typename T>
+void MediaHistogram<T>::insert(T sample) {
+ // histogram is not set up
+ if (mBuckets.size() == 0) {
+ 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 (mWidth == -1) {
+ // 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 < mBucketLimits.size(); ++slot) {
+ if (sample < mBucketLimits[slot]) {
+ mBuckets[slot]++;
+ break;
+ }
+ }
+ } else {
+ int64_t slot = (sample - mFloor) / mWidth;
+ assert(slot < mBuckets.size());
+ mBuckets[slot]++;
+ }
+ return;
+}
+
+template<typename T>
+size_t MediaHistogram<T>::size() {
+ return mBuckets.size() + 1;
+}
+
+template<typename T>
+int64_t MediaHistogram<T>::operator[](int i) {
+ assert(i >= 0);
+ assert(i <= mBuckets.size());
+ if (i == mBuckets.size()) {
+ return mAbove;
+ }
+ return mBuckets[i];
+}
+
+template<typename T>
+std::string MediaHistogram<T>::emit() const {
+ // emits: floor,width,below{bucket0,bucket1,...., bucketN}above
+ // or.. emits: below{bucket0,bucket1,...., bucketN}above
+ // unconfigured will emit: 0{}0
+ // XXX: is this best representation?
+ std::stringstream ss("");
+ if (mWidth == -1) {
+ ss << mBelow << "{";
+ } else {
+ ss << mFloor << "," << mWidth << "," << mBelow << "{";
+ }
+ for (int i = 0; i < mBuckets.size(); i++) {
+ if (i != 0) {
+ ss << ",";
+ }
+ ss << mBuckets[i];
+ }
+ ss << "}" << mAbove;
+ return ss.str();
+}
+
+template<typename T>
+std::string MediaHistogram<T>::emitBuckets() const {
+ std::stringstream ss("");
+ if (mWidth == -1) {
+ ss << mFloor;
+ for (int i = 0; i < mBucketLimits.size(); ++i) {
+ ss << ',' << mBucketLimits[i];
+ }
+ ss << ',' << mCeiling;
+ } else {
+ ss << mFloor;
+ for (int i = 0; i < mBuckets.size(); ++i) {
+ ss << ',' << (mFloor + i * mWidth);
+ }
+ ss << ',' << mCeiling;
+ }
+ return ss.str();
+}
+
} // android
#endif // MEDIA_HISTOGRAM_H_
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index 25ccfdb..ec25a36 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -26,15 +26,17 @@
namespace android {
-static const float FRAME_RATE_UNDETERMINED = -1.0f;
-static const float FRAME_RATE_24HZ_3_2_PULLDOWN = -2.0f;
-
// A variety of video rendering quality metrics.
struct VideoRenderQualityMetrics {
+ static constexpr float FRAME_RATE_UNDETERMINED = -1.0f;
+ static constexpr float FRAME_RATE_24_3_2_PULLDOWN = -2.0f;
+
VideoRenderQualityMetrics();
+ void clear();
+
// The render time of the first video frame.
- int64_t firstFrameRenderTimeUs;
+ int64_t firstRenderTimeUs;
// The number of frames released to be rendered.
int64_t frameReleasedCount;
@@ -59,13 +61,21 @@
float actualFrameRate;
// A histogram of the durations of freezes due to dropped/skipped frames.
- MediaHistogram freezeDurationMsHistogram;
+ MediaHistogram<int32_t> freezeDurationMsHistogram;
+ // The computed overall freeze score using the above histogram and score conversion table.
+ int32_t freezeScore;
+ // The computed percentage of total playback duration that was frozen.
+ float freezeRate;
// A histogram of the durations between each freeze.
- MediaHistogram freezeDistanceMsHistogram;
+ MediaHistogram<int32_t> freezeDistanceMsHistogram;
// A histogram of the judder scores.
- MediaHistogram judderScoreHistogram;
+ MediaHistogram<int32_t> judderScoreHistogram;
+ // The computed overall judder score using the above histogram and score conversion table.
+ int32_t judderScore;
+ // The computed percentage of total frames that had judder.
+ float judderRate;
};
///////////////////////////////////////////////////////
@@ -93,6 +103,9 @@
public:
Configuration();
+ // Whether or not frame render quality is tracked.
+ bool enabled;
+
// Whether or not frames that are intentionally not rendered by the app should be considered
// as dropped.
bool areSkippedFramesDropped;
@@ -114,11 +127,24 @@
int32_t contentTimeAdvancedForLiveContentToleranceUs;
// Freeze configuration
- std::vector<int64_t> freezeDurationMsHistogramBuckets;
- std::vector<int64_t> freezeDistanceMsHistogramBuckets;
+ //
+ // The values used to distribute freeze durations across a histogram.
+ std::vector<int32_t> freezeDurationMsHistogramBuckets;
+ // The values used to compare against freeze duration counts when determining an overall
+ // score.
+ std::vector<int64_t> freezeDurationMsHistogramToScore;
+ // The values used to distribute distances between freezes across a histogram.
+ std::vector<int32_t> freezeDistanceMsHistogramBuckets;
+ // Judder configuration
+ //
+ // A judder error lower than this value is not scored as judder.
int32_t judderErrorToleranceUs;
- std::vector<int64_t> judderScoreHistogramBuckets;
+ // The values used to distribute judder scores across a histogram.
+ std::vector<int32_t> judderScoreHistogramBuckets;
+ // The values used to compare against judder score histogram counts when determining an
+ // overall score.
+ std::vector<int32_t> judderScoreHistogramToScore;
};
VideoRenderQualityTracker();
@@ -138,11 +164,14 @@
void onFrameRendered(int64_t contentTimeUs, int64_t actualRenderTimeNs);
// Retrieve the metrics.
- const VideoRenderQualityMetrics &getMetrics() const;
+ const VideoRenderQualityMetrics &getMetrics();
// Called when a change in codec state will result in a content discontinuity - e.g. flush.
void resetForDiscontinuity();
+ // Clear out all metrics and tracking - e.g. codec reconfigured.
+ void clear();
+
private:
// Tracking of frames that are pending to be rendered to the display.
struct FrameInfo {
@@ -237,6 +266,9 @@
// The most recent timestamp of the first frame rendered after the freeze.
int64_t mLastFreezeEndTimeUs;
+ // The render duration of the playback.
+ int64_t mRenderDurationMs;
+
// Frames skipped at the end of playback shouldn't really be considered skipped, therefore keep
// a list of the frames, and process them as skipped frames the next time a frame is rendered.
std::list<int64_t> mPendingSkippedFrameContentTimeUsList;
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index efe1990..9f14663 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -27,6 +27,10 @@
using Metrics = VideoRenderQualityMetrics;
using Configuration = VideoRenderQualityTracker::Configuration;
+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;
+
class Helper {
public:
Helper(double contentFrameDurationMs, const Configuration &configuration) :
@@ -76,7 +80,7 @@
}
}
- const Metrics & getMetrics() const {
+ const Metrics & getMetrics() {
return mVideoRenderQualityTracker.getMetrics();
}
@@ -194,7 +198,7 @@
Helper h(41.66, c);
h.render({49.9, 33.2, 50.0, 33.4, 50.1, 33.2});
EXPECT_NEAR(h.getMetrics().contentFrameRate, 24.0, 0.5);
- EXPECT_EQ(h.getMetrics().actualFrameRate, FRAME_RATE_24HZ_3_2_PULLDOWN);
+ EXPECT_EQ(h.getMetrics().actualFrameRate, FRAME_RATE_24_3_2_PULLDOWN);
}
TEST_F(VideoRenderQualityTrackerTest, whenBad32Pulldown_doesntDetect32Pulldown) {
@@ -216,7 +220,7 @@
h.changeContentFrameDuration(41.66);
h.render({50.0, 33.33, 50.0, 33.33, 50.0, 33.33});
EXPECT_NEAR(h.getMetrics().contentFrameRate, 24.0, 0.5);
- EXPECT_EQ(h.getMetrics().actualFrameRate, FRAME_RATE_24HZ_3_2_PULLDOWN);
+ EXPECT_EQ(h.getMetrics().actualFrameRate, FRAME_RATE_24_3_2_PULLDOWN);
}
TEST_F(VideoRenderQualityTrackerTest, whenFrameRateIsUnstable_doesntDetectFrameRate) {
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 158914a..a0b8f16 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -40,6 +40,159 @@
namespace android {
+using stats::media_metrics::stats_write;
+using stats::media_metrics::MEDIA_CODEC_RENDERED;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_UNKNOWN;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_INVALID;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_ZERO;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_UNKNOWN;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_UNDETERMINED;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_24_3_2_PULLDOWN;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_NONE;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HLG;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HDR10;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HDR10_PLUS;
+using stats::media_metrics::MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_DOLBY_VISION;
+
+static const int BITRATE_UNKNOWN =
+ stats::media_metrics::MEDIA_CODEC_RENDERED__BITRATE__BITRATE_UNKNOWN;
+
+static const std::pair<char const *, int> CODEC_LOOKUP[] = {
+ { "avc", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_AVC },
+ { "h264", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_AVC },
+ { "hevc", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_HEVC },
+ { "h265", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_HEVC },
+ { "vp8", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_VP8 },
+ { "vp9", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_VP9 },
+ { "av1", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_AV1 },
+ { "av01", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_AV1 },
+ { "dolby-vision", stats::media_metrics::MEDIA_CODEC_RENDERED__CODEC__CODEC_HEVC },
+};
+
+static const int32_t RESOLUTION_LOOKUP[] = {
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_MAX_SIZE,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_32K,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_16K,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_8K_UHD,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_8K_UHD_ALMOST,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_4K_UHD_ALMOST,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_1440X2560,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_1080X2400,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_1080X2340,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_1080P_FHD,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_1080P_FHD_ALMOST,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_720P_HD,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_720P_HD_ALMOST,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_576X1024,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_540X960,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_480X854,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_480X640,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_360X640,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_352X640,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_VERY_LOW,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_SMALLEST,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_ZERO,
+};
+
+static const int32_t FRAMERATE_LOOKUP[] = {
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_24,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_25,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_30,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_50,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_60,
+ stats::media_metrics::MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_120,
+};
+
+static int32_t getMetricsCodecEnum(const std::string &mime, const std::string &componentName) {
+ for (const auto & codecStrAndEnum : CODEC_LOOKUP) {
+ if (strcasestr(mime.c_str(), codecStrAndEnum.first) != nullptr ||
+ strcasestr(componentName.c_str(), codecStrAndEnum.first) != nullptr) {
+ return codecStrAndEnum.second;
+ }
+ }
+ return MEDIA_CODEC_RENDERED__CODEC__CODEC_UNKNOWN;
+}
+
+static int32_t getMetricsResolutionEnum(int32_t width, int32_t height) {
+ if (width == 0 || height == 0) {
+ return MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_ZERO;
+ }
+ int64_t pixels = int64_t(width) * height / 1000;
+ if (width < 0 || height < 0 || pixels > RESOLUTION_LOOKUP[0]) {
+ return MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_INVALID;
+ }
+ for (int32_t resolutionEnum : RESOLUTION_LOOKUP) {
+ if (pixels > resolutionEnum) {
+ return resolutionEnum;
+ }
+ }
+ return MEDIA_CODEC_RENDERED__RESOLUTION__RESOLUTION_ZERO;
+}
+
+static int32_t getMetricsFramerateEnum(float inFramerate) {
+ if (inFramerate == -1.0f) {
+ return MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_UNDETERMINED;
+ }
+ if (inFramerate == -2.0f) {
+ return MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_24_3_2_PULLDOWN;
+ }
+ int framerate = int(inFramerate * 100); // Table is in hundredths of frames per second
+ static const int framerateTolerance = 40; // Tolerance is 0.4 frames per second - table is 100s
+ for (int32_t framerateEnum : FRAMERATE_LOOKUP) {
+ if (abs(framerate - framerateEnum) < framerateTolerance) {
+ return framerateEnum;
+ }
+ }
+ return MEDIA_CODEC_RENDERED__CONTENT_FRAMERATE__FRAMERATE_UNKNOWN;
+}
+
+static int32_t getMetricsHdrFormatEnum(std::string &mime, std::string &componentName,
+ int32_t configColorTransfer, int32_t parsedColorTransfer,
+ int32_t hdr10StaticInfo, int32_t hdr10PlusInfo) {
+ if (hdr10PlusInfo) {
+ return MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HDR10_PLUS;
+ }
+ if (hdr10StaticInfo) {
+ return MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HDR10;
+ }
+ // 7 = COLOR_TRANSFER_HLG in MediaCodecConstants.h
+ if (configColorTransfer == 7 || parsedColorTransfer == 7) {
+ return MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_HLG;
+ }
+ if (strcasestr(mime.c_str(), "dolby-vision") != nullptr ||
+ strcasestr(componentName.c_str(), "dvhe") != nullptr ||
+ strcasestr(componentName.c_str(), "dvav") != nullptr ||
+ strcasestr(componentName.c_str(), "dav1") != nullptr) {
+ return MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_DOLBY_VISION;
+ }
+ return MEDIA_CODEC_RENDERED__HDR_FORMAT__HDR_FORMAT_NONE;
+}
+
+static void parseVector(const std::string &str, std::vector<int32_t> *vector) {
+ char valueStr[12] = {};
+ int i = 0;
+ for (char const * p = str.c_str(); *p != 0; ++p) {
+ if (*p == ',' || *p == '{' || *p == '}') {
+ valueStr[i] = 0;
+ int64_t value = strtol(valueStr, nullptr, 10);
+ if (value >= std::numeric_limits<int32_t>::max() ||
+ value <= std::numeric_limits<int32_t>::min() ||
+ value == 0) {
+ ALOGE("failed to parse integer vector at '%s' from '%s'", p, str.c_str());
+ return;
+ }
+ vector->push_back(int32_t(value));
+ i = valueStr[0] = 0;
+ } else {
+ valueStr[i++] = *p;
+ if (i == sizeof(valueStr) - 1) { // -1 because we need space for a null terminator
+ ALOGE("failed to parse integer vector at '%s' from '%s'", p, str.c_str());
+ return;
+ }
+ }
+ }
+}
+
bool statsd_codec(const std::shared_ptr<const mediametrics::Item>& item,
const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
@@ -48,17 +201,17 @@
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, stats::media_metrics::MEDIA_CODEC_REPORTED);
- const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
- AStatsEvent_writeInt64(event, timestamp_nanos);
+ const nsecs_t timestampNanos = MediaMetricsService::roundTime(item->getTimestamp());
+ AStatsEvent_writeInt64(event, timestampNanos);
- std::string package_name = item->getPkgName();
- AStatsEvent_writeString(event, package_name.c_str());
+ std::string packageName = item->getPkgName();
+ AStatsEvent_writeString(event, packageName.c_str());
- int64_t package_version_code = item->getPkgVersionCode();
- AStatsEvent_writeInt64(event, package_version_code);
+ int64_t packageVersionCode = item->getPkgVersionCode();
+ AStatsEvent_writeInt64(event, packageVersionCode);
- int64_t media_apex_version = 0;
- AStatsEvent_writeInt64(event, media_apex_version);
+ int64_t mediaApexVersion = 0;
+ AStatsEvent_writeInt64(event, mediaApexVersion);
// the rest into our own proto
//
@@ -84,17 +237,25 @@
}
AStatsEvent_writeString(event, mode.c_str());
- int32_t encoder = -1;
- if (item->getInt32("android.media.mediacodec.encoder", &encoder)) {
- metrics_proto.set_encoder(encoder);
+ int32_t isEncoder = -1;
+ if (item->getInt32("android.media.mediacodec.encoder", &isEncoder)) {
+ metrics_proto.set_encoder(isEncoder);
}
- AStatsEvent_writeInt32(event, encoder);
+ AStatsEvent_writeInt32(event, isEncoder);
- int32_t secure = -1;
- if (item->getInt32("android.media.mediacodec.secure", &secure)) {
- metrics_proto.set_secure(secure);
+ int32_t isSecure = -1;
+ if (item->getInt32("android.media.mediacodec.secure", &isSecure)) {
+ metrics_proto.set_secure(isSecure);
}
- AStatsEvent_writeInt32(event, secure);
+ AStatsEvent_writeInt32(event, isSecure);
+
+ int32_t isHardware = -1;
+ item->getInt32("android.media.mediacodec.hardware", &isHardware);
+ // not logged to MediaCodecReported or MediametricsCodecReported
+
+ int32_t isTunneled = -1;
+ item->getInt32("android.media.mediacodec.tunneled", &isTunneled);
+ // not logged to MediaCodecReported or MediametricsCodecReported
int32_t width = -1;
if (item->getInt32("android.media.mediacodec.width", &width)) {
@@ -133,79 +294,78 @@
AStatsEvent_writeInt32(event, level);
- int32_t max_width = -1;
- if ( item->getInt32("android.media.mediacodec.maxwidth", &max_width)) {
- metrics_proto.set_max_width(max_width);
+ int32_t maxWidth = -1;
+ if ( item->getInt32("android.media.mediacodec.maxwidth", &maxWidth)) {
+ metrics_proto.set_max_width(maxWidth);
}
- AStatsEvent_writeInt32(event, max_width);
+ AStatsEvent_writeInt32(event, maxWidth);
- int32_t max_height = -1;
- if ( item->getInt32("android.media.mediacodec.maxheight", &max_height)) {
- metrics_proto.set_max_height(max_height);
+ int32_t maxHeight = -1;
+ if ( item->getInt32("android.media.mediacodec.maxheight", &maxHeight)) {
+ metrics_proto.set_max_height(maxHeight);
}
- AStatsEvent_writeInt32(event, max_height);
+ AStatsEvent_writeInt32(event, maxHeight);
- int32_t error_code = -1;
- if ( item->getInt32("android.media.mediacodec.errcode", &error_code)) {
- metrics_proto.set_error_code(error_code);
+ int32_t errorCode = -1;
+ if ( item->getInt32("android.media.mediacodec.errcode", &errorCode)) {
+ metrics_proto.set_error_code(errorCode);
}
- AStatsEvent_writeInt32(event, error_code);
+ AStatsEvent_writeInt32(event, errorCode);
- std::string error_state;
- if ( item->getString("android.media.mediacodec.errstate", &error_state)) {
- metrics_proto.set_error_state(error_state);
+ std::string errorState;
+ if ( item->getString("android.media.mediacodec.errstate", &errorState)) {
+ metrics_proto.set_error_state(errorState);
}
- AStatsEvent_writeString(event, error_state.c_str());
+ AStatsEvent_writeString(event, errorState.c_str());
- int64_t latency_max = -1;
- if (item->getInt64("android.media.mediacodec.latency.max", &latency_max)) {
- metrics_proto.set_latency_max(latency_max);
+ int64_t latencyMax = -1;
+ if (item->getInt64("android.media.mediacodec.latency.max", &latencyMax)) {
+ metrics_proto.set_latency_max(latencyMax);
}
- AStatsEvent_writeInt64(event, latency_max);
+ AStatsEvent_writeInt64(event, latencyMax);
- int64_t latency_min = -1;
- if (item->getInt64("android.media.mediacodec.latency.min", &latency_min)) {
- metrics_proto.set_latency_min(latency_min);
+ int64_t latencyMin = -1;
+ if (item->getInt64("android.media.mediacodec.latency.min", &latencyMin)) {
+ metrics_proto.set_latency_min(latencyMin);
}
- AStatsEvent_writeInt64(event, latency_min);
+ AStatsEvent_writeInt64(event, latencyMin);
- int64_t latency_avg = -1;
- if (item->getInt64("android.media.mediacodec.latency.avg", &latency_avg)) {
- metrics_proto.set_latency_avg(latency_avg);
+ int64_t latencyAvg = -1;
+ if (item->getInt64("android.media.mediacodec.latency.avg", &latencyAvg)) {
+ metrics_proto.set_latency_avg(latencyAvg);
}
- AStatsEvent_writeInt64(event, latency_avg);
+ AStatsEvent_writeInt64(event, latencyAvg);
- int64_t latency_count = -1;
- if (item->getInt64("android.media.mediacodec.latency.n", &latency_count)) {
- metrics_proto.set_latency_count(latency_count);
+ int64_t latencyCount = -1;
+ if (item->getInt64("android.media.mediacodec.latency.n", &latencyCount)) {
+ metrics_proto.set_latency_count(latencyCount);
}
- AStatsEvent_writeInt64(event, latency_count);
+ AStatsEvent_writeInt64(event, latencyCount);
- int64_t latency_unknown = -1;
- if (item->getInt64("android.media.mediacodec.latency.unknown", &latency_unknown)) {
- metrics_proto.set_latency_unknown(latency_unknown);
+ int64_t latencyUnknown = -1;
+ if (item->getInt64("android.media.mediacodec.latency.unknown", &latencyUnknown)) {
+ metrics_proto.set_latency_unknown(latencyUnknown);
}
- AStatsEvent_writeInt64(event, latency_unknown);
+ AStatsEvent_writeInt64(event, latencyUnknown);
- int32_t queue_secure_input_buffer_error = -1;
+ int32_t queueSecureInputBufferError = -1;
if (item->getInt32("android.media.mediacodec.queueSecureInputBufferError",
- &queue_secure_input_buffer_error)) {
- metrics_proto.set_queue_secure_input_buffer_error(queue_secure_input_buffer_error);
+ &queueSecureInputBufferError)) {
+ metrics_proto.set_queue_secure_input_buffer_error(queueSecureInputBufferError);
}
- AStatsEvent_writeInt32(event, queue_secure_input_buffer_error);
+ AStatsEvent_writeInt32(event, queueSecureInputBufferError);
- int32_t queue_input_buffer_error = -1;
- if (item->getInt32("android.media.mediacodec.queueInputBufferError",
- &queue_input_buffer_error)) {
- metrics_proto.set_queue_input_buffer_error(queue_input_buffer_error);
+ int32_t queueInputBufferError = -1;
+ if (item->getInt32("android.media.mediacodec.queueInputBufferError", &queueInputBufferError)) {
+ metrics_proto.set_queue_input_buffer_error(queueInputBufferError);
}
- AStatsEvent_writeInt32(event, queue_input_buffer_error);
+ AStatsEvent_writeInt32(event, queueInputBufferError);
- std::string bitrate_mode;
- if (item->getString("android.media.mediacodec.bitrate_mode", &bitrate_mode)) {
- metrics_proto.set_bitrate_mode(bitrate_mode);
+ std::string bitrateMode;
+ if (item->getString("android.media.mediacodec.bitrate_mode", &bitrateMode)) {
+ metrics_proto.set_bitrate_mode(bitrateMode);
}
- AStatsEvent_writeString(event, bitrate_mode.c_str());
+ AStatsEvent_writeString(event, bitrateMode.c_str());
int32_t bitrate = -1;
if (item->getInt32("android.media.mediacodec.bitrate", &bitrate)) {
@@ -213,18 +373,18 @@
}
AStatsEvent_writeInt32(event, bitrate);
- int64_t lifetime_millis = -1;
- if (item->getInt64("android.media.mediacodec.lifetimeMs", &lifetime_millis)) {
- lifetime_millis = mediametrics::bucket_time_minutes(lifetime_millis);
- metrics_proto.set_lifetime_millis(lifetime_millis);
+ int64_t lifetimeMillis = -1;
+ if (item->getInt64("android.media.mediacodec.lifetimeMs", &lifetimeMillis)) {
+ lifetimeMillis = mediametrics::bucket_time_minutes(lifetimeMillis);
+ metrics_proto.set_lifetime_millis(lifetimeMillis);
}
- AStatsEvent_writeInt64(event, lifetime_millis);
+ AStatsEvent_writeInt64(event, lifetimeMillis);
- int64_t playback_duration_sec = -1;
- item->getInt64("android.media.mediacodec.playback-duration-sec", &playback_duration_sec);
+ int64_t playbackDurationSec = -1;
+ item->getInt64("android.media.mediacodec.playback-duration-sec", &playbackDurationSec);
// DO NOT record playback-duration in the metrics_proto - it should only
// exist in the flattened atom
- AStatsEvent_writeInt64(event, playback_duration_sec);
+ AStatsEvent_writeInt64(event, playbackDurationSec);
std::string sessionId;
if (item->getString("android.media.mediacodec.log-session-id", &sessionId)) {
@@ -505,61 +665,182 @@
}
AStatsEvent_writeInt32(event, resolutionChangeCount);
+ int64_t firstRenderTimeUs = -1;
+ item->getInt64("android.media.mediacodec.first-render-time-us", &firstRenderTimeUs);
+ int64_t framesReleased = -1;
+ item->getInt64("android.media.mediacodec.frames-released", &framesReleased);
+ int64_t framesRendered = -1;
+ item->getInt64("android.media.mediacodec.frames-rendered", &framesRendered);
+ int64_t framesDropped = -1;
+ item->getInt64("android.media.mediacodec.frames-dropped", &framesDropped);
+ int64_t framesSkipped = -1;
+ item->getInt64("android.media.mediacodec.frames-skipped", &framesSkipped);
+ double framerateContent = -1;
+ item->getDouble("android.media.mediacodec.framerate-content", &framerateContent);
+ double framerateActual = -1;
+ item->getDouble("android.media.mediacodec.framerate-actual", &framerateActual);
+ int64_t freezeScore = -1;
+ item->getInt64("android.media.mediacodec.freeze-score", &freezeScore);
+ double freezeRate = -1;
+ item->getDouble("android.media.mediacodec.freeze-rate", &freezeRate);
+ std::string freezeScoreHistogramStr;
+ item->getString("android.media.mediacodec.freeze-score-histogram", &freezeScoreHistogramStr);
+ std::string freezeScoreHistogramBucketsStr;
+ item->getString("android.media.mediacodec.freeze-score-histogram-buckets",
+ &freezeScoreHistogramBucketsStr);
+ std::string freezeDurationMsHistogramStr;
+ item->getString("android.media.mediacodec.freeze-duration-ms-histogram",
+ &freezeDurationMsHistogramStr);
+ std::string freezeDurationMsHistogramBucketsStr;
+ item->getString("android.media.mediacodec.freeze-duration-ms-histogram-buckets",
+ &freezeDurationMsHistogramBucketsStr);
+ std::string freezeDistanceMsHistogramStr;
+ item->getString("android.media.mediacodec.freeze-distance-ms-histogram",
+ &freezeDistanceMsHistogramStr);
+ std::string freezeDistanceMsHistogramBucketsStr;
+ item->getString("android.media.mediacodec.freeze-distance-ms-histogram-buckets",
+ &freezeDistanceMsHistogramBucketsStr);
+ int64_t judderScore = -1;
+ item->getInt64("android.media.mediacodec.judder-score", &judderScore);
+ double judderRate = -1;
+ item->getDouble("android.media.mediacodec.judder-rate", &judderRate);
+ std::string judderScoreHistogramStr;
+ item->getString("android.media.mediacodec.judder-score-histogram", &judderScoreHistogramStr);
+ std::string judderScoreHistogramBucketsStr;
+ item->getString("android.media.mediacodec.judder-score-histogram-buckets",
+ &judderScoreHistogramBucketsStr);
+
int err = AStatsEvent_write(event);
if (err < 0) {
ALOGE("Failed to write codec metrics to statsd (%d)", err);
}
AStatsEvent_release(event);
+ if (framesRendered > 0) {
+ int32_t statsUid = item->getUid();
+ int64_t statsCodecId = codecId;
+ char const *statsLogSessionId = sessionId.c_str();
+ int32_t statsIsHardware = isHardware;
+ int32_t statsIsSecure = isSecure;
+ int32_t statsIsTunneled = isTunneled;
+ int32_t statsCodec = getMetricsCodecEnum(mime, codec);
+ int32_t statsResolution = getMetricsResolutionEnum(width, height);
+ int32_t statsBitrate = BITRATE_UNKNOWN;
+ int32_t statsContentFramerate = getMetricsFramerateEnum(framerateContent);
+ int32_t statsActualFramerate = getMetricsFramerateEnum(framerateActual);
+ int32_t statsHdrFormat = getMetricsHdrFormatEnum(mime, codec, configColorTransfer,
+ parsedColorTransfer, hdrStaticInfo,
+ hdr10PlusInfo);
+ int64_t statsFirstRenderTimeUs = firstRenderTimeUs;
+ int64_t statsPlaybackDurationSeconds = playbackDurationSec;
+ int64_t statsFramesTotal = framesReleased + framesSkipped;
+ int64_t statsFramesReleased = framesReleased;
+ int64_t statsFramesRendered = framesRendered;
+ int64_t statsFramesDropped = framesDropped;
+ int64_t statsFramesSkipped = framesSkipped;
+ float statsFrameDropRate = float(double(framesDropped) / statsFramesTotal);
+ float statsFrameSkipRate = float(double(framesSkipped) / statsFramesTotal);
+ float statsFrameSkipDropRate = float(double(framesSkipped + framesDropped) /
+ statsFramesTotal);
+ int64_t statsFreezeScore = freezeScore;
+ float statsFreezeRate = freezeRate;
+ std::vector<int32_t> statsFreezeDurationMsHistogram;
+ parseVector(freezeDurationMsHistogramStr, &statsFreezeDurationMsHistogram);
+ std::vector<int32_t> statsFreezeDurationMsHistogramBuckets;
+ parseVector(freezeDurationMsHistogramBucketsStr, &statsFreezeDurationMsHistogramBuckets);
+ std::vector<int32_t> statsFreezeDistanceMsHistogram;
+ parseVector(freezeDistanceMsHistogramStr, &statsFreezeDistanceMsHistogram);
+ std::vector<int32_t> statsFreezeDistanceMsHistogramBuckets;
+ parseVector(freezeDistanceMsHistogramBucketsStr, &statsFreezeDistanceMsHistogramBuckets);
+ int64_t statsJudderScore = judderScore;
+ float statsJudderRate = judderRate;
+ std::vector<int32_t> statsJudderScoreHistogram;
+ parseVector(judderScoreHistogramStr, &statsJudderScoreHistogram);
+ std::vector<int32_t> statsJudderScoreHistogramBuckets;
+ parseVector(judderScoreHistogramBucketsStr, &statsJudderScoreHistogramBuckets);
+ int result = stats_write(
+ MEDIA_CODEC_RENDERED,
+ statsUid,
+ statsCodecId,
+ statsLogSessionId,
+ statsIsHardware,
+ statsIsSecure,
+ statsIsTunneled,
+ statsCodec,
+ statsResolution,
+ statsBitrate,
+ statsContentFramerate,
+ statsActualFramerate,
+ statsHdrFormat,
+ statsFirstRenderTimeUs,
+ statsPlaybackDurationSeconds,
+ statsFramesTotal,
+ statsFramesReleased,
+ statsFramesRendered,
+ statsFramesDropped,
+ statsFramesSkipped,
+ statsFrameDropRate,
+ statsFrameSkipRate,
+ statsFrameSkipDropRate,
+ statsFreezeScore,
+ statsFreezeRate,
+ statsFreezeDurationMsHistogram,
+ statsFreezeDurationMsHistogramBuckets,
+ statsFreezeDistanceMsHistogram,
+ statsFreezeDistanceMsHistogramBuckets,
+ statsJudderScore,
+ statsJudderRate,
+ statsJudderScoreHistogram,
+ statsJudderScoreHistogramBuckets);
+ ALOGE_IF(result < 0, "Failed to record MEDIA_CODEC_RENDERED atom (%d)", result);
+ }
+
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
ALOGE("Failed to serialize codec metrics");
return false;
}
- const stats::media_metrics::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ const stats::media_metrics::BytesField bf_serialized(serialized.c_str(), serialized.size());
const int result = stats::media_metrics::stats_write(stats::media_metrics::MEDIAMETRICS_CODEC_REPORTED,
- timestamp_nanos, package_name.c_str(), package_version_code,
- media_apex_version,
+ timestampNanos, packageName.c_str(), packageVersionCode,
+ mediaApexVersion,
bf_serialized);
std::stringstream log;
log << "result:" << result << " {"
<< " mediametrics_codec_reported:"
<< stats::media_metrics::MEDIAMETRICS_CODEC_REPORTED
- << " timestamp_nanos:" << timestamp_nanos
- << " package_name:" << package_name
- << " package_version_code:" << package_version_code
- << " media_apex_version:" << media_apex_version
-
+ << " timestamp_nanos:" << timestampNanos
+ << " package_name:" << packageName
+ << " package_version_code:" << packageVersionCode
+ << " media_apex_version:" << mediaApexVersion
<< " codec:" << codec
<< " mime:" << mime
<< " mode:" << mode
- << " encoder:" << encoder
- << " secure:" << secure
+ << " encoder:" << isEncoder
+ << " secure:" << isSecure
<< " width:" << width
<< " height:" << height
<< " rotation:" << rotation
<< " crypto:" << crypto
<< " profile:" << profile
-
<< " level:" << level
- << " max_width:" << max_width
- << " max_height:" << max_height
- << " error_code:" << error_code
- << " error_state:" << error_state
- << " latency_max:" << latency_max
- << " latency_min:" << latency_min
- << " latency_avg:" << latency_avg
- << " latency_count:" << latency_count
- << " latency_unknown:" << latency_unknown
-
- << " queue_input_buffer_error:" << queue_input_buffer_error
- << " queue_secure_input_buffer_error:" << queue_secure_input_buffer_error
- << " bitrate_mode:" << bitrate_mode
+ << " max_width:" << maxWidth
+ << " max_height:" << maxHeight
+ << " error_code:" << errorCode
+ << " error_state:" << errorState
+ << " latency_max:" << latencyMax
+ << " latency_min:" << latencyMin
+ << " latency_avg:" << latencyAvg
+ << " latency_count:" << latencyCount
+ << " latency_unknown:" << latencyUnknown
+ << " queue_input_buffer_error:" << queueInputBufferError
+ << " queue_secure_input_buffer_error:" << queueSecureInputBufferError
+ << " bitrate_mode:" << bitrateMode
<< " bitrate:" << bitrate
<< " original_bitrate:" << originalBitrate
- << " lifetime_millis:" << lifetime_millis
- << " playback_duration_seconds:" << playback_duration_sec
+ << " lifetime_millis:" << lifetimeMillis
+ << " playback_duration_seconds:" << playbackDurationSec
<< " log_session_id:" << sessionId
<< " channel_count:" << channelCount
<< " sample_rate:" << sampleRate
@@ -572,7 +853,6 @@
<< " operating_rate:" << operatingRate
<< " priority:" << priority
<< " shaping_enhanced:" << shapingEnhanced
-
<< " qp_i_min:" << qpIMin
<< " qp_i_max:" << qpIMax
<< " qp_p_min:" << qpPMin