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