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

Commit 04d329e7 authored by Brian Lindahl's avatar Brian Lindahl
Browse files

Make video render quality metrics configurable

Bug: 234833109
Test: atest VideoRenderQualityTracker_test
Test: manual test with 'adb shell device_config' commands
Change-Id: I9b06fd1c5085e32ba4a87b1bd5ccc0e6797f058d
parent 4c2a16c7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -314,6 +314,7 @@ cc_library {
        "framework-permission-aidl-cpp",
        "libaudioclient_aidl_conversion",
        "packagemanager_aidl-cpp",
        "server_configurable_flags",
    ],

    static_libs: [
+4 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@
#include <media/stagefright/SurfaceUtils.h>
#include <nativeloader/dlext_namespaces.h>
#include <private/android_filesystem_config.h>
#include <server_configurable_flags/get_flags.h>
#include <utils/Singleton.h>

namespace android {
@@ -1024,6 +1025,9 @@ MediaCodec::MediaCodec(
      mHavePendingInputBuffers(false),
      mCpuBoostRequested(false),
      mIsSurfaceToDisplay(false),
      mVideoRenderQualityTracker(
              VideoRenderQualityTracker::Configuration::getFromServerConfigurableFlags(
                      server_configurable_flags::GetServerConfigurableFlag)),
      mLatencyUnknown(0),
      mBytesEncoded(0),
      mEarliestEncodedPtsUs(INT64_MAX),
+106 −2
Original line number Diff line number Diff line
@@ -20,15 +20,92 @@
#include <media/stagefright/VideoRenderQualityTracker.h>

#include <assert.h>
#include <charconv>
#include <cmath>
#include <stdio.h>
#include <sys/time.h>

#include <android-base/parsebool.h>
#include <android-base/parseint.h>

namespace android {

using android::base::ParseBoolResult;

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;

typedef VideoRenderQualityTracker::Configuration::GetServerConfigurableFlagFn
        GetServerConfigurableFlagFn;

static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
                                      char const *flagNameSuffix, bool *value) {
    std::string flagName("render_metrics_");
    flagName.append(flagNameSuffix);
    std::string valueStr = (*getServerConfigurableFlagFn)("media_native", flagName,
                                                          *value ? "true" : "false");
    switch (android::base::ParseBool(valueStr)) {
    case ParseBoolResult::kTrue: *value = true; break;
    case ParseBoolResult::kFalse: *value = false; break;
    case ParseBoolResult::kError:
        ALOGW("failed to parse server-configurable flag '%s' from '%s'", flagNameSuffix,
              valueStr.c_str());
        break;
    }
}

static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
                                      char const *flagNameSuffix, int32_t *value) {
    char defaultStr[11];
    sprintf(defaultStr, "%d", int(*value));
    std::string flagName("render_metrics_");
    flagName.append(flagNameSuffix);
    std::string valueStr = (*getServerConfigurableFlagFn)("media_native", flagName, defaultStr);
    if (!android::base::ParseInt(valueStr.c_str(), value) || valueStr.size() == 0) {
        ALOGW("failed to parse server-configurable flag '%s' from '%s'", flagNameSuffix,
              valueStr.c_str());
        return;
    }
}

template<typename T>
static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
                                      char const *flagNameSuffix, std::vector<T> *value) {
    std::stringstream sstr;
    for (int i = 0; i < value->size(); ++i) {
        if (i != 0) {
            sstr << ",";
        }
        sstr << (*value)[i];
    }
    std::string flagName("render_metrics_");
    flagName.append(flagNameSuffix);
    std::string valueStr = (*getServerConfigurableFlagFn)("media_native", flagName, sstr.str());
    if (valueStr.size() == 0) {
        return;
    }
    // note: using android::base::Tokenize fails to catch parsing failures for values ending in ','
    std::vector<T> newValues;
    const char *p = valueStr.c_str();
    const char *last = p + valueStr.size();
    while (p != last) {
        if (*p == ',') {
            p++;
        }
        T value = -1;
        auto [ptr, error] = std::from_chars(p, last, value);
        if (error == std::errc::invalid_argument || error == std::errc::result_out_of_range) {
            ALOGW("failed to parse server-configurable flag '%s' from '%s'", flagNameSuffix,
                  valueStr.c_str());
            return;
        }
        p = ptr;
        newValues.push_back(value);
    }
    *value = std::move(newValues);
}

VideoRenderQualityMetrics::VideoRenderQualityMetrics() {
    clear();
}
@@ -49,6 +126,33 @@ void VideoRenderQualityMetrics::clear() {
    judderScoreHistogram.clear();
}

VideoRenderQualityTracker::Configuration
        VideoRenderQualityTracker::Configuration::getFromServerConfigurableFlags(
            GetServerConfigurableFlagFn getServerConfigurableFlagFn) {
    VideoRenderQualityTracker::Configuration c;
#define getFlag(FIELDNAME, FLAGNAME) \
    getServerConfigurableFlag(getServerConfigurableFlagFn, FLAGNAME, &c.FIELDNAME)
    getFlag(enabled, "enabled");
    getFlag(areSkippedFramesDropped, "are_skipped_frames_dropped");
    getFlag(maxExpectedContentFrameDurationUs, "max_expected_content_frame_duration_us");
    getFlag(frameRateDetectionToleranceUs, "frame_rate_detection_tolerance_us");
    getFlag(liveContentFrameDropToleranceUs, "live_content_frame_drop_tolerance_us");
    getFlag(freezeDurationMsHistogramBuckets, "freeze_duration_ms_histogram_buckets");
    getFlag(freezeDurationMsHistogramToScore, "freeze_duration_ms_histogram_to_score");
    getFlag(freezeDistanceMsHistogramBuckets, "freeze_distance_ms_histogram_buckets");
    getFlag(freezeEventMax, "freeze_event_max");
    getFlag(freezeEventDetailsMax, "freeze_event_details_max");
    getFlag(freezeEventDistanceToleranceMs, "freeze_event_distance_tolerance_ms");
    getFlag(judderErrorToleranceUs, "judder_error_tolerance_us");
    getFlag(judderScoreHistogramBuckets, "judder_score_histogram_buckets");
    getFlag(judderScoreHistogramToScore, "judder_score_histogram_to_score");
    getFlag(judderEventMax, "judder_event_max");
    getFlag(judderEventDetailsMax, "judder_event_details_max");
    getFlag(judderEventDistanceToleranceMs, "judder_event_distance_tolerance_ms");
#undef getFlag
    return c;
}

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

@@ -64,7 +168,7 @@ VideoRenderQualityTracker::Configuration::Configuration() {

    // Allow for a tolerance of 200 milliseconds for determining if we moved forward in content time
    // because of frame drops for live content, or because the user is seeking.
    contentTimeAdvancedForLiveContentToleranceUs = 200 * 1000;
    liveContentFrameDropToleranceUs = 200 * 1000;

    // Freeze configuration
    freezeDurationMsHistogramBuckets = {1, 20, 40, 60, 80, 100, 120, 150, 175, 225, 300, 400, 500};
@@ -303,7 +407,7 @@ bool VideoRenderQualityTracker::resetIfDiscontinuity(int64_t contentTimeUs,
        int64_t desiredFrameDurationUs = desiredRenderTimeUs - mLastRenderTimeUs;
        bool skippedForwardDueToLiveContentFrameDrops =
                abs(contentFrameDurationUs - desiredFrameDurationUs) <
                mConfiguration.contentTimeAdvancedForLiveContentToleranceUs;
                mConfiguration.liveContentFrameDropToleranceUs;
        if (!skippedForwardDueToLiveContentFrameDrops) {
            ALOGI("Video playback jumped %d ms forward in content time (%d -> %d) ",
                int((contentTimeUs - mLastContentTimeUs) / 1000), int(mLastContentTimeUs / 1000),
+17 −8
Original line number Diff line number Diff line
@@ -105,6 +105,15 @@ public:
    // Configurable elements of the metrics algorithms
    class Configuration {
    public:
        // system/server_configurable_flags/libflags/include/get_flags.h:GetServerConfigurableFlag
        typedef std::string (*GetServerConfigurableFlagFn)(
                const std::string& experiment_category_name,
                const std::string& experiment_flag_name,
                const std::string& default_value);

        static Configuration getFromServerConfigurableFlags(
                GetServerConfigurableFlagFn getServerConfigurableFlagFn);

        Configuration();

        // Whether or not frame render quality is tracked.
@@ -128,7 +137,7 @@ public:
        // skip forward in content time is due to frame drops. If the app-desired frame duration is
        // short, but the content frame duration is large, it is assumed the app is intentionally
        // seeking forward.
        int32_t contentTimeAdvancedForLiveContentToleranceUs;
        int32_t liveContentFrameDropToleranceUs;

        // Freeze configuration
        //
@@ -140,12 +149,12 @@ public:
        // The values used to distribute distances between freezes across a histogram.
        std::vector<int32_t> freezeDistanceMsHistogramBuckets;
        // The maximum number of freeze events to send back to the caller.
        int64_t freezeEventMax;
        int32_t freezeEventMax;
        // The maximum number of detail entries tracked per freeze event.
        int64_t freezeEventDetailsMax;
        int32_t freezeEventDetailsMax;
        // The maximum distance in time between two freeze occurrences such that both will be
        // lumped into the same freeze event.
        int64_t freezeEventDistanceToleranceMs;
        int32_t freezeEventDistanceToleranceMs;

        // Judder configuration
        //
@@ -155,14 +164,14 @@ public:
        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;
        std::vector<int64_t> judderScoreHistogramToScore;
        // The maximum number of judder events to send back to the caller.
        int64_t judderEventMax;
        int32_t judderEventMax;
        // The maximum number of detail entries tracked per judder event.
        int64_t judderEventDetailsMax;
        int32_t judderEventDetailsMax;
        // The maximum distance in time between two judder occurrences such that both will be
        // lumped into the same judder event.
        int64_t judderEventDistanceToleranceMs;
        int32_t judderEventDistanceToleranceMs;
    };

    struct FreezeEvent {
+3 −3
Original line number Diff line number Diff line
@@ -60,10 +60,10 @@ cc_test {
    name: "VideoRenderQualityTracker_test",
    srcs: ["VideoRenderQualityTracker_test.cpp"],

    // TODO(b/234833109): Figure out why shared_libs linkage causes stack corruption
    static_libs: [
        "libstagefright",
    shared_libs: [
        "libbase",
        "liblog",
        "libstagefright",
    ],

    cflags: [
Loading