1 /* 2 * Copyright (C) 2021 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 17 package android.mediapc.cts; 18 19 import static android.mediapc.cts.CodecTestBase.codecFilter; 20 import static android.mediapc.cts.CodecTestBase.codecPrefix; 21 import static android.mediapc.cts.CodecTestBase.mediaTypePrefix; 22 import static android.mediapc.cts.CodecTestBase.selectCodecs; 23 import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs; 24 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assume.assumeFalse; 28 29 import android.media.MediaFormat; 30 import android.mediapc.cts.common.Utils; 31 import android.os.Build; 32 import android.view.Surface; 33 34 import androidx.test.rule.ActivityTestRule; 35 36 import org.junit.After; 37 import org.junit.Before; 38 import org.junit.Rule; 39 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 45 public class FrameDropTestBase { 46 private static final String LOG_TAG = FrameDropTestBase.class.getSimpleName(); 47 static final boolean[] boolStates = {false, true}; 48 static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC; 49 static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC; 50 static final String VP8 = MediaFormat.MIMETYPE_VIDEO_VP8; 51 static final String VP9 = MediaFormat.MIMETYPE_VIDEO_VP9; 52 static final String AV1 = MediaFormat.MIMETYPE_VIDEO_AV1; 53 static final String AAC = MediaFormat.MIMETYPE_AUDIO_AAC; 54 static final String AAC_LOAD_FILE_NAME = "bbb_1c_128kbps_aac_audio.mp4"; 55 static final String AVC_LOAD_FILE_NAME = "bbb_1920x1080_8mbps_60fps_avc.mp4"; 56 static final long DECODE_31S = 31000; // In ms 57 static final int MAX_FRAME_DROP_FOR_30S; 58 // For perf class R, one frame drop per 10 seconds at 30 fps i.e. 3 drops per 30 seconds 59 static final int MAX_FRAME_DROP_FOR_30S_30FPS_PC_R = 3; 60 // For perf class S, two frame drops per 10 seconds at 60 fps i.e. 6 drops per 30 seconds 61 static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_S = 6; 62 // For perf class T, one frame drop per 10 seconds at 60 fps i.e. 3 drops per 30 seconds 63 static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_T = 3; 64 65 final String mMediaType; 66 final String mDecoderName; 67 final boolean mIsAsync; 68 Surface mSurface; 69 70 private LoadStatus mLoadStatus = null; 71 private Thread mTranscodeLoadThread = null; 72 private Thread mAudioPlaybackLoadThread = null; 73 private Exception mTranscodeLoadException = null; 74 private Exception mAudioPlaybackLoadException = null; 75 76 static String AVC_DECODER_NAME; 77 static String AVC_ENCODER_NAME; 78 static String AAC_DECODER_NAME; 79 static Map<String, String> m540p30FpsTestFiles = new HashMap<>(); 80 static Map<String, String> m1080p30FpsTestFiles = new HashMap<>(); 81 static Map<String, String> m540p60FpsTestFiles = new HashMap<>(); 82 static Map<String, String> m1080p60FpsTestFiles = new HashMap<>(); 83 static Map<String, String> m2160p60FpsTestFiles = new HashMap<>(); 84 static { m540p60FpsTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4")85 m540p60FpsTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4"); m540p60FpsTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4")86 m540p60FpsTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4"); m540p60FpsTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm")87 m540p60FpsTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm"); m540p60FpsTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm")88 m540p60FpsTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm"); m540p60FpsTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4")89 m540p60FpsTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4"); 90 m1080p60FpsTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4")91 m1080p60FpsTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4"); m1080p60FpsTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4")92 m1080p60FpsTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4"); m1080p60FpsTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm")93 m1080p60FpsTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm"); m1080p60FpsTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm")94 m1080p60FpsTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm"); m1080p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4")95 m1080p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4"); 96 m2160p60FpsTestFiles.put(AVC, "bbb_3840x2160_24mbps_60fps_avc.mp4")97 m2160p60FpsTestFiles.put(AVC, "bbb_3840x2160_24mbps_60fps_avc.mp4"); m2160p60FpsTestFiles.put(HEVC, "bbb_3840x2160_18mbps_60fps_hevc.mkv")98 m2160p60FpsTestFiles.put(HEVC, "bbb_3840x2160_18mbps_60fps_hevc.mkv"); m2160p60FpsTestFiles.put(VP8, "bbb_3840x2160_24mbps_60fps_vp8.webm")99 m2160p60FpsTestFiles.put(VP8, "bbb_3840x2160_24mbps_60fps_vp8.webm"); m2160p60FpsTestFiles.put(VP9, "bbb_3840x2160_18mbps_60fps_vp9.webm")100 m2160p60FpsTestFiles.put(VP9, "bbb_3840x2160_18mbps_60fps_vp9.webm"); 101 // Limit AV1 4k tests to 1080p as per PC14 requirements m2160p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4")102 m2160p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4"); 103 m540p30FpsTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4")104 m540p30FpsTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4"); m540p30FpsTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4")105 m540p30FpsTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4"); m540p30FpsTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm")106 m540p30FpsTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm"); m540p30FpsTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm")107 m540p30FpsTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm"); m540p30FpsTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4")108 m540p30FpsTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4"); 109 m1080p30FpsTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4")110 m1080p30FpsTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4"); m1080p30FpsTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4")111 m1080p30FpsTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4"); m1080p30FpsTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm")112 m1080p30FpsTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm"); m1080p30FpsTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm")113 m1080p30FpsTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm"); m1080p30FpsTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4")114 m1080p30FpsTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4"); 115 116 switch (Utils.getPerfClass()) { 117 case Build.VERSION_CODES.TIRAMISU: 118 MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_T; 119 break; 120 case Build.VERSION_CODES.S: 121 MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_S; 122 break; 123 case Build.VERSION_CODES.R: 124 default: 125 MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_30FPS_PC_R; 126 break; 127 } 128 } 129 130 @Before setUp()131 public void setUp() throws Exception { 132 Utils.assumeDeviceMeetsPerformanceClassPreconditions(); 133 134 ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false); 135 assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty()); 136 AVC_DECODER_NAME = listOfAvcHwDecoders.get(0); 137 138 ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true); 139 assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty()); 140 AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0); 141 142 ArrayList<String> listOfAacDecoders = selectCodecs(AAC, null, null, false); 143 assertFalse("Test requires aac decoder", listOfAacDecoders.isEmpty()); 144 AAC_DECODER_NAME = listOfAacDecoders.get(0); 145 146 createSurface(); 147 startLoad(); 148 } 149 150 @After tearDown()151 public void tearDown() throws Exception { 152 stopLoad(); 153 releaseSurface(); 154 } 155 156 @Rule 157 public ActivityTestRule<TestActivity> mActivityRule = 158 new ActivityTestRule<>(TestActivity.class); 159 FrameDropTestBase(String mediaType, String decoderName, boolean isAsync)160 public FrameDropTestBase(String mediaType, String decoderName, boolean isAsync) { 161 mMediaType = mediaType; 162 mDecoderName = decoderName; 163 mIsAsync = isAsync; 164 } 165 166 // Returns the list of objects with mediaTypes and their hardware decoders supporting the 167 // given features combining with sync and async modes. prepareArgumentsList(String[] features)168 static List<Object[]> prepareArgumentsList(String[] features) { 169 final List<Object[]> argsList = new ArrayList<>(); 170 final String[] mediaTypesList = new String[] {AVC, HEVC, VP8, VP9, AV1}; 171 for (String mediaType : mediaTypesList) { 172 if (mediaTypePrefix != null && !mediaType.startsWith(mediaTypePrefix)) { 173 continue; 174 } 175 MediaFormat format = MediaFormat.createVideoFormat(mediaType, 1920, 1080); 176 format.setInteger(MediaFormat.KEY_FRAME_RATE, 30); 177 ArrayList<MediaFormat> formats = new ArrayList<>(); 178 formats.add(format); 179 ArrayList<String> listOfDecoders = 180 selectHardwareCodecs(mediaType, formats, features, false); 181 for (String decoder : listOfDecoders) { 182 if ((codecPrefix != null && !decoder.startsWith(codecPrefix)) 183 || (codecFilter != null && !codecFilter.matcher(decoder).matches())) { 184 continue; 185 } 186 for (boolean isAsync : boolStates) { 187 argsList.add(new Object[]{mediaType, decoder, isAsync}); 188 } 189 } 190 } 191 return argsList; 192 } 193 getAchievedPerfClass(int frameRate, int frameDropCount)194 protected int getAchievedPerfClass(int frameRate, int frameDropCount) { 195 int pc = 0; 196 if (frameRate == 30) { 197 pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_30FPS_PC_R ? Build.VERSION_CODES.R : 0; 198 } else { 199 pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_T ? Build.VERSION_CODES.TIRAMISU 200 : frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_S ? Build.VERSION_CODES.S 201 : 0; 202 } 203 return pc; 204 } 205 createSurface()206 private void createSurface() throws InterruptedException { 207 mActivityRule.getActivity().waitTillSurfaceIsCreated(); 208 mSurface = mActivityRule.getActivity().getSurface(); 209 assertTrue("Surface created is null.", mSurface != null); 210 assertTrue("Surface created is invalid.", mSurface.isValid()); 211 // As we display 1920x1080 and 960x540 only which are of same aspect ratio, we will 212 // be setting screen params to 1920x1080 213 mActivityRule.getActivity().setScreenParams(1920, 1080, true); 214 } 215 releaseSurface()216 private void releaseSurface() { 217 if (mSurface != null) { 218 mSurface.release(); 219 mSurface = null; 220 } 221 } 222 createTranscodeLoad()223 private Thread createTranscodeLoad() { 224 Thread transcodeLoadThread = new Thread(() -> { 225 try { 226 TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_LOAD_FILE_NAME, 227 AVC_DECODER_NAME, AVC_ENCODER_NAME, mLoadStatus); 228 transcodeLoad.doTranscode(); 229 } catch (Exception e) { 230 mTranscodeLoadException = e; 231 } 232 }); 233 return transcodeLoadThread; 234 } 235 createAudioPlaybackLoad()236 private Thread createAudioPlaybackLoad() { 237 Thread audioPlaybackLoadThread = new Thread(() -> { 238 try { 239 AudioPlaybackLoad audioPlaybackLoad = new AudioPlaybackLoad(AAC, AAC_LOAD_FILE_NAME, 240 AAC_DECODER_NAME, mLoadStatus); 241 audioPlaybackLoad.doDecodeAndPlayback(); 242 } catch (Exception e) { 243 mAudioPlaybackLoadException = e; 244 } 245 }); 246 return audioPlaybackLoadThread; 247 } 248 startLoad()249 private void startLoad() { 250 // Start Transcode load (Decoder(1080p) + Encoder(720p)) 251 mLoadStatus = new LoadStatus(); 252 mTranscodeLoadThread = createTranscodeLoad(); 253 mTranscodeLoadThread.start(); 254 // Start 128kbps AAC audio playback 255 mAudioPlaybackLoadThread = createAudioPlaybackLoad(); 256 mAudioPlaybackLoadThread.start(); 257 } 258 stopLoad()259 private void stopLoad() throws Exception { 260 if (mLoadStatus != null) { 261 mLoadStatus.setLoadFinished(); 262 } 263 if (mTranscodeLoadThread != null) { 264 mTranscodeLoadThread.join(); 265 mTranscodeLoadThread = null; 266 } 267 if (mAudioPlaybackLoadThread != null) { 268 mAudioPlaybackLoadThread.join(); 269 mAudioPlaybackLoadThread = null; 270 } 271 if (mTranscodeLoadException != null) throw mTranscodeLoadException; 272 if (mAudioPlaybackLoadException != null) throw mAudioPlaybackLoadException; 273 mLoadStatus = null; 274 } 275 } 276