1 /* 2 * Copyright 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.cts.util; 17 18 import android.media.MediaFormat; 19 import android.util.Range; 20 21 import com.android.compatibility.common.util.DeviceReportLog; 22 import com.android.compatibility.common.util.ResultType; 23 import com.android.compatibility.common.util.ResultUnit; 24 25 import java.util.Arrays; 26 import android.util.Log; 27 28 public class MediaPerfUtils { 29 private static final String TAG = "MediaPerfUtils"; 30 31 private static final int MOVING_AVERAGE_NUM_FRAMES = 10; 32 private static final int MOVING_AVERAGE_WINDOW_MS = 1000; 33 34 // allow a variance of 2x for measured frame rates (e.g. half of lower-limit to double of 35 // upper-limit of the published values). Also allow an extra 10% margin. This also acts as 36 // a limit for the size of the published rates (e.g. upper-limit / lower-limit <= tolerance). 37 private static final double FRAMERATE_TOLERANCE = 2.0 * 1.1; 38 39 /* 40 * ------------------ HELPER METHODS FOR ACHIEVABLE FRAME RATES ------------------ 41 */ 42 43 /** removes brackets from format to be included in JSON. */ 44 private static String formatForReport(MediaFormat format) { 45 String asString = "" + format; 46 return asString.substring(1, asString.length() - 1); 47 } 48 49 /** 50 * Adds performance header info to |log| for |codecName|, |round|, |configFormat|, |inputFormat| 51 * and |outputFormat|. Also appends same to |message| and returns the resulting base message 52 * for logging purposes. 53 */ 54 public static String addPerformanceHeadersToLog( 55 DeviceReportLog log, String message, int round, String codecName, 56 MediaFormat configFormat, MediaFormat inputFormat, MediaFormat outputFormat) { 57 log.addValue("round", round, ResultType.NEUTRAL, ResultUnit.NONE); 58 log.addValue("codec_name", codecName, ResultType.NEUTRAL, ResultUnit.NONE); 59 log.addValue("mime_type", configFormat.getString(MediaFormat.KEY_MIME), 60 ResultType.NEUTRAL, ResultUnit.NONE); 61 log.addValue("width", configFormat.getInteger(MediaFormat.KEY_WIDTH), 62 ResultType.NEUTRAL, ResultUnit.NONE); 63 log.addValue("height", configFormat.getInteger(MediaFormat.KEY_HEIGHT), 64 ResultType.NEUTRAL, ResultUnit.NONE); 65 log.addValue("config_format", formatForReport(configFormat), 66 ResultType.NEUTRAL, ResultUnit.NONE); 67 log.addValue("input_format", formatForReport(inputFormat), 68 ResultType.NEUTRAL, ResultUnit.NONE); 69 log.addValue("output_format", formatForReport(outputFormat), 70 ResultType.NEUTRAL, ResultUnit.NONE); 71 72 message += " codec=" + codecName + " round=" + round + " configFormat=" + configFormat 73 + " inputFormat=" + inputFormat + " outputFormat=" + outputFormat; 74 return message; 75 } 76 77 /** 78 * Adds performance statistics based on the raw |stats| to |log|. Also prints the same into 79 * logcat. Returns the "final fps" value. 80 */ 81 public static double addPerformanceStatsToLog( 82 DeviceReportLog log, MediaUtils.Stats durationsUsStats, String message) { 83 84 MediaUtils.Stats frameAvgUsStats = 85 durationsUsStats.movingAverage(MOVING_AVERAGE_NUM_FRAMES); 86 log.addValue( 87 "window_frames", MOVING_AVERAGE_NUM_FRAMES, ResultType.NEUTRAL, ResultUnit.COUNT); 88 logPerformanceStats(log, frameAvgUsStats, "frame_avg_stats", 89 message + " window=" + MOVING_AVERAGE_NUM_FRAMES); 90 91 MediaUtils.Stats timeAvgUsStats = 92 durationsUsStats.movingAverageOverSum(MOVING_AVERAGE_WINDOW_MS * 1000); 93 log.addValue("window_time", MOVING_AVERAGE_WINDOW_MS, ResultType.NEUTRAL, ResultUnit.MS); 94 double fps = logPerformanceStats(log, timeAvgUsStats, "time_avg_stats", 95 message + " windowMs=" + MOVING_AVERAGE_WINDOW_MS); 96 97 log.setSummary("fps", fps, ResultType.HIGHER_BETTER, ResultUnit.FPS); 98 return fps; 99 } 100 101 /** 102 * Adds performance statistics based on the processed |stats| to |log| using |prefix|. 103 * Also prints the same into logcat using |message| as the base message. Returns the fps value 104 * for |stats|. |prefix| must be lowercase alphanumeric underscored format. 105 */ 106 private static double logPerformanceStats( 107 DeviceReportLog log, MediaUtils.Stats statsUs, String prefix, String message) { 108 final String[] labels = { 109 "min", "p5", "p10", "p20", "p30", "p40", "p50", "p60", "p70", "p80", "p90", "p95", "max" 110 }; 111 final double[] points = { 112 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100 113 }; 114 115 int num = statsUs.getNum(); 116 long avg = Math.round(statsUs.getAverage()); 117 long stdev = Math.round(statsUs.getStdev()); 118 log.addValue(prefix + "_num", num, ResultType.NEUTRAL, ResultUnit.COUNT); 119 log.addValue(prefix + "_avg", avg / 1000., ResultType.LOWER_BETTER, ResultUnit.MS); 120 log.addValue(prefix + "_stdev", stdev / 1000., ResultType.LOWER_BETTER, ResultUnit.MS); 121 message += " num=" + num + " avg=" + avg + " stdev=" + stdev; 122 final double[] percentiles = statsUs.getPercentiles(points); 123 for (int i = 0; i < labels.length; ++i) { 124 long p = Math.round(percentiles[i]); 125 message += " " + labels[i] + "=" + p; 126 log.addValue(prefix + "_" + labels[i], p / 1000., ResultType.NEUTRAL, ResultUnit.MS); 127 } 128 129 // print result to logcat in case test aborts before logs are written 130 Log.i(TAG, message); 131 132 return 1e6 / percentiles[points.length - 2]; 133 } 134 135 /** Verifies |measuredFps| against reported achievable rates. Returns null if at least 136 * one measurement falls within the margins of the reported range. Otherwise, returns 137 * an error message to display.*/ 138 public static String verifyAchievableFrameRates( 139 String name, String mime, int w, int h, double... measuredFps) { 140 Range<Double> reported = 141 MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h); 142 String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h; 143 if (reported == null) { 144 return "Failed to get " + kind; 145 } 146 double lowerBoundary1 = reported.getLower() / FRAMERATE_TOLERANCE; 147 double upperBoundary1 = reported.getUpper() * FRAMERATE_TOLERANCE; 148 double lowerBoundary2 = reported.getUpper() / Math.pow(FRAMERATE_TOLERANCE, 2); 149 double upperBoundary2 = reported.getLower() * Math.pow(FRAMERATE_TOLERANCE, 2); 150 Log.d(TAG, name + " " + mime + " " + w + "x" + h + 151 " lowerBoundary1 " + lowerBoundary1 + " upperBoundary1 " + upperBoundary1 + 152 " lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 + 153 " measured " + Arrays.toString(measuredFps)); 154 155 for (double measured : measuredFps) { 156 if (measured >= lowerBoundary1 && measured <= upperBoundary1 157 && measured >= lowerBoundary2 && measured <= upperBoundary2) { 158 return null; 159 } 160 } 161 162 return "Expected " + kind + ": " + reported + ".\n" 163 + "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n"; 164 } 165 } 166