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

Commit 7d1066db authored by Brian Lindahl's avatar Brian Lindahl Committed by Android (Google) Code Review
Browse files

Merge changes I5e3a8b02,Ie3995fb9,Ie8b4d2c8 into udc-dev

* changes:
  Add histogram metrics for video playback freezes
  Move Histogram out of MediaCodec so it can be used for render quality metrics
  Capture basic metrics for video frame rendering
parents 4cf4c1da c4e9deef
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -86,6 +86,11 @@ void mediametrics_setRate(mediametrics_handle_t handle, attr_t attr,
    if (item != NULL) item->setRate(attr, count, duration);
    if (item != NULL) item->setRate(attr, count, duration);
}
}


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

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


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

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

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


__END_DECLS
__END_DECLS


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

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


    const Prop::Elem* get(const char *key) const {
    const Prop::Elem* get(const char *key) const {
        const Prop *prop = findProp(key);
        const Prop *prop = findProp(key);
+2 −0
Original line number Original line Diff line number Diff line
@@ -255,6 +255,7 @@ cc_library {
        "MediaCodecSource.cpp",
        "MediaCodecSource.cpp",
        "MediaExtractor.cpp",
        "MediaExtractor.cpp",
        "MediaExtractorFactory.cpp",
        "MediaExtractorFactory.cpp",
        "MediaHistogram.cpp",
        "MediaSource.cpp",
        "MediaSource.cpp",
        "MediaSync.cpp",
        "MediaSync.cpp",
        "MediaTrack.cpp",
        "MediaTrack.cpp",
@@ -270,6 +271,7 @@ cc_library {
        "SurfaceUtils.cpp",
        "SurfaceUtils.cpp",
        "ThrottledSource.cpp",
        "ThrottledSource.cpp",
        "Utils.cpp",
        "Utils.cpp",
        "VideoRenderQualityTracker.cpp",
        "VideoFrameSchedulerBase.cpp",
        "VideoFrameSchedulerBase.cpp",
        "VideoFrameScheduler.cpp",
        "VideoFrameScheduler.cpp",
    ],
    ],
+85 −111
Original line number Original line Diff line number Diff line
@@ -30,7 +30,6 @@
#include <C2Buffer.h>
#include <C2Buffer.h>


#include "include/SoftwareRenderer.h"
#include "include/SoftwareRenderer.h"
#include "PlaybackDurationAccumulator.h"


#include <android/binder_manager.h>
#include <android/binder_manager.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/content/pm/IPackageManagerNative.h>
@@ -199,6 +198,23 @@ static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hi
static const char *kCodecPlaybackDurationSec =
static const char *kCodecPlaybackDurationSec =
        "android.media.mediacodec.playback-duration-sec"; /* in sec */
        "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";

/* -1: shaper disabled
/* -1: shaper disabled
   >=0: number of fields changed */
   >=0: number of fields changed */
static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped";
static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped";
@@ -960,8 +976,7 @@ MediaCodec::MediaCodec(
      mHaveInputSurface(false),
      mHaveInputSurface(false),
      mHavePendingInputBuffers(false),
      mHavePendingInputBuffers(false),
      mCpuBoostRequested(false),
      mCpuBoostRequested(false),
      mPlaybackDurationAccumulator(new PlaybackDurationAccumulator()),
      mIsSurfaceToDisplay(false),
      mIsSurfaceToScreen(false),
      mLatencyUnknown(0),
      mLatencyUnknown(0),
      mBytesEncoded(0),
      mBytesEncoded(0),
      mEarliestEncodedPtsUs(INT64_MAX),
      mEarliestEncodedPtsUs(INT64_MAX),
@@ -1096,6 +1111,32 @@ void MediaCodec::updateMediametrics() {
    mediametrics_setInt32(mMetricsHandle, kCodecResolutionChangeCount,
    mediametrics_setInt32(mMetricsHandle, kCodecResolutionChangeCount,
            mReliabilityContextMetrics.resolutionChangeCount);
            mReliabilityContextMetrics.resolutionChangeCount);


    // Video rendering quality metrics
    {
        const VideoRenderQualityMetrics& m = mVideoRenderQualityTracker.getMetrics();
        if (m.frameRenderedCount > 0) {
            mediametrics_setInt64(mMetricsHandle, kCodecFramesReleased, m.frameReleasedCount);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesRendered, m.frameRenderedCount);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesSkipped, m.frameSkippedCount);
            mediametrics_setInt64(mMetricsHandle, kCodecFramesDropped, m.frameDroppedCount);
            mediametrics_setDouble(mMetricsHandle, kCodecFramerateContent, m.contentFrameRate);
            mediametrics_setDouble(mMetricsHandle, kCodecFramerateDesired, m.desiredFrameRate);
            mediametrics_setDouble(mMetricsHandle, kCodecFramerateActual, m.actualFrameRate);
        }
        if (m.freezeDurationMsHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.freezeDurationMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeCount, histogram.getCount());
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationAverage, histogram.getAvg());
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDurationMax, histogram.getMax());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDurationHistogram, histogram.emit());
        }
        if (m.freezeDistanceMsHistogram.getCount() >= 1) {
            const MediaHistogram &histogram = m.freezeDistanceMsHistogram;
            mediametrics_setInt64(mMetricsHandle, kCodecFreezeDistanceAverage, histogram.getAvg());
            mediametrics_setString(mMetricsHandle, kCodecFreezeDistanceHistogram, histogram.emit());
        }
    }

    if (mLatencyHist.getCount() != 0 ) {
    if (mLatencyHist.getCount() != 0 ) {
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
@@ -1111,7 +1152,7 @@ void MediaCodec::updateMediametrics() {
    if (mLatencyUnknown > 0) {
    if (mLatencyUnknown > 0) {
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
        mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
    }
    }
    int64_t playbackDurationSec = mPlaybackDurationAccumulator->getDurationInSeconds();
    int64_t playbackDurationSec = mPlaybackDurationAccumulator.getDurationInSeconds();
    if (playbackDurationSec > 0) {
    if (playbackDurationSec > 0) {
        mediametrics_setInt64(mMetricsHandle, kCodecPlaybackDurationSec, playbackDurationSec);
        mediametrics_setInt64(mMetricsHandle, kCodecPlaybackDurationSec, playbackDurationSec);
    }
    }
@@ -1302,7 +1343,7 @@ void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) {
        return;
        return;
    }
    }


    Histogram recentHist;
    MediaHistogram recentHist;


    // build an empty histogram
    // build an empty histogram
    recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
    recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
@@ -1436,116 +1477,34 @@ void MediaCodec::updateTunnelPeek(const sp<AMessage> &msg) {
    ALOGV("TunnelPeekState: %s -> %s", asString(previousState), asString(mTunnelPeekState));
    ALOGV("TunnelPeekState: %s -> %s", asString(previousState), asString(mTunnelPeekState));
}
}


