Merge "Add atom reporting for MediaCodecRendered" into udc-dev
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