Loading media/libstagefright/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -255,7 +255,6 @@ cc_library { "MediaCodecSource.cpp", "MediaExtractor.cpp", "MediaExtractorFactory.cpp", "MediaHistogram.cpp", "MediaSource.cpp", "MediaSync.cpp", "MediaTrack.cpp", Loading media/libstagefright/MediaCodec.cpp +73 −49 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ #include <set> #include <random> #include <stdlib.h> #include <inttypes.h> #include <stdlib.h> #include <dlfcn.h> Loading Loading @@ -108,7 +107,9 @@ static const char *kCodecModeAudio = "audio"; static const char *kCodecModeImage = "image"; static const char *kCodecModeUnknown = "unknown"; static const char *kCodecEncoder = "android.media.mediacodec.encoder"; /* 0,1 */ static const char *kCodecHardware = "android.media.mediacodec.hardware"; /* 0,1 */ static const char *kCodecSecure = "android.media.mediacodec.secure"; /* 0, 1 */ static const char *kCodecTunneled = "android.media.mediacodec.tunneled"; /* 0,1 */ static const char *kCodecWidth = "android.media.mediacodec.width"; /* 0..n */ static const char *kCodecHeight = "android.media.mediacodec.height"; /* 0..n */ static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees"; /* 0/90/180/270 */ Loading Loading @@ -172,9 +173,9 @@ static const char *kCodecConfigColorTransfer = "android.media.mediacodec.config- static const char *kCodecParsedColorStandard = "android.media.mediacodec.parsed-color-standard"; static const char *kCodecParsedColorRange = "android.media.mediacodec.parsed-color-range"; static const char *kCodecParsedColorTransfer = "android.media.mediacodec.parsed-color-transfer"; static const char *kCodecHDRStaticInfo = "android.media.mediacodec.hdr-static-info"; static const char *kCodecHDR10PlusInfo = "android.media.mediacodec.hdr10-plus-info"; static const char *kCodecHDRFormat = "android.media.mediacodec.hdr-format"; static const char *kCodecHdrStaticInfo = "android.media.mediacodec.hdr-static-info"; static const char *kCodecHdr10PlusInfo = "android.media.mediacodec.hdr10-plus-info"; static const char *kCodecHdrFormat = "android.media.mediacodec.hdr-format"; // array/sync/async/block modes static const char *kCodecArrayMode = "android.media.mediacodec.array-mode"; static const char *kCodecOperationMode = "android.media.mediacodec.operation-mode"; Loading @@ -195,35 +196,44 @@ static const char *kCodecRecentLatencyMin = "android.media.mediacodec.recent.min static const char *kCodecRecentLatencyAvg = "android.media.mediacodec.recent.avg"; /* in us */ static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n"; static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist"; /* in us */ static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec"; /* in sec */ static const char *kCodecFramesReleased = "android.media.mediacodec.frames.released"; static const char *kCodecFramesRendered = "android.media.mediacodec.frames.rendered"; static const char *kCodecFramesSkipped = "android.media.mediacodec.frames.skipped"; static const char *kCodecFramesDropped = "android.media.mediacodec.frames.dropped"; static const char *kCodecFramerateContent = "android.media.mediacodec.framerate.content"; static const char *kCodecFramerateDesired = "android.media.mediacodec.framerate.desired"; static const char *kCodecFramerateActual = "android.media.mediacodec.framerate.actual"; static const char *kCodecFreezeCount = "android.media.mediacodec.freeze.count"; static const char *kCodecFreezeDurationAverage = "android.media.mediacodec.freeze.duration.average"; static const char *kCodecFreezeDurationMax = "android.media.mediacodec.freeze.duration.max"; static const char *kCodecFreezeDurationHistogram = "android.media.mediacodec.freeze.duration.histogram"; static const char *kCodecFreezeDistanceAverage = "android.media.mediacodec.freeze.distance.average"; static const char *kCodecFreezeDistanceHistogram = "android.media.mediacodec.freeze.distance.histogram"; static const char *kCodecJudderCount = "android.media.mediacodec.judder.count"; static const char *kCodecJudderScoreAverage = "android.media.mediacodec.judder.average"; static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder.max"; static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder.histogram"; /* -1: shaper disabled >=0: number of fields changed */ static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped"; // Render metrics static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec"; static const char *kCodecFirstRenderTimeUs = "android.media.mediacodec.first-render-time-us"; static const char *kCodecFramesReleased = "android.media.mediacodec.frames-released"; static const char *kCodecFramesRendered = "android.media.mediacodec.frames-rendered"; static const char *kCodecFramesDropped = "android.media.mediacodec.frames-dropped"; static const char *kCodecFramesSkipped = "android.media.mediacodec.frames-skipped"; static const char *kCodecFramerateContent = "android.media.mediacodec.framerate-content"; static const char *kCodecFramerateDesired = "android.media.mediacodec.framerate-desired"; static const char *kCodecFramerateActual = "android.media.mediacodec.framerate-actual"; static const char *kCodecFreezeCount = "android.media.mediacodec.freeze-count"; static const char *kCodecFreezeScore = "android.media.mediacodec.freeze-score"; static const char *kCodecFreezeRate = "android.media.mediacodec.freeze-rate"; static const char *kCodecFreezeDurationMsAvg = "android.media.mediacodec.freeze-duration-ms-avg"; static const char *kCodecFreezeDurationMsMax = "android.media.mediacodec.freeze-duration-ms-max"; static const char *kCodecFreezeDurationMsHistogram = "android.media.mediacodec.freeze-duration-ms-histogram"; static const char *kCodecFreezeDurationMsHistogramBuckets = "android.media.mediacodec.freeze-duration-ms-histogram-buckets"; static const char *kCodecFreezeDistanceMsAvg = "android.media.mediacodec.freeze-distance-ms-avg"; static const char *kCodecFreezeDistanceMsHistogram = "android.media.mediacodec.freeze-distance-ms-histogram"; static const char *kCodecFreezeDistanceMsHistogramBuckets = "android.media.mediacodec.freeze-distance-ms-histogram-buckets"; static const char *kCodecJudderCount = "android.media.mediacodec.judder-count"; static const char *kCodecJudderScore = "android.media.mediacodec.judder-score"; static const char *kCodecJudderRate = "android.media.mediacodec.judder-rate"; static const char *kCodecJudderScoreAvg = "android.media.mediacodec.judder-score-avg"; static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder-score-max"; static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder-score-histogram"; static const char *kCodecJudderScoreHistogramBuckets = "android.media.mediacodec.judder-score-histogram-buckets"; // XXX suppress until we get our representation right static bool kEmitHistogram = false; Loading Loading @@ -1120,6 +1130,7 @@ void MediaCodec::updateMediametrics() { { const VideoRenderQualityMetrics &m = mVideoRenderQualityTracker.getMetrics(); if (m.frameRenderedCount > 0) { mediametrics_setInt64(mMetricsHandle, kCodecFirstRenderTimeUs, m.firstRenderTimeUs); mediametrics_setInt64(mMetricsHandle, kCodecFramesReleased, m.frameReleasedCount); mediametrics_setInt64(mMetricsHandle, kCodecFramesRendered, m.frameRenderedCount); mediametrics_setInt64(mMetricsHandle, kCodecFramesSkipped, m.frameSkippedCount); Loading @@ -1129,23 +1140,33 @@ void MediaCodec::updateMediametrics() { mediametrics_setDouble(mMetricsHandle, kCodecFramerateActual, m.actualFrameRate); } if (m.freezeDurationMsHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.freezeDurationMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, histogram.getCount()); mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationAverage, histogram.getAvg()); mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationMax, histogram.getMax()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.freezeDurationMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeScore, m.freezeScore); mediametrics_setDouble(mMetricsHandle, kCodecFreezeRate, m.freezeRate); mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, h.getCount()); mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsAvg, h.getAvg()); mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsMax, h.getMax()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogramBuckets, h.emitBuckets()); } if (m.freezeDistanceMsHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.freezeDistanceMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeDistanceAverage, histogram.getAvg()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.freezeDistanceMsHistogram; mediametrics_setInt32(mMetricsHandle, kCodecFreezeDistanceMsAvg, h.getAvg()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogramBuckets, h.emitBuckets()); } if (m.judderScoreHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.judderScoreHistogram; mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, histogram.getCount()); mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreAverage, histogram.getAvg()); mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreMax, histogram.getMax()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.judderScoreHistogram; mediametrics_setInt64(mMetricsHandle, kCodecJudderScore, m.judderScore); mediametrics_setDouble(mMetricsHandle, kCodecJudderRate, m.judderRate); mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, h.getCount()); mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreAvg, h.getAvg()); mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreMax, h.getMax()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogramBuckets, h.emitBuckets()); } } Loading Loading @@ -1227,14 +1248,14 @@ void MediaCodec::updateHdrMetrics(bool isConfig) { && ColorUtils::isHDRStaticInfoValid(&info)) { mHdrInfoFlags |= kFlagHasHdrStaticInfo; } mediametrics_setInt32(mMetricsHandle, kCodecHDRStaticInfo, mediametrics_setInt32(mMetricsHandle, kCodecHdrStaticInfo, (mHdrInfoFlags & kFlagHasHdrStaticInfo) ? 1 : 0); sp<ABuffer> hdr10PlusInfo; if (mOutputFormat->findBuffer("hdr10-plus-info", &hdr10PlusInfo) && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0) { mHdrInfoFlags |= kFlagHasHdr10PlusInfo; } mediametrics_setInt32(mMetricsHandle, kCodecHDR10PlusInfo, mediametrics_setInt32(mMetricsHandle, kCodecHdr10PlusInfo, (mHdrInfoFlags & kFlagHasHdr10PlusInfo) ? 1 : 0); // hdr format Loading @@ -1247,7 +1268,7 @@ void MediaCodec::updateHdrMetrics(bool isConfig) { && codedFormat->findInt32(KEY_PROFILE, &profile) && colorTransfer != -1) { hdr_format hdrFormat = getHdrFormat(mime, profile, colorTransfer); mediametrics_setInt32(mMetricsHandle, kCodecHDRFormat, static_cast<int>(hdrFormat)); mediametrics_setInt32(mMetricsHandle, kCodecHdrFormat, static_cast<int>(hdrFormat)); } } Loading Loading @@ -1355,9 +1376,8 @@ void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) { return; } MediaHistogram recentHist; // build an empty histogram MediaHistogram<int64_t> recentHist; recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor); // stuff it with the samples in the ring buffer Loading Loading @@ -3593,8 +3613,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { setState(UNINITIALIZED); } else { setState( (mFlags & kFlagIsAsync) ? FLUSHED : STARTED); setState((mFlags & kFlagIsAsync) ? FLUSHED : STARTED); } break; } Loading Loading @@ -3719,6 +3738,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0); } mediametrics_setInt32(mMetricsHandle, kCodecHardware, MediaCodecList::isSoftwareCodec(mComponentName) ? 0 : 1); mResourceManagerProxy->addResource(MediaResource::CodecResource( mFlags & kFlagIsSecure, toMediaResourceSubType(mDomain))); Loading Loading @@ -4133,6 +4155,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { if (mIsSurfaceToDisplay) { mVideoRenderQualityTracker.resetForDiscontinuity(); } // Notify the RM that the codec has been stopped. ClientConfigParcel clientConfig; initClientConfigParcel(clientConfig); Loading Loading @@ -4443,6 +4466,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } else { mTunneled = false; } mediametrics_setInt32(mMetricsHandle, kCodecTunneled, mTunneled ? 1 : 0); int32_t background = 0; if (format->findInt32("android._background-mode", &background) && background) { Loading media/libstagefright/MediaHistogram.cppdeleted 100644 → 0 +0 −173 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "MediaHistogram" #include <utils/Log.h> #include <media/stagefright/MediaHistogram.h> #include <assert.h> #include <inttypes.h> #include <sstream> #include <stdio.h> namespace android { MediaHistogram::MediaHistogram() { mBuckets = nullptr; mBucketLimits = nullptr; mBucketCount = 0; mFloor = mCeiling = mWidth = 0; mBelow = 0; mAbove = 0; mSum = 0; mCount = 0; mMin = INT64_MAX; mMax = INT64_MIN; } void MediaHistogram::clear() { if (mBuckets != nullptr) { free(mBuckets); mBuckets = nullptr; } if (mBucketLimits != nullptr) { free(mBucketLimits); mBucketLimits = nullptr; } mBucketCount = 0; } bool MediaHistogram::setup(int bucketCount, int64_t width, int64_t floor) { if (bucketCount <= 0 || width <= 0) { return false; } if (!allocate(bucketCount, false)) { return false; } mWidth = width; mFloor = floor; mCeiling = floor + bucketCount * width; mMin = INT64_MAX; mMax = INT64_MIN; mSum = 0; mCount = 0; mBelow = mAbove = 0; return true; } bool MediaHistogram::setup(const std::vector<int64_t> &bucketLimits) { if (bucketLimits.size() <= 1) { return false; } int bucketCount = bucketLimits.size() - 1; if (!allocate(bucketCount, true)) { return false; } mWidth = -1; mFloor = bucketLimits[0]; for (int i = 0; i < bucketCount; ++i) { mBucketLimits[i] = bucketLimits[i + 1]; } mCeiling = bucketLimits[bucketCount]; mMin = INT64_MAX; mMax = INT64_MIN; mSum = 0; mCount = 0; mBelow = mAbove = 0; return true; } bool MediaHistogram::allocate(int bucketCount, bool withBucketLimits) { assert(bucketCount > 0); if (bucketCount != mBucketCount) { clear(); mBuckets = (int64_t *) calloc(bucketCount, sizeof(*mBuckets)); if (mBuckets == nullptr) { return false; } } if (withBucketLimits && mBucketLimits == nullptr) { mBucketLimits = (int64_t *) calloc(bucketCount, sizeof(*mBucketLimits)); if (mBucketLimits == nullptr) { clear(); return false; } } mBucketCount = bucketCount; memset(mBuckets, 0, sizeof(*mBuckets) * mBucketCount); return true; } void MediaHistogram::insert(int64_t sample) { // histogram is not set up if (mBuckets == nullptr) { return; } mCount++; mSum += sample; if (mMin > sample) mMin = sample; if (mMax < sample) mMax = sample; if (sample < mFloor) { mBelow++; } else if (sample >= mCeiling) { mAbove++; } else if (mBucketLimits == nullptr) { int64_t slot = (sample - mFloor) / mWidth; assert(slot < mBucketCount); mBuckets[slot]++; } else { // A binary search might be more efficient for large number of buckets, but it is expected // that there will never be a large amount of buckets, so keep the code simple. for (int slot = 0; slot < mBucketCount; ++slot) { if (sample < mBucketLimits[slot]) { mBuckets[slot]++; break; } } } return; } std::string MediaHistogram::emit() const { // emits: floor,width,below{bucket0,bucket1,...., bucketN}above // or.. emits: below{bucket0,bucket1,...., bucketN}above // unconfigured will emit: 0,0,0{}0 // XXX: is this best representation? std::stringstream ss; if (mBucketLimits == nullptr) { ss << mFloor << "," << mWidth << "," << mBelow << "{"; } else { ss << mBelow << "{"; } for (int i = 0; i < mBucketCount; i++) { if (i != 0) { ss << ","; } ss << mBuckets[i]; } ss << "}" << mAbove; return ss.str(); } } // android media/libstagefright/VideoRenderQualityTracker.cpp +75 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -34,9 +42,14 @@ VideoRenderQualityMetrics::VideoRenderQualityMetrics() { contentFrameRate = FRAME_RATE_UNDETERMINED; desiredFrameRate = FRAME_RATE_UNDETERMINED; actualFrameRate = FRAME_RATE_UNDETERMINED; freezeDurationMsHistogram.clear(); freezeDistanceMsHistogram.clear(); judderScoreHistogram.clear(); } VideoRenderQualityTracker::Configuration::Configuration() { enabled = true; // Assume that the app is skipping frames because it's detected that the frame couldn't be // rendered in time. areSkippedFramesDropped = true; Loading @@ -53,26 +66,32 @@ VideoRenderQualityTracker::Configuration::Configuration() { // Freeze configuration freezeDurationMsHistogramBuckets = {1, 20, 40, 60, 80, 100, 120, 150, 175, 225, 300, 400, 500}; freezeDurationMsHistogramToScore = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; freezeDistanceMsHistogramBuckets = {0, 20, 100, 400, 1000, 2000, 3000, 4000, 8000, 15000, 30000, 60000}; // Judder configuration judderErrorToleranceUs = 2000; judderScoreHistogramBuckets = {1, 4, 5, 9, 11, 20, 30, 40, 50, 60, 70, 80}; judderScoreHistogramToScore = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; } VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) { configureHistograms(mMetrics, mConfiguration); resetForDiscontinuity(); clear(); } VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) : mConfiguration(configuration) { configureHistograms(mMetrics, mConfiguration); resetForDiscontinuity(); clear(); } void VideoRenderQualityTracker::onFrameSkipped(int64_t contentTimeUs) { if (!mConfiguration.enabled) { return; } // Frames skipped at the beginning shouldn't really be counted as skipped frames, since the // app might be seeking to a starting point that isn't the first key frame. if (mLastRenderTimeUs == -1) { Loading @@ -90,6 +109,10 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs) { void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs, int64_t desiredRenderTimeNs) { if (!mConfiguration.enabled) { return; } int64_t desiredRenderTimeUs = desiredRenderTimeNs / 1000; resetIfDiscontinuity(contentTimeUs, desiredRenderTimeUs); mMetrics.frameReleasedCount++; Loading @@ -98,8 +121,15 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs, } void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t actualRenderTimeNs) { if (!mConfiguration.enabled) { return; } int64_t actualRenderTimeUs = actualRenderTimeNs / 1000; if (mLastRenderTimeUs != -1) { mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000; } // Now that a frame has been rendered, the previously skipped frames can be processed as skipped // frames since the app is not skipping them to terminate playback. for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) { Loading Loading @@ -131,10 +161,47 @@ void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t a mLastRenderTimeUs = actualRenderTimeUs; } const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() const { const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() { if (!mConfiguration.enabled) { return mMetrics; } mMetrics.freezeScore = 0; if (mConfiguration.freezeDurationMsHistogramToScore.size() == mMetrics.freezeDurationMsHistogram.size()) { for (int i = 0; i < mMetrics.freezeDurationMsHistogram.size(); ++i) { int32_t count = 0; for (int j = i; j < mMetrics.freezeDurationMsHistogram.size(); ++j) { count += mMetrics.freezeDurationMsHistogram[j]; } mMetrics.freezeScore += count / mConfiguration.freezeDurationMsHistogramToScore[i]; } } mMetrics.freezeRate = float(double(mMetrics.freezeDurationMsHistogram.getSum()) / mRenderDurationMs); mMetrics.judderScore = 0; if (mConfiguration.judderScoreHistogramToScore.size() == mMetrics.judderScoreHistogram.size()) { for (int i = 0; i < mMetrics.judderScoreHistogram.size(); ++i) { int32_t count = 0; for (int j = i; j < mMetrics.judderScoreHistogram.size(); ++j) { count += mMetrics.judderScoreHistogram[j]; } mMetrics.judderScore += count / mConfiguration.judderScoreHistogramToScore[i]; } } mMetrics.judderRate = float(double(mMetrics.judderScoreHistogram.getCount()) / (mMetrics.frameReleasedCount + mMetrics.frameSkippedCount)); return mMetrics; } void VideoRenderQualityTracker::clear() { mRenderDurationMs = 0; mMetrics.clear(); resetForDiscontinuity(); } void VideoRenderQualityTracker::resetForDiscontinuity() { mLastContentTimeUs = -1; mLastRenderTimeUs = -1; Loading Loading @@ -220,11 +287,12 @@ void VideoRenderQualityTracker::processMetricsForRenderedFrame(int64_t contentTi int64_t desiredRenderTimeUs, int64_t actualRenderTimeUs) { // Capture the timestamp at which the first frame was rendered if (mMetrics.firstFrameRenderTimeUs == 0) { mMetrics.firstFrameRenderTimeUs = actualRenderTimeUs; if (mMetrics.firstRenderTimeUs == 0) { mMetrics.firstRenderTimeUs = actualRenderTimeUs; } mMetrics.frameRenderedCount++; // The content time is -1 when it was rendered after a discontinuity (e.g. seek) was detected. // So, even though a frame was rendered, it's impact on the user is insignificant, so don't do // anything other than count it as a rendered frame. Loading Loading @@ -352,7 +420,7 @@ float VideoRenderQualityTracker::detectFrameRate(const FrameDurationUs &duration // Only determine frame rate if the render durations are stable across 3 frames if (abs(durationUs[0] - durationUs[1]) > c.frameRateDetectionToleranceUs || abs(durationUs[0] - durationUs[2]) > c.frameRateDetectionToleranceUs) { return is32pulldown(durationUs, c) ? FRAME_RATE_24HZ_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED; return is32pulldown(durationUs, c) ? FRAME_RATE_24_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED; } return 1000.0 * 1000.0 / durationUs[0]; } Loading media/libstagefright/include/media/stagefright/MediaCodec.h +1 −1 Original line number Diff line number Diff line Loading @@ -715,7 +715,7 @@ private: int mRecentHead; Mutex mRecentLock; MediaHistogram mLatencyHist; MediaHistogram<int64_t> mLatencyHist; // An unique ID for the codec - Used by the metrics. uint64_t mCodecId = 0; Loading Loading
media/libstagefright/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -255,7 +255,6 @@ cc_library { "MediaCodecSource.cpp", "MediaExtractor.cpp", "MediaExtractorFactory.cpp", "MediaHistogram.cpp", "MediaSource.cpp", "MediaSync.cpp", "MediaTrack.cpp", Loading
media/libstagefright/MediaCodec.cpp +73 −49 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ #include <set> #include <random> #include <stdlib.h> #include <inttypes.h> #include <stdlib.h> #include <dlfcn.h> Loading Loading @@ -108,7 +107,9 @@ static const char *kCodecModeAudio = "audio"; static const char *kCodecModeImage = "image"; static const char *kCodecModeUnknown = "unknown"; static const char *kCodecEncoder = "android.media.mediacodec.encoder"; /* 0,1 */ static const char *kCodecHardware = "android.media.mediacodec.hardware"; /* 0,1 */ static const char *kCodecSecure = "android.media.mediacodec.secure"; /* 0, 1 */ static const char *kCodecTunneled = "android.media.mediacodec.tunneled"; /* 0,1 */ static const char *kCodecWidth = "android.media.mediacodec.width"; /* 0..n */ static const char *kCodecHeight = "android.media.mediacodec.height"; /* 0..n */ static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees"; /* 0/90/180/270 */ Loading Loading @@ -172,9 +173,9 @@ static const char *kCodecConfigColorTransfer = "android.media.mediacodec.config- static const char *kCodecParsedColorStandard = "android.media.mediacodec.parsed-color-standard"; static const char *kCodecParsedColorRange = "android.media.mediacodec.parsed-color-range"; static const char *kCodecParsedColorTransfer = "android.media.mediacodec.parsed-color-transfer"; static const char *kCodecHDRStaticInfo = "android.media.mediacodec.hdr-static-info"; static const char *kCodecHDR10PlusInfo = "android.media.mediacodec.hdr10-plus-info"; static const char *kCodecHDRFormat = "android.media.mediacodec.hdr-format"; static const char *kCodecHdrStaticInfo = "android.media.mediacodec.hdr-static-info"; static const char *kCodecHdr10PlusInfo = "android.media.mediacodec.hdr10-plus-info"; static const char *kCodecHdrFormat = "android.media.mediacodec.hdr-format"; // array/sync/async/block modes static const char *kCodecArrayMode = "android.media.mediacodec.array-mode"; static const char *kCodecOperationMode = "android.media.mediacodec.operation-mode"; Loading @@ -195,35 +196,44 @@ static const char *kCodecRecentLatencyMin = "android.media.mediacodec.recent.min static const char *kCodecRecentLatencyAvg = "android.media.mediacodec.recent.avg"; /* in us */ static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n"; static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist"; /* in us */ static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec"; /* in sec */ static const char *kCodecFramesReleased = "android.media.mediacodec.frames.released"; static const char *kCodecFramesRendered = "android.media.mediacodec.frames.rendered"; static const char *kCodecFramesSkipped = "android.media.mediacodec.frames.skipped"; static const char *kCodecFramesDropped = "android.media.mediacodec.frames.dropped"; static const char *kCodecFramerateContent = "android.media.mediacodec.framerate.content"; static const char *kCodecFramerateDesired = "android.media.mediacodec.framerate.desired"; static const char *kCodecFramerateActual = "android.media.mediacodec.framerate.actual"; static const char *kCodecFreezeCount = "android.media.mediacodec.freeze.count"; static const char *kCodecFreezeDurationAverage = "android.media.mediacodec.freeze.duration.average"; static const char *kCodecFreezeDurationMax = "android.media.mediacodec.freeze.duration.max"; static const char *kCodecFreezeDurationHistogram = "android.media.mediacodec.freeze.duration.histogram"; static const char *kCodecFreezeDistanceAverage = "android.media.mediacodec.freeze.distance.average"; static const char *kCodecFreezeDistanceHistogram = "android.media.mediacodec.freeze.distance.histogram"; static const char *kCodecJudderCount = "android.media.mediacodec.judder.count"; static const char *kCodecJudderScoreAverage = "android.media.mediacodec.judder.average"; static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder.max"; static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder.histogram"; /* -1: shaper disabled >=0: number of fields changed */ static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped"; // Render metrics static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec"; static const char *kCodecFirstRenderTimeUs = "android.media.mediacodec.first-render-time-us"; static const char *kCodecFramesReleased = "android.media.mediacodec.frames-released"; static const char *kCodecFramesRendered = "android.media.mediacodec.frames-rendered"; static const char *kCodecFramesDropped = "android.media.mediacodec.frames-dropped"; static const char *kCodecFramesSkipped = "android.media.mediacodec.frames-skipped"; static const char *kCodecFramerateContent = "android.media.mediacodec.framerate-content"; static const char *kCodecFramerateDesired = "android.media.mediacodec.framerate-desired"; static const char *kCodecFramerateActual = "android.media.mediacodec.framerate-actual"; static const char *kCodecFreezeCount = "android.media.mediacodec.freeze-count"; static const char *kCodecFreezeScore = "android.media.mediacodec.freeze-score"; static const char *kCodecFreezeRate = "android.media.mediacodec.freeze-rate"; static const char *kCodecFreezeDurationMsAvg = "android.media.mediacodec.freeze-duration-ms-avg"; static const char *kCodecFreezeDurationMsMax = "android.media.mediacodec.freeze-duration-ms-max"; static const char *kCodecFreezeDurationMsHistogram = "android.media.mediacodec.freeze-duration-ms-histogram"; static const char *kCodecFreezeDurationMsHistogramBuckets = "android.media.mediacodec.freeze-duration-ms-histogram-buckets"; static const char *kCodecFreezeDistanceMsAvg = "android.media.mediacodec.freeze-distance-ms-avg"; static const char *kCodecFreezeDistanceMsHistogram = "android.media.mediacodec.freeze-distance-ms-histogram"; static const char *kCodecFreezeDistanceMsHistogramBuckets = "android.media.mediacodec.freeze-distance-ms-histogram-buckets"; static const char *kCodecJudderCount = "android.media.mediacodec.judder-count"; static const char *kCodecJudderScore = "android.media.mediacodec.judder-score"; static const char *kCodecJudderRate = "android.media.mediacodec.judder-rate"; static const char *kCodecJudderScoreAvg = "android.media.mediacodec.judder-score-avg"; static const char *kCodecJudderScoreMax = "android.media.mediacodec.judder-score-max"; static const char *kCodecJudderScoreHistogram = "android.media.mediacodec.judder-score-histogram"; static const char *kCodecJudderScoreHistogramBuckets = "android.media.mediacodec.judder-score-histogram-buckets"; // XXX suppress until we get our representation right static bool kEmitHistogram = false; Loading Loading @@ -1120,6 +1130,7 @@ void MediaCodec::updateMediametrics() { { const VideoRenderQualityMetrics &m = mVideoRenderQualityTracker.getMetrics(); if (m.frameRenderedCount > 0) { mediametrics_setInt64(mMetricsHandle, kCodecFirstRenderTimeUs, m.firstRenderTimeUs); mediametrics_setInt64(mMetricsHandle, kCodecFramesReleased, m.frameReleasedCount); mediametrics_setInt64(mMetricsHandle, kCodecFramesRendered, m.frameRenderedCount); mediametrics_setInt64(mMetricsHandle, kCodecFramesSkipped, m.frameSkippedCount); Loading @@ -1129,23 +1140,33 @@ void MediaCodec::updateMediametrics() { mediametrics_setDouble(mMetricsHandle, kCodecFramerateActual, m.actualFrameRate); } if (m.freezeDurationMsHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.freezeDurationMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, histogram.getCount()); mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationAverage, histogram.getAvg()); mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationMax, histogram.getMax()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.freezeDurationMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeScore, m.freezeScore); mediametrics_setDouble(mMetricsHandle, kCodecFreezeRate, m.freezeRate); mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, h.getCount()); mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsAvg, h.getAvg()); mediametrics_setInt32(mMetricsHandle, kCodecFreezeDurationMsMax, h.getMax()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecFreezeDurationMsHistogramBuckets, h.emitBuckets()); } if (m.freezeDistanceMsHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.freezeDistanceMsHistogram; mediametrics_setInt64(mMetricsHandle, kCodecFreezeDistanceAverage, histogram.getAvg()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.freezeDistanceMsHistogram; mediametrics_setInt32(mMetricsHandle, kCodecFreezeDistanceMsAvg, h.getAvg()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceMsHistogramBuckets, h.emitBuckets()); } if (m.judderScoreHistogram.getCount() >= 1) { const MediaHistogram &histogram = m.judderScoreHistogram; mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, histogram.getCount()); mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreAverage, histogram.getAvg()); mediametrics_setInt64(mMetricsHandle, kCodecJudderScoreMax, histogram.getMax()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, histogram.emit()); const MediaHistogram<int32_t> &h = m.judderScoreHistogram; mediametrics_setInt64(mMetricsHandle, kCodecJudderScore, m.judderScore); mediametrics_setDouble(mMetricsHandle, kCodecJudderRate, m.judderRate); mediametrics_setInt64(mMetricsHandle, kCodecJudderCount, h.getCount()); mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreAvg, h.getAvg()); mediametrics_setInt32(mMetricsHandle, kCodecJudderScoreMax, h.getMax()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogram, h.emit()); mediametrics_setString(mMetricsHandle, kCodecJudderScoreHistogramBuckets, h.emitBuckets()); } } Loading Loading @@ -1227,14 +1248,14 @@ void MediaCodec::updateHdrMetrics(bool isConfig) { && ColorUtils::isHDRStaticInfoValid(&info)) { mHdrInfoFlags |= kFlagHasHdrStaticInfo; } mediametrics_setInt32(mMetricsHandle, kCodecHDRStaticInfo, mediametrics_setInt32(mMetricsHandle, kCodecHdrStaticInfo, (mHdrInfoFlags & kFlagHasHdrStaticInfo) ? 1 : 0); sp<ABuffer> hdr10PlusInfo; if (mOutputFormat->findBuffer("hdr10-plus-info", &hdr10PlusInfo) && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0) { mHdrInfoFlags |= kFlagHasHdr10PlusInfo; } mediametrics_setInt32(mMetricsHandle, kCodecHDR10PlusInfo, mediametrics_setInt32(mMetricsHandle, kCodecHdr10PlusInfo, (mHdrInfoFlags & kFlagHasHdr10PlusInfo) ? 1 : 0); // hdr format Loading @@ -1247,7 +1268,7 @@ void MediaCodec::updateHdrMetrics(bool isConfig) { && codedFormat->findInt32(KEY_PROFILE, &profile) && colorTransfer != -1) { hdr_format hdrFormat = getHdrFormat(mime, profile, colorTransfer); mediametrics_setInt32(mMetricsHandle, kCodecHDRFormat, static_cast<int>(hdrFormat)); mediametrics_setInt32(mMetricsHandle, kCodecHdrFormat, static_cast<int>(hdrFormat)); } } Loading Loading @@ -1355,9 +1376,8 @@ void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) { return; } MediaHistogram recentHist; // build an empty histogram MediaHistogram<int64_t> recentHist; recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor); // stuff it with the samples in the ring buffer Loading Loading @@ -3593,8 +3613,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { setState(UNINITIALIZED); } else { setState( (mFlags & kFlagIsAsync) ? FLUSHED : STARTED); setState((mFlags & kFlagIsAsync) ? FLUSHED : STARTED); } break; } Loading Loading @@ -3719,6 +3738,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0); } mediametrics_setInt32(mMetricsHandle, kCodecHardware, MediaCodecList::isSoftwareCodec(mComponentName) ? 0 : 1); mResourceManagerProxy->addResource(MediaResource::CodecResource( mFlags & kFlagIsSecure, toMediaResourceSubType(mDomain))); Loading Loading @@ -4133,6 +4155,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { if (mIsSurfaceToDisplay) { mVideoRenderQualityTracker.resetForDiscontinuity(); } // Notify the RM that the codec has been stopped. ClientConfigParcel clientConfig; initClientConfigParcel(clientConfig); Loading Loading @@ -4443,6 +4466,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } else { mTunneled = false; } mediametrics_setInt32(mMetricsHandle, kCodecTunneled, mTunneled ? 1 : 0); int32_t background = 0; if (format->findInt32("android._background-mode", &background) && background) { Loading
media/libstagefright/MediaHistogram.cppdeleted 100644 → 0 +0 −173 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "MediaHistogram" #include <utils/Log.h> #include <media/stagefright/MediaHistogram.h> #include <assert.h> #include <inttypes.h> #include <sstream> #include <stdio.h> namespace android { MediaHistogram::MediaHistogram() { mBuckets = nullptr; mBucketLimits = nullptr; mBucketCount = 0; mFloor = mCeiling = mWidth = 0; mBelow = 0; mAbove = 0; mSum = 0; mCount = 0; mMin = INT64_MAX; mMax = INT64_MIN; } void MediaHistogram::clear() { if (mBuckets != nullptr) { free(mBuckets); mBuckets = nullptr; } if (mBucketLimits != nullptr) { free(mBucketLimits); mBucketLimits = nullptr; } mBucketCount = 0; } bool MediaHistogram::setup(int bucketCount, int64_t width, int64_t floor) { if (bucketCount <= 0 || width <= 0) { return false; } if (!allocate(bucketCount, false)) { return false; } mWidth = width; mFloor = floor; mCeiling = floor + bucketCount * width; mMin = INT64_MAX; mMax = INT64_MIN; mSum = 0; mCount = 0; mBelow = mAbove = 0; return true; } bool MediaHistogram::setup(const std::vector<int64_t> &bucketLimits) { if (bucketLimits.size() <= 1) { return false; } int bucketCount = bucketLimits.size() - 1; if (!allocate(bucketCount, true)) { return false; } mWidth = -1; mFloor = bucketLimits[0]; for (int i = 0; i < bucketCount; ++i) { mBucketLimits[i] = bucketLimits[i + 1]; } mCeiling = bucketLimits[bucketCount]; mMin = INT64_MAX; mMax = INT64_MIN; mSum = 0; mCount = 0; mBelow = mAbove = 0; return true; } bool MediaHistogram::allocate(int bucketCount, bool withBucketLimits) { assert(bucketCount > 0); if (bucketCount != mBucketCount) { clear(); mBuckets = (int64_t *) calloc(bucketCount, sizeof(*mBuckets)); if (mBuckets == nullptr) { return false; } } if (withBucketLimits && mBucketLimits == nullptr) { mBucketLimits = (int64_t *) calloc(bucketCount, sizeof(*mBucketLimits)); if (mBucketLimits == nullptr) { clear(); return false; } } mBucketCount = bucketCount; memset(mBuckets, 0, sizeof(*mBuckets) * mBucketCount); return true; } void MediaHistogram::insert(int64_t sample) { // histogram is not set up if (mBuckets == nullptr) { return; } mCount++; mSum += sample; if (mMin > sample) mMin = sample; if (mMax < sample) mMax = sample; if (sample < mFloor) { mBelow++; } else if (sample >= mCeiling) { mAbove++; } else if (mBucketLimits == nullptr) { int64_t slot = (sample - mFloor) / mWidth; assert(slot < mBucketCount); mBuckets[slot]++; } else { // A binary search might be more efficient for large number of buckets, but it is expected // that there will never be a large amount of buckets, so keep the code simple. for (int slot = 0; slot < mBucketCount; ++slot) { if (sample < mBucketLimits[slot]) { mBuckets[slot]++; break; } } } return; } std::string MediaHistogram::emit() const { // emits: floor,width,below{bucket0,bucket1,...., bucketN}above // or.. emits: below{bucket0,bucket1,...., bucketN}above // unconfigured will emit: 0,0,0{}0 // XXX: is this best representation? std::stringstream ss; if (mBucketLimits == nullptr) { ss << mFloor << "," << mWidth << "," << mBelow << "{"; } else { ss << mBelow << "{"; } for (int i = 0; i < mBucketCount; i++) { if (i != 0) { ss << ","; } ss << mBuckets[i]; } ss << "}" << mAbove; return ss.str(); } } // android
media/libstagefright/VideoRenderQualityTracker.cpp +75 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -34,9 +42,14 @@ VideoRenderQualityMetrics::VideoRenderQualityMetrics() { contentFrameRate = FRAME_RATE_UNDETERMINED; desiredFrameRate = FRAME_RATE_UNDETERMINED; actualFrameRate = FRAME_RATE_UNDETERMINED; freezeDurationMsHistogram.clear(); freezeDistanceMsHistogram.clear(); judderScoreHistogram.clear(); } VideoRenderQualityTracker::Configuration::Configuration() { enabled = true; // Assume that the app is skipping frames because it's detected that the frame couldn't be // rendered in time. areSkippedFramesDropped = true; Loading @@ -53,26 +66,32 @@ VideoRenderQualityTracker::Configuration::Configuration() { // Freeze configuration freezeDurationMsHistogramBuckets = {1, 20, 40, 60, 80, 100, 120, 150, 175, 225, 300, 400, 500}; freezeDurationMsHistogramToScore = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; freezeDistanceMsHistogramBuckets = {0, 20, 100, 400, 1000, 2000, 3000, 4000, 8000, 15000, 30000, 60000}; // Judder configuration judderErrorToleranceUs = 2000; judderScoreHistogramBuckets = {1, 4, 5, 9, 11, 20, 30, 40, 50, 60, 70, 80}; judderScoreHistogramToScore = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; } VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) { configureHistograms(mMetrics, mConfiguration); resetForDiscontinuity(); clear(); } VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) : mConfiguration(configuration) { configureHistograms(mMetrics, mConfiguration); resetForDiscontinuity(); clear(); } void VideoRenderQualityTracker::onFrameSkipped(int64_t contentTimeUs) { if (!mConfiguration.enabled) { return; } // Frames skipped at the beginning shouldn't really be counted as skipped frames, since the // app might be seeking to a starting point that isn't the first key frame. if (mLastRenderTimeUs == -1) { Loading @@ -90,6 +109,10 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs) { void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs, int64_t desiredRenderTimeNs) { if (!mConfiguration.enabled) { return; } int64_t desiredRenderTimeUs = desiredRenderTimeNs / 1000; resetIfDiscontinuity(contentTimeUs, desiredRenderTimeUs); mMetrics.frameReleasedCount++; Loading @@ -98,8 +121,15 @@ void VideoRenderQualityTracker::onFrameReleased(int64_t contentTimeUs, } void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t actualRenderTimeNs) { if (!mConfiguration.enabled) { return; } int64_t actualRenderTimeUs = actualRenderTimeNs / 1000; if (mLastRenderTimeUs != -1) { mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000; } // Now that a frame has been rendered, the previously skipped frames can be processed as skipped // frames since the app is not skipping them to terminate playback. for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) { Loading Loading @@ -131,10 +161,47 @@ void VideoRenderQualityTracker::onFrameRendered(int64_t contentTimeUs, int64_t a mLastRenderTimeUs = actualRenderTimeUs; } const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() const { const VideoRenderQualityMetrics &VideoRenderQualityTracker::getMetrics() { if (!mConfiguration.enabled) { return mMetrics; } mMetrics.freezeScore = 0; if (mConfiguration.freezeDurationMsHistogramToScore.size() == mMetrics.freezeDurationMsHistogram.size()) { for (int i = 0; i < mMetrics.freezeDurationMsHistogram.size(); ++i) { int32_t count = 0; for (int j = i; j < mMetrics.freezeDurationMsHistogram.size(); ++j) { count += mMetrics.freezeDurationMsHistogram[j]; } mMetrics.freezeScore += count / mConfiguration.freezeDurationMsHistogramToScore[i]; } } mMetrics.freezeRate = float(double(mMetrics.freezeDurationMsHistogram.getSum()) / mRenderDurationMs); mMetrics.judderScore = 0; if (mConfiguration.judderScoreHistogramToScore.size() == mMetrics.judderScoreHistogram.size()) { for (int i = 0; i < mMetrics.judderScoreHistogram.size(); ++i) { int32_t count = 0; for (int j = i; j < mMetrics.judderScoreHistogram.size(); ++j) { count += mMetrics.judderScoreHistogram[j]; } mMetrics.judderScore += count / mConfiguration.judderScoreHistogramToScore[i]; } } mMetrics.judderRate = float(double(mMetrics.judderScoreHistogram.getCount()) / (mMetrics.frameReleasedCount + mMetrics.frameSkippedCount)); return mMetrics; } void VideoRenderQualityTracker::clear() { mRenderDurationMs = 0; mMetrics.clear(); resetForDiscontinuity(); } void VideoRenderQualityTracker::resetForDiscontinuity() { mLastContentTimeUs = -1; mLastRenderTimeUs = -1; Loading Loading @@ -220,11 +287,12 @@ void VideoRenderQualityTracker::processMetricsForRenderedFrame(int64_t contentTi int64_t desiredRenderTimeUs, int64_t actualRenderTimeUs) { // Capture the timestamp at which the first frame was rendered if (mMetrics.firstFrameRenderTimeUs == 0) { mMetrics.firstFrameRenderTimeUs = actualRenderTimeUs; if (mMetrics.firstRenderTimeUs == 0) { mMetrics.firstRenderTimeUs = actualRenderTimeUs; } mMetrics.frameRenderedCount++; // The content time is -1 when it was rendered after a discontinuity (e.g. seek) was detected. // So, even though a frame was rendered, it's impact on the user is insignificant, so don't do // anything other than count it as a rendered frame. Loading Loading @@ -352,7 +420,7 @@ float VideoRenderQualityTracker::detectFrameRate(const FrameDurationUs &duration // Only determine frame rate if the render durations are stable across 3 frames if (abs(durationUs[0] - durationUs[1]) > c.frameRateDetectionToleranceUs || abs(durationUs[0] - durationUs[2]) > c.frameRateDetectionToleranceUs) { return is32pulldown(durationUs, c) ? FRAME_RATE_24HZ_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED; return is32pulldown(durationUs, c) ? FRAME_RATE_24_3_2_PULLDOWN : FRAME_RATE_UNDETERMINED; } return 1000.0 * 1000.0 / durationUs[0]; } Loading
media/libstagefright/include/media/stagefright/MediaCodec.h +1 −1 Original line number Diff line number Diff line Loading @@ -715,7 +715,7 @@ private: int mRecentHead; Mutex mRecentLock; MediaHistogram mLatencyHist; MediaHistogram<int64_t> mLatencyHist; // An unique ID for the codec - Used by the metrics. uint64_t mCodecId = 0; Loading