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. */ formatForReport(MediaFormat format)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 */ addPerformanceHeadersToLog( DeviceReportLog log, String message, int round, String codecName, MediaFormat configFormat, MediaFormat inputFormat, MediaFormat outputFormat)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 */ addPerformanceStatsToLog( DeviceReportLog log, MediaUtils.Stats durationsUsStats, String message)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 */ logPerformanceStats( DeviceReportLog log, MediaUtils.Stats statsUs, String prefix, String message)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.*/ verifyAchievableFrameRates( String name, String mime, int w, int h, double... measuredFps)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