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