void MediaCodec::updatePlaybackDuration(const sp<AMessage> &msg) {
void MediaCodec::processRenderedFrames(const sp<AMessage> &msg) {
    int what = 0;
    int what = 0;
    msg->findInt32("what", &what);
    msg->findInt32("what", &what);
    if (msg->what() != kWhatCodecNotify && what != kWhatOutputFramesRendered) {
    if (msg->what() != kWhatCodecNotify && what != kWhatOutputFramesRendered) {
        static bool logged = false;
        static bool logged = false;
        if (!logged) {
        if (!logged) {
            logged = true;
            logged = true;
            ALOGE("updatePlaybackDuration: expected kWhatOuputFramesRendered (%d)", msg->what());
            ALOGE("processRenderedFrames: expected kWhatOutputFramesRendered (%d)", msg->what());
        }
        return;
        }
        }
    // Playback duration only counts if the buffers are going to the screen.
    if (!mIsSurfaceToScreen) {
        return;
        return;
    }
    }
    // Rendered frames only matter if they're being sent to the display
    if (mIsSurfaceToDisplay) {
        int64_t renderTimeNs;
        int64_t renderTimeNs;
    size_t index = 0;
        for (size_t index = 0;
    while (msg->findInt64(AStringPrintf("%zu-system-nano", index++).c_str(), &renderTimeNs)) {
            msg->findInt64(AStringPrintf("%zu-system-nano", index).c_str(), &renderTimeNs);
        mPlaybackDurationAccumulator->processRenderTime(renderTimeNs);
            index++) {
    }
            // Capture metrics for playback duration
}
            mPlaybackDurationAccumulator.onFrameRendered(renderTimeNs);

            // Capture metrics for quality
bool MediaCodec::Histogram::setup(int nbuckets, int64_t width, int64_t floor)
            int64_t mediaTimeUs = 0;
{
            if (!msg->findInt64(AStringPrintf("%zu-media-time-us", index).c_str(), &mediaTimeUs)) {
    if (nbuckets <= 0 || width <= 0) {
                ALOGE("processRenderedFrames: no media time found");
        return false;
                continue;
    }

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

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

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

    return true;

  bad:
    if (mBuckets != NULL) {
        free(mBuckets);
        mBuckets = NULL;
    }

    return false;
}

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

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

    if (sample < mFloor) {
        mBelow++;
    } else if (sample >= mCeiling) {
        mAbove++;
    } else {
        int64_t slot = (sample - mFloor) / mWidth;
        CHECK(slot < mBucketCount);
        mBuckets[slot]++;
    }
    return;
            }
            }

            mVideoRenderQualityTracker.onFrameRendered(mediaTimeUs, renderTimeNs);
std::string MediaCodec::Histogram::emit()
{
    std::string value;
    char buffer[64];

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


// when we send a buffer to the codec;
// when we send a buffer to the codec;
@@ -3964,7 +3923,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                                asString(previousState),
                                asString(previousState),
                                asString(TunnelPeekState::kBufferRendered));
                                asString(TunnelPeekState::kBufferRendered));
                    }
                    }
                    updatePlaybackDuration(msg);
                    processRenderedFrames(msg);
                    // check that we have a notification set
                    // check that we have a notification set
                    if (mOnFrameRenderedNotification != NULL) {
                    if (mOnFrameRenderedNotification != NULL) {
                        sp<AMessage> notify = mOnFrameRenderedNotification->dup();
                        sp<AMessage> notify = mOnFrameRenderedNotification->dup();
@@ -4158,6 +4117,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                              mState, stateString(mState).c_str());
                              mState, stateString(mState).c_str());
                        break;
                        break;
                    }
                    }

                    if (mIsSurfaceToDisplay) {
                        mVideoRenderQualityTracker.resetForDiscontinuity();
                    }
                    // Notify the RM that the codec has been stopped.
                    // Notify the RM that the codec has been stopped.
                    ClientConfigParcel clientConfig;
                    ClientConfigParcel clientConfig;
                    initClientConfigParcel(clientConfig);
                    initClientConfigParcel(clientConfig);
