blob: 23d7e969a967629057c9d1ed032af4ee29ba9c5e [file] [log] [blame]
/*
* Copyright 2016 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.
*/
package com.android.compatibility.common.util;
import android.media.MediaFormat;
import android.util.Range;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import java.util.Arrays;
import android.util.Log;
public class MediaPerfUtils {
private static final String TAG = "MediaPerfUtils";
private static final int MOVING_AVERAGE_NUM_FRAMES = 10;
private static final int MOVING_AVERAGE_WINDOW_MS = 1000;
// allow a variance of 2.5x for measured frame rates (e.g. 40% of lower-limit to 2.5x of
// upper-limit of the published values). Also allow an extra 20% margin. This also acts as
// a limit for the size of the published rates (e.g. upper-limit / lower-limit <= tolerance).
private static final double FRAMERATE_TOLERANCE = 2.5 * 1.2;
/*
* ------------------ HELPER METHODS FOR ACHIEVABLE FRAME RATES ------------------
*/
/** removes brackets from format to be included in JSON. */
private static String formatForReport(MediaFormat format) {
String asString = "" + format;
return asString.substring(1, asString.length() - 1);
}
/**
* Adds performance header info to |log| for |codecName|, |round|, |configFormat|, |inputFormat|
* and |outputFormat|. Also appends same to |message| and returns the resulting base message
* for logging purposes.
*/
public static String addPerformanceHeadersToLog(
DeviceReportLog log, String message, int round, String codecName,
MediaFormat configFormat, MediaFormat inputFormat, MediaFormat outputFormat) {
String mime = configFormat.getString(MediaFormat.KEY_MIME);
int width = configFormat.getInteger(MediaFormat.KEY_WIDTH);
int height = configFormat.getInteger(MediaFormat.KEY_HEIGHT);
log.addValue("round", round, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("codec_name", codecName, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("mime_type", mime, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("width", width, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("height", height, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("config_format", formatForReport(configFormat),
ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("input_format", formatForReport(inputFormat),
ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("output_format", formatForReport(outputFormat),
ResultType.NEUTRAL, ResultUnit.NONE);
message += " codec=" + codecName + " round=" + round + " configFormat=" + configFormat
+ " inputFormat=" + inputFormat + " outputFormat=" + outputFormat;
Range<Double> reported =
MediaUtils.getVideoCapabilities(codecName, mime)
.getAchievableFrameRatesFor(width, height);
if (reported != null) {
log.addValue("reported_low", reported.getLower(), ResultType.NEUTRAL, ResultUnit.FPS);
log.addValue("reported_high", reported.getUpper(), ResultType.NEUTRAL, ResultUnit.FPS);
message += " reported=" + reported.getLower() + "-" + reported.getUpper();
}
return message;
}
/**
* Adds performance statistics based on the raw |stats| to |log|. Also prints the same into
* logcat. Returns the "final fps" value.
*/
public static double addPerformanceStatsToLog(
DeviceReportLog log, MediaUtils.Stats durationsUsStats, String message) {
MediaUtils.Stats frameAvgUsStats =
durationsUsStats.movingAverage(MOVING_AVERAGE_NUM_FRAMES);
log.addValue(
"window_frames", MOVING_AVERAGE_NUM_FRAMES, ResultType.NEUTRAL, ResultUnit.COUNT);
logPerformanceStats(log, frameAvgUsStats, "frame_avg_stats",
message + " window=" + MOVING_AVERAGE_NUM_FRAMES);
MediaUtils.Stats timeAvgUsStats =
durationsUsStats.movingAverageOverSum(MOVING_AVERAGE_WINDOW_MS * 1000);
log.addValue("window_time", MOVING_AVERAGE_WINDOW_MS, ResultType.NEUTRAL, ResultUnit.MS);
double fps = logPerformanceStats(log, timeAvgUsStats, "time_avg_stats",
message + " windowMs=" + MOVING_AVERAGE_WINDOW_MS);
log.setSummary("fps", fps, ResultType.HIGHER_BETTER, ResultUnit.FPS);
return fps;
}
/**
* Adds performance statistics based on the processed |stats| to |log| using |prefix|.
* Also prints the same into logcat using |message| as the base message. Returns the fps value
* for |stats|. |prefix| must be lowercase alphanumeric underscored format.
*/
private static double logPerformanceStats(
DeviceReportLog log, MediaUtils.Stats statsUs, String prefix, String message) {
final String[] labels = {
"min", "p5", "p10", "p20", "p30", "p40", "p50", "p60", "p70", "p80", "p90", "p95", "max"
};
final double[] points = {
0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100
};
int num = statsUs.getNum();
long avg = Math.round(statsUs.getAverage());
long stdev = Math.round(statsUs.getStdev());
log.addValue(prefix + "_num", num, ResultType.NEUTRAL, ResultUnit.COUNT);
log.addValue(prefix + "_avg", avg / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
log.addValue(prefix + "_stdev", stdev / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
message += " num=" + num + " avg=" + avg + " stdev=" + stdev;
final double[] percentiles = statsUs.getPercentiles(points);
for (int i = 0; i < labels.length; ++i) {
long p = Math.round(percentiles[i]);
message += " " + labels[i] + "=" + p;
log.addValue(prefix + "_" + labels[i], p / 1000., ResultType.NEUTRAL, ResultUnit.MS);
}
// print result to logcat in case test aborts before logs are written
Log.i(TAG, message);
return 1e6 / percentiles[points.length - 2];
}
/** Verifies |measuredFps| against reported achievable rates. Returns null if at least
* one measurement falls within the margins of the reported range. Otherwise, returns
* an error message to display.*/
public static String verifyAchievableFrameRates(
String name, String mime, int w, int h, double... measuredFps) {
Range<Double> reported =
MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h);
String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h;
if (reported == null) {
return "Failed to get " + kind;
}
double lowerBoundary1 = reported.getLower() / FRAMERATE_TOLERANCE;
double upperBoundary1 = reported.getUpper() * FRAMERATE_TOLERANCE;
double lowerBoundary2 = reported.getUpper() / Math.pow(FRAMERATE_TOLERANCE, 2);
double upperBoundary2 = reported.getLower() * Math.pow(FRAMERATE_TOLERANCE, 2);
Log.d(TAG, name + " " + mime + " " + w + "x" + h +
" lowerBoundary1 " + lowerBoundary1 + " upperBoundary1 " + upperBoundary1 +
" lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 +
" measured " + Arrays.toString(measuredFps));
for (double measured : measuredFps) {
if (measured >= lowerBoundary1 && measured <= upperBoundary1
&& measured >= lowerBoundary2 && measured <= upperBoundary2) {
return null;
}
}
return "Expected " + kind + ": " + reported + ".\n"
+ "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n";
}
}