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 android.media.MediaFormat; 20 import android.util.Log; 21 import android.view.Surface; 22 23 import androidx.test.rule.ActivityTestRule; 24 25 import org.junit.After; 26 import org.junit.Before; 27 import org.junit.Rule; 28 29 import java.util.ArrayList; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 34 import static android.mediapc.cts.CodecTestBase.selectCodecs; 35 import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs; 36 import static org.junit.Assert.assertFalse; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assume.assumeFalse; 39 import static org.junit.Assume.assumeTrue; 40 41 public class FrameDropTestBase { 42 private static final String LOG_TAG = FrameDropTestBase.class.getSimpleName(); 43 static final boolean[] boolStates = {false, true}; 44 static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC; 45 static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC; 46 static final String VP8 = MediaFormat.MIMETYPE_VIDEO_VP8; 47 static final String VP9 = MediaFormat.MIMETYPE_VIDEO_VP9; 48 static final String AV1 = MediaFormat.MIMETYPE_VIDEO_AV1; 49 static final String AAC = MediaFormat.MIMETYPE_AUDIO_AAC; 50 static final String AAC_LOAD_FILE_NAME = "bbb_1c_128kbps_aac_audio.mp4"; 51 static final String AVC_LOAD_FILE_NAME = "bbb_1280x720_3mbps_30fps_avc.mp4"; 52 static final long DECODE_31S = 31000; // In ms 53 static final int MAX_ADAPTIVE_PLAYBACK_FRAME_DROP = 0; 54 static final int FRAME_RATE = Utils.isSPerfClass() ? 60 : 30; 55 static final int MAX_FRAME_DROP_FOR_30S; 56 57 final String mMime; 58 final String mDecoderName; 59 final boolean mIsAsync; 60 Surface mSurface; 61 62 private LoadStatus mLoadStatus = null; 63 private Thread mTranscodeLoadThread = null; 64 private Thread mAudioPlaybackLoadThread = null; 65 private Exception mTranscodeLoadException = null; 66 private Exception mAudioPlaybackLoadException = null; 67 68 static String AVC_DECODER_NAME; 69 static String AVC_ENCODER_NAME; 70 static String AAC_DECODER_NAME; 71 static Map<String, String> m540pTestFiles = new HashMap<>(); 72 static Map<String, String> m1080pTestFiles = new HashMap<>(); 73 static { 74 if (Utils.isSPerfClass()) { 75 // Two frame drops per 10 seconds at 60 fps is 6 drops per 30 seconds 76 MAX_FRAME_DROP_FOR_30S = 6; m540pTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4")77 m540pTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4"); m540pTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4")78 m540pTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4"); m540pTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm")79 m540pTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm"); m540pTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm")80 m540pTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm"); m540pTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4")81 m540pTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4"); 82 m1080pTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4")83 m1080pTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4"); m1080pTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4")84 m1080pTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4"); m1080pTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm")85 m1080pTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm"); m1080pTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm")86 m1080pTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm"); m1080pTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4")87 m1080pTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4"); 88 } else if (Utils.isRPerfClass()) { 89 // One frame drops per 10 seconds at 30 fps is 3 drops per 30 seconds 90 MAX_FRAME_DROP_FOR_30S = 3; m540pTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4")91 m540pTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4"); m540pTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4")92 m540pTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4"); m540pTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm")93 m540pTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm"); m540pTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm")94 m540pTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm"); m540pTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4")95 m540pTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4"); 96 m1080pTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4")97 m1080pTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4"); m1080pTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4")98 m1080pTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4"); m1080pTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm")99 m1080pTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm"); m1080pTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm")100 m1080pTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm"); m1080pTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4")101 m1080pTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4"); 102 } else { 103 MAX_FRAME_DROP_FOR_30S = 0; Log.e(LOG_TAG, "Unknown performance class.")104 Log.e(LOG_TAG, "Unknown performance class."); 105 } 106 } 107 108 @Before setUp()109 public void setUp() throws Exception { 110 assumeTrue("Test requires performance class.", Utils.isPerfClass()); 111 112 ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false); 113 assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty()); 114 AVC_DECODER_NAME = listOfAvcHwDecoders.get(0); 115 116 ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true); 117 assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty()); 118 AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0); 119 120 ArrayList<String> listOfAacDecoders = selectCodecs(AAC, null, null, false); 121 assertFalse("Test requires aac decoder", listOfAacDecoders.isEmpty()); 122 AAC_DECODER_NAME = listOfAacDecoders.get(0); 123 124 createSurface(); 125 startLoad(); 126 } 127 128 @After tearDown()129 public void tearDown() throws Exception { 130 stopLoad(); 131 releaseSurface(); 132 } 133 134 @Rule 135 public ActivityTestRule<TestActivity> mActivityRule = 136 new ActivityTestRule<>(TestActivity.class); 137 FrameDropTestBase(String mimeType, String decoderName, boolean isAsync)138 public FrameDropTestBase(String mimeType, String decoderName, boolean isAsync) { 139 mMime = mimeType; 140 mDecoderName = decoderName; 141 mIsAsync = isAsync; 142 } 143 144 // Returns the list of objects with mimeTypes and their hardware decoders supporting the 145 // given features combining with sync and async modes. prepareArgumentsList(String[] features)146 static List<Object[]> prepareArgumentsList(String[] features) { 147 final List<Object[]> argsList = new ArrayList<>(); 148 final String[] mimesList = new String[] {AVC, HEVC, VP8, VP9, AV1}; 149 for (String mime : mimesList) { 150 MediaFormat format = MediaFormat.createVideoFormat(mime, 1920, 1080); 151 format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); 152 ArrayList<MediaFormat> formats = new ArrayList<>(); 153 formats.add(format); 154 ArrayList<String> listOfDecoders = 155 selectHardwareCodecs(mime, formats, features, false); 156 for (String decoder : listOfDecoders) { 157 for (boolean isAsync : boolStates) { 158 argsList.add(new Object[]{mime, decoder, isAsync}); 159 } 160 } 161 } 162 return argsList; 163 } 164 createSurface()165 private void createSurface() throws InterruptedException { 166 mActivityRule.getActivity().waitTillSurfaceIsCreated(); 167 mSurface = mActivityRule.getActivity().getSurface(); 168 assertTrue("Surface created is null.", mSurface != null); 169 assertTrue("Surface created is invalid.", mSurface.isValid()); 170 // As we display 1920x1080 and 960x540 only which are of same aspect ratio, we will 171 // be setting screen params to 1920x1080 172 mActivityRule.getActivity().setScreenParams(1920, 1080, true); 173 } 174 releaseSurface()175 private void releaseSurface() { 176 if (mSurface != null) { 177 mSurface.release(); 178 mSurface = null; 179 } 180 } 181 createTranscodeLoad()182 private Thread createTranscodeLoad() { 183 Thread transcodeLoadThread = new Thread(() -> { 184 try { 185 TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_LOAD_FILE_NAME, 186 AVC_DECODER_NAME, AVC_ENCODER_NAME, mLoadStatus); 187 transcodeLoad.doTranscode(); 188 } catch (Exception e) { 189 mTranscodeLoadException = e; 190 } 191 }); 192 return transcodeLoadThread; 193 } 194 createAudioPlaybackLoad()195 private Thread createAudioPlaybackLoad() { 196 Thread audioPlaybackLoadThread = new Thread(() -> { 197 try { 198 AudioPlaybackLoad audioPlaybackLoad = new AudioPlaybackLoad(AAC, AAC_LOAD_FILE_NAME, 199 AAC_DECODER_NAME, mLoadStatus); 200 audioPlaybackLoad.doDecodeAndPlayback(); 201 } catch (Exception e) { 202 mAudioPlaybackLoadException = e; 203 } 204 }); 205 return audioPlaybackLoadThread; 206 } 207 startLoad()208 private void startLoad() { 209 // TODO: b/183671436 210 // Start Transcode load (Decoder(720p) + Encoder(720p)) 211 mLoadStatus = new LoadStatus(); 212 mTranscodeLoadThread = createTranscodeLoad(); 213 mTranscodeLoadThread.start(); 214 // Start 128kbps AAC audio playback 215 mAudioPlaybackLoadThread = createAudioPlaybackLoad(); 216 mAudioPlaybackLoadThread.start(); 217 } 218 stopLoad()219 private void stopLoad() throws Exception { 220 if (mLoadStatus != null) { 221 mLoadStatus.setLoadFinished(); 222 mLoadStatus = null; 223 } 224 if (mTranscodeLoadThread != null) { 225 mTranscodeLoadThread.join(); 226 mTranscodeLoadThread = null; 227 } 228 if (mAudioPlaybackLoadThread != null) { 229 mAudioPlaybackLoadThread.join(); 230 mAudioPlaybackLoadThread = null; 231 } 232 if (mTranscodeLoadException != null) throw mTranscodeLoadException; 233 if (mAudioPlaybackLoadException != null) throw mAudioPlaybackLoadException; 234 } 235 } 236