@@ -4213,6 +4176,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        break;
                        break;
                    }
                    }


                    if (mIsSurfaceToDisplay) {
                        mVideoRenderQualityTracker.resetForDiscontinuity();
                    }

                    if (mFlags & kFlagIsAsync) {
                    if (mFlags & kFlagIsAsync) {
                        setState(FLUSHED);
                        setState(FLUSHED);
                    } else {
                    } else {
@@ -5927,7 +5894,9 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {


        // If rendering to the screen, then schedule a time in the future to poll to see if this
        // If rendering to the screen, then schedule a time in the future to poll to see if this
        // frame was ever rendered to seed onFrameRendered callbacks.
        // frame was ever rendered to seed onFrameRendered callbacks.
        if (mIsSurfaceToScreen) {
        if (mIsSurfaceToDisplay) {
            noRenderTime ? mVideoRenderQualityTracker.onFrameReleased(mediaTimeUs)
                         : mVideoRenderQualityTracker.onFrameReleased(mediaTimeUs, renderTimeNs);
            // can't initialize this in the constructor because the Looper parent class needs to be
            // can't initialize this in the constructor because the Looper parent class needs to be
            // initialized first
            // initialized first
            if (mMsgPollForRenderedBuffers == nullptr) {
            if (mMsgPollForRenderedBuffers == nullptr) {
@@ -5957,6 +5926,11 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
            ALOGI("rendring output error %d", err);
            ALOGI("rendring output error %d", err);
        }
        }
    } else {
    } else {
        if (mIsSurfaceToDisplay) {
            int64_t mediaTimeUs = -1;
            buffer->meta()->findInt64("timeUs", &mediaTimeUs);
            mVideoRenderQualityTracker.onFrameSkipped(mediaTimeUs);
        }
        mBufferChannel->discardBuffer(buffer);
        mBufferChannel->discardBuffer(buffer);
    }
    }


@@ -6023,7 +5997,7 @@ status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {


        // in case we don't connect, ensure that we don't signal the surface is
        // in case we don't connect, ensure that we don't signal the surface is
        // connected to the screen
        // connected to the screen
        mIsSurfaceToScreen = false;
        mIsSurfaceToDisplay = false;


        err = nativeWindowConnect(surface.get(), "connectToSurface");
        err = nativeWindowConnect(surface.get(), "connectToSurface");
        if (err == OK) {
        if (err == OK) {
@@ -6053,7 +6027,7 @@ status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
            // keep track whether or not the buffers of the connected surface go to the screen
            // keep track whether or not the buffers of the connected surface go to the screen
            int result = 0;
            int result = 0;
            surface->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result);
            surface->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result);
            mIsSurfaceToScreen = result != 0;
            mIsSurfaceToDisplay = result != 0;
        }
        }
    }
    }
    // do not return ALREADY_EXISTS unless surfaces are the same
    // do not return ALREADY_EXISTS unless surfaces are the same
@@ -6071,7 +6045,7 @@ status_t MediaCodec::disconnectFromSurface() {
        }
        }
        // assume disconnected even on error
        // assume disconnected even on error
        mSurface.clear();
        mSurface.clear();
        mIsSurfaceToScreen = false;
        mIsSurfaceToDisplay = false;
    }
    }
    return err;
    return err;
}
}
Loading