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