1 /*
2  * Copyright (C) 2020 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.mediav2.cts;
18 
19 import static android.mediav2.common.cts.CodecTestBase.SupportClass.CODEC_ALL;
20 import static android.mediav2.common.cts.CodecTestBase.SupportClass.CODEC_OPTIONAL;
21 
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.media.MediaCodec;
26 import android.media.MediaCodecInfo.CodecCapabilities;
27 import android.media.MediaExtractor;
28 import android.media.MediaFormat;
29 import android.media.codec.Flags;
30 import android.mediav2.common.cts.CodecDecoderTestBase;
31 import android.mediav2.common.cts.CodecTestActivity;
32 import android.mediav2.common.cts.OutputManager;
33 import android.os.Build;
34 import android.platform.test.annotations.RequiresFlagsEnabled;
35 import android.util.Log;
36 import android.view.Surface;
37 
38 import androidx.test.ext.junit.rules.ActivityScenarioRule;
39 import androidx.test.filters.LargeTest;
40 import androidx.test.filters.SdkSuppress;
41 
42 import com.android.compatibility.common.util.ApiTest;
43 import com.android.compatibility.common.util.CddTest;
44 
45 import org.junit.Assume;
46 import org.junit.Before;
47 import org.junit.Rule;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.junit.runners.Parameterized;
51 
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collection;
56 import java.util.List;
57 
58 /**
59  * Test mediacodec api, video decoders and their interactions in surface mode.
60  * <p>
61  * When video decoders are configured in surface mode, the getOutputImage() returns null. So
62  * there is no way to validate the decoded output frame analytically. The tests in this class
63  * however ensures that,
64  * <ul>
65  *     <li> The number of decoded frames are equal to the number of input frames.</li>
66  *     <li> The output timestamp list is same as the input timestamp list.</li>
67  *     <li> The timestamp information obtained is consistent with results seen in byte buffer
68  *     mode.</li>
69  * </ul>
70  * <p>
71  * The test verifies all the above needs by running mediacodec in both sync and async mode.
72  */
73 @RunWith(Parameterized.class)
74 public class CodecDecoderSurfaceTest extends CodecDecoderTestBase {
75     private static final String LOG_TAG = CodecDecoderSurfaceTest.class.getSimpleName();
76     private static final String MEDIA_DIR = WorkDir.getMediaDirString();
77 
78     private final String mReconfigFile;
79     private final SupportClass mSupportRequirements;
80 
81     static {
82         System.loadLibrary("ctsmediav2codecdecsurface_jni");
83     }
84 
CodecDecoderSurfaceTest(String decoder, String mediaType, String testFile, String reconfigFile, SupportClass supportRequirements, String allTestParams)85     public CodecDecoderSurfaceTest(String decoder, String mediaType, String testFile,
86             String reconfigFile, SupportClass supportRequirements, String allTestParams) {
87         super(decoder, mediaType, MEDIA_DIR + testFile, allTestParams);
88         mReconfigFile = MEDIA_DIR + reconfigFile;
89         mSupportRequirements = supportRequirements;
90     }
91 
dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)92     protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
93         if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
94             mSawOutputEOS = true;
95         }
96         if (ENABLE_LOGS) {
97             Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
98                     info.size + " timestamp: " + info.presentationTimeUs);
99         }
100         if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
101             mOutputBuff.saveOutPTS(info.presentationTimeUs);
102             mOutputCount++;
103         }
104         mCodec.releaseOutputBuffer(bufferIndex, mSurface != null);
105     }
106 
decodeAndSavePts(String file, String decoder, long pts, int mode, int frameLimit)107     private void decodeAndSavePts(String file, String decoder, long pts, int mode, int frameLimit)
108             throws IOException, InterruptedException {
109         Surface sf = mSurface;
110         mSurface = null; // for reference, decode in non-surface mode
111         mOutputBuff = new OutputManager();
112         mCodec = MediaCodec.createByCodecName(decoder);
113         MediaFormat format = setUpSource(file);
114         configureCodec(format, false, true, false);
115         mCodec.start();
116         mExtractor.seekTo(pts, mode);
117         doWork(frameLimit);
118         queueEOS();
119         waitForAllOutputs();
120         endCodecSession(mCodec);
121         mCodec.release();
122         mExtractor.release();
123         mSurface = sf; // restore surface
124     }
125 
126     @Rule
127     public ActivityScenarioRule<CodecTestActivity> mActivityRule =
128             new ActivityScenarioRule<>(CodecTestActivity.class);
129 
130     @Before
setUp()131     public void setUp() throws IOException, InterruptedException {
132         MediaFormat format = setUpSource(mTestFile);
133         mExtractor.release();
134         if (IS_Q) {
135             Log.i(LOG_TAG, "Android 10: skip checkFormatSupport() for format " + format);
136         } else {
137             ArrayList<MediaFormat> formatList = new ArrayList<>();
138             formatList.add(format);
139             checkFormatSupport(mCodecName, mMediaType, false, formatList, null,
140                     mSupportRequirements);
141         }
142         mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
143         setUpSurface(mActivity);
144     }
145 
146     @Parameterized.Parameters(name = "{index}_{0}_{1}")
input()147     public static Collection<Object[]> input() {
148         final boolean isEncoder = false;
149         final boolean needAudio = false;
150         final boolean needVideo = true;
151         // mediaType, test file, reconfig test file, SupportClass
152         final List<Object[]> exhaustiveArgsList = new ArrayList<>(Arrays.asList(new Object[][]{
153                 {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4",
154                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", CODEC_ALL},
155                 {MediaFormat.MIMETYPE_VIDEO_MPEG2,
156                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_2fields.mp4",
157                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", CODEC_ALL},
158                 {MediaFormat.MIMETYPE_VIDEO_MPEG2,
159                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_1field.ts",
160                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", CODEC_ALL},
161                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4",
162                         "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL},
163                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_360x640_768kbps_30fps_avc.mp4",
164                         "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL},
165                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_160x1024_1500kbps_30fps_avc.mp4",
166                         "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_OPTIONAL},
167                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x120_1500kbps_30fps_avc.mp4",
168                         "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_OPTIONAL},
169                 {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4",
170                         "bbb_340x280_768kbps_30fps_hevc.mp4", CODEC_ALL},
171                 {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4",
172                         "bbb_176x144_192kbps_15fps_mpeg4.mp4", CODEC_ALL},
173                 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp",
174                         "bbb_176x144_192kbps_10fps_h263.3gp", CODEC_ALL},
175                 {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm",
176                         "bbb_520x390_1mbps_30fps_vp8.webm", CODEC_ALL},
177                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm",
178                         "bbb_520x390_1mbps_30fps_vp9.webm", CODEC_ALL},
179                 {MediaFormat.MIMETYPE_VIDEO_VP9,
180                         "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
181                         "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm", CODEC_ALL},
182                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
183                         "bbb_520x390_1mbps_30fps_av1.mp4", CODEC_ALL},
184                 {MediaFormat.MIMETYPE_VIDEO_AV1,
185                         "bikes_qcif_color_bt2020_smpte2086Hlg_bt2020Ncl_fr_av1.mp4",
186                         "bbb_520x390_1mbps_30fps_av1.mp4", CODEC_ALL},
187         }));
188         // P010 support was added in Android T, hence limit the following tests to Android T and
189         // above
190         if (IS_AT_LEAST_T) {
191             exhaustiveArgsList.addAll(Arrays.asList(new Object[][]{
192                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_340x280_24fps_crf22_avc_10bit.mkv",
193                             "cosmat_520x390_24fps_crf22_avc_10bit.mkv", CODEC_OPTIONAL},
194                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_340x280_24fps_crf22_hevc_10bit.mkv",
195                             "cosmat_520x390_24fps_crf22_hevc_10bit.mkv", CODEC_OPTIONAL},
196                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_340x280_24fps_crf22_vp9_10bit.mkv",
197                             "cosmat_520x390_24fps_crf22_vp9_10bit.mkv", CODEC_OPTIONAL},
198                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_340x280_24fps_512kbps_av1_10bit.mkv",
199                             "cosmat_520x390_24fps_768kbps_av1_10bit.mkv", CODEC_ALL},
200                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_340x280_24fps_crf22_avc_10bit.mkv",
201                             "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_OPTIONAL},
202                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_340x280_24fps_crf22_hevc_10bit.mkv",
203                             "bbb_520x390_1mbps_30fps_hevc.mp4", CODEC_OPTIONAL},
204                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_340x280_24fps_crf22_vp9_10bit.mkv",
205                             "bbb_520x390_1mbps_30fps_vp9.webm", CODEC_OPTIONAL},
206                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_340x280_24fps_512kbps_av1_10bit.mkv",
207                             "bbb_520x390_1mbps_30fps_av1.mp4", CODEC_ALL},
208                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_520x390_24fps_crf22_avc_10bit.mkv",
209                             "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_OPTIONAL},
210                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_520x390_24fps_crf22_hevc_10bit.mkv",
211                             "bbb_340x280_768kbps_30fps_hevc.mp4", CODEC_OPTIONAL},
212                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_520x390_24fps_crf22_vp9_10bit.mkv",
213                             "bbb_340x280_768kbps_30fps_vp9.webm", CODEC_OPTIONAL},
214                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_520x390_24fps_768kbps_av1_10bit.mkv",
215                             "bbb_340x280_768kbps_30fps_av1.mp4", CODEC_ALL},
216             }));
217         }
218         return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, true);
219     }
220 
221     /**
222      * Checks if the component under test can decode the test file to surface. The test runs
223      * mediacodec in both synchronous and asynchronous mode. It expects consistent output
224      * timestamp list in all runs and this list to be identical to the reference list. The
225      * reference list is obtained from the same decoder running in byte buffer mode
226      */
227     @CddTest(requirements = {"2.2.2", "2.3.2", "2.5.2"})
228     @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface"})
229     @LargeTest
230     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeToSurface()231     public void testSimpleDecodeToSurface() throws IOException, InterruptedException {
232         boolean[] boolStates = {true, false};
233         final long pts = 0;
234         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
235         {
236             decodeAndSavePts(mTestFile, mCodecName, pts, mode, Integer.MAX_VALUE);
237             OutputManager ref = mOutputBuff;
238             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
239             MediaFormat format = setUpSource(mTestFile);
240             mCodec = MediaCodec.createByCodecName(mCodecName);
241             mOutputBuff = test;
242             mActivity.setScreenParams(getWidth(format), getHeight(format), true);
243             for (boolean isAsync : boolStates) {
244                 mOutputBuff.reset();
245                 mExtractor.seekTo(pts, mode);
246                 configureCodec(format, isAsync, true, false);
247                 mCodec.start();
248                 doWork(Integer.MAX_VALUE);
249                 queueEOS();
250                 waitForAllOutputs();
251                 endCodecSession(mCodec);
252                 if (!(mIsInterlaced ? ref.equalsDequeuedOutput(test) : ref.equals(test))) {
253                     fail("Decoder output in surface mode does not match with output in bytebuffer "
254                             + "mode \n" + mTestConfig + mTestEnv + test.getErrMsg());
255                 }
256             }
257             mCodec.release();
258             mExtractor.release();
259         }
260     }
261 
262     /**
263      * Checks if the component under test can decode the test file to surface, while that
264      * surface is repeatedly detached and reattached. The test runs mediacodec in both
265      * synchronous and asynchronous mode.
266      *
267      * TODO: actually verify that the correct buffers are rendered to the surface
268      * TODO: expand this test to use 2 output surfaces
269      */
270     @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_DetachedSurface",
271                      "android.media.MediaCodec#detachOutputSurface",
272                      "android.media.MediaCodec#CONFIGURE_FLAG_DETACHED_SURFACE"})
273     @LargeTest
274     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
275     @RequiresFlagsEnabled(Flags.FLAG_NULL_OUTPUT_SURFACE)
276     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
277             codeName = "VanillaIceCream")
testDetachAndReattachSurface()278     public void testDetachAndReattachSurface() throws IOException, InterruptedException {
279         boolean[] boolStates = {true, false};
280         final long pts = 0;
281         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
282         {
283             mOutputBuff = new OutputManager();
284             MediaFormat format = setUpSource(mTestFile);
285             mCodec = MediaCodec.createByCodecName(mCodecName);
286             CodecCapabilities caps = mCodec.getCodecInfo()
287                     .getCapabilitiesForType(format.getString(MediaFormat.KEY_MIME));
288             boolean detachable = caps.isFeatureSupported(CodecCapabilities.FEATURE_DetachedSurface);
289 
290             mActivity.setScreenParams(getWidth(format), getHeight(format), true);
291             for (boolean isAsync : boolStates) {
292                 for (boolean startDetached : boolStates) {
293                     mOutputBuff.reset();
294                     mExtractor.seekTo(pts, mode);
295                     if (startDetached) {
296                         try {
297                             configureCodecInDetachedMode(
298                                     format, isAsync,
299                                     isAsync /* cryptoCallAndSignalEosWithLastFrame */);
300                             if (!detachable) {
301                                 fail("configure with CONFIGURE_FLAG_DETACHED_SURFACE did not throw "
302                                         + "even though FEATURE_DetachedSurface is not advertised\n"
303                                         + mTestConfig + mTestEnv);
304                             }
305                         } catch (IllegalArgumentException e) {
306                             if (!detachable) {
307                                 // we got the exception that we expected and we can end this run
308                                 continue;
309                             }
310                             // we got the exception that we expected and we can and this run
311                             fail("configure with CONFIGURE_FLAG_DETACHED_SURFACE failed even "
312                                     + "though FEATURE_DetachedSurface is advertised\n"
313                                     + "Exception is" + e
314                                     + mTestConfig + mTestEnv);
315                         }
316                     } else {
317                         configureCodec(
318                                 format, isAsync, isAsync /* cryptoCallAndSignalEosWithLastFrame */,
319                                 false /* isEncoder */);
320                     }
321                     mCodec.start();
322 
323                     // TODO: test various burst size of frame outputs on and off the surface
324                     // TODO: switch surface based on number of frames output vs input
325                     final int toggleSequenceLength = 30; // number of frames before surface change
326                     while (!mSawInputEOS) {
327                         doWork(toggleSequenceLength);
328                         try {
329                             mCodec.detachOutputSurface();
330                             if (!detachable) {
331                                 fail("detachOutputSurface() did not throw even though "
332                                         + "FEATURE_DetachedSurface is not advertised\n"
333                                         + mTestConfig + mTestEnv);
334                             }
335                         } catch (IllegalStateException e) {
336                             if (!detachable) {
337                                 // we got the exception that we expected and we can end this run
338                                 break;
339                             }
340                             fail("detachOutputSurface() failed even though "
341                                     + "FEATURE_DetachedSurface is advertised\n"
342                                     + "Exception is" + e
343                                     + mTestConfig + mTestEnv);
344                         }
345                         if (!mSawInputEOS) {
346                             doWork(toggleSequenceLength);
347                         }
348                         mCodec.setOutputSurface(mSurface);
349                     }
350                     queueEOS();
351                     waitForAllOutputs();
352                     endCodecSession(mCodec);
353                 }
354             }
355             mCodec.release();
356             mExtractor.release();
357         }
358     }
359 
360     /**
361      * Checks component and framework behaviour to flush API when the codec is operating in
362      * surface mode.
363      * <p>
364      * While the component is decoding the test clip to surface, mediacodec flush() is called.
365      * The flush API is called at various points :-
366      * <ul>
367      *     <li>In running state but before queueing any input (might have to resubmit csd as they
368      *     may not have been processed).</li>
369      *     <li>In running state, after queueing 1 frame.</li>
370      *     <li>In running state, after queueing n frames.</li>
371      *     <li>In eos state.</li>
372      * </ul>
373      * <p>
374      * In all situations (pre-flush or post-flush), the test expects the output timestamps to be
375      * strictly increasing. The flush call makes the output received non-deterministic even for a
376      * given input. Hence, besides timestamp checks, no additional validation is done for outputs
377      * received before flush. Post flush, the decode begins from a sync frame. So the test
378      * expects consistent output and this needs to be identical to the reference. The reference
379      * is obtained from the same decoder running in byte buffer mode.
380      * <p>
381      * The test runs mediacodec in synchronous and asynchronous mode.
382      */
383     @ApiTest(apis = {"android.media.MediaCodec#flush"})
384     @LargeTest
385     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlush()386     public void testFlush() throws IOException, InterruptedException {
387         MediaFormat format = setUpSource(mTestFile);
388         mExtractor.release();
389         mCsdBuffers.clear();
390         for (int i = 0; ; i++) {
391             String csdKey = "csd-" + i;
392             if (format.containsKey(csdKey)) {
393                 mCsdBuffers.add(format.getByteBuffer(csdKey));
394             } else break;
395         }
396         final long pts = 500000;
397         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
398         boolean[] boolStates = {true, false};
399         {
400             decodeAndSavePts(mTestFile, mCodecName, pts, mode, Integer.MAX_VALUE);
401             OutputManager ref = mOutputBuff;
402             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
403             mOutputBuff = test;
404             setUpSource(mTestFile);
405             mCodec = MediaCodec.createByCodecName(mCodecName);
406             mActivity.setScreenParams(getWidth(format), getHeight(format), false);
407             for (boolean isAsync : boolStates) {
408                 if (isAsync) continue;  // TODO(b/147576107)
409                 mExtractor.seekTo(0, mode);
410                 configureCodec(format, isAsync, true, false);
411                 mCodec.start();
412 
413                 /* test flush in running state before queuing input */
414                 flushCodec();
415                 if (mIsCodecInAsyncMode) mCodec.start();
416                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
417 
418                 doWork(1);
419                 flushCodec();
420                 if (mIsCodecInAsyncMode) mCodec.start();
421                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
422 
423                 mExtractor.seekTo(0, mode);
424                 test.reset();
425                 doWork(23);
426                 if (!test.isPtsStrictlyIncreasing(mPrevOutputPts)) {
427                     fail("Output timestamps are not strictly increasing \n" + mTestConfig + mTestEnv
428                             + test.getErrMsg());
429                 }
430 
431                 /* test flush in running state */
432                 flushCodec();
433                 if (mIsCodecInAsyncMode) mCodec.start();
434                 test.reset();
435                 mExtractor.seekTo(pts, mode);
436                 doWork(Integer.MAX_VALUE);
437                 queueEOS();
438                 waitForAllOutputs();
439                 if (!(mIsInterlaced ? ref.equalsDequeuedOutput(test) : ref.equals(test))) {
440                     fail("Decoder output in surface mode does not match with output in bytebuffer "
441                             + "mode \n" + mTestConfig + mTestEnv + test.getErrMsg());
442                 }
443 
444                 /* test flush in eos state */
445                 flushCodec();
446                 if (mIsCodecInAsyncMode) mCodec.start();
447                 test.reset();
448                 mExtractor.seekTo(pts, mode);
449                 doWork(Integer.MAX_VALUE);
450                 queueEOS();
451                 waitForAllOutputs();
452                 endCodecSession(mCodec);
453                 if (!(mIsInterlaced ? ref.equalsDequeuedOutput(test) : ref.equals(test))) {
454                     fail("Decoder output in surface mode does not match with output in bytebuffer "
455                             + "mode \n" + mTestConfig + mTestEnv + test.getErrMsg());
456                 }
457             }
458             mCodec.release();
459             mExtractor.release();
460         }
461     }
462 
463     /**
464      * Checks component and framework behaviour for resolution change in surface mode. The
465      * resolution change is not seamless (AdaptivePlayback) but done via reconfigure.
466      * <p>
467      * The reconfiguring of media codec component happens at various points :-
468      * <ul>
469      *     <li>After initial configuration (stopped state).</li>
470      *     <li>In running state, before queueing any input.</li>
471      *     <li>In running state, after queuing n frames.</li>
472      *     <li>In eos state.</li>
473      * </ul>
474      * In eos state,
475      * <ul>
476      *     <li>reconfigure with same clip.</li>
477      *     <li>reconfigure with different clip (different resolution).</li>
478      * </ul>
479      * <p>
480      * In all situations (pre-reconfigure or post-reconfigure), the test expects the output
481      * timestamps to be strictly increasing. The reconfigure call makes the output received
482      * non-deterministic even for a given input. Hence, besides timestamp checks, no additional
483      * validation is done for outputs received before reconfigure. Post reconfigure, the decode
484      * begins from a sync frame. So the test expects consistent output and this needs to be
485      * identical to the reference. The reference is obtained from the same decoder running in
486      * byte buffer mode.
487      * <p>
488      * The test runs mediacodec in synchronous and asynchronous mode.
489      * <p>
490      * During reconfiguration, the mode of operation is toggled. That is, if first configure
491      * operates the codec in sync mode, then next configure operates the codec in async mode and
492      * so on.
493      */
494     @ApiTest(apis = "android.media.MediaCodec#configure")
495     @LargeTest
496     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testReconfigure()497     public void testReconfigure() throws IOException, InterruptedException {
498         Assume.assumeTrue("Test needs Android 11", IS_AT_LEAST_R);
499 
500         MediaFormat format = setUpSource(mTestFile);
501         mExtractor.release();
502         MediaFormat newFormat = setUpSource(mReconfigFile);
503         mExtractor.release();
504         ArrayList<MediaFormat> formatList = new ArrayList<>();
505         formatList.add(newFormat);
506         checkFormatSupport(mCodecName, mMediaType, false, formatList, null, mSupportRequirements);
507         final long pts = 500000;
508         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
509         boolean[] boolStates = {true, false};
510         {
511             decodeAndSavePts(mTestFile, mCodecName, pts, mode, Integer.MAX_VALUE);
512             OutputManager ref = mOutputBuff;
513             decodeAndSavePts(mReconfigFile, mCodecName, pts, mode, Integer.MAX_VALUE);
514             OutputManager configRef = mOutputBuff;
515             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
516             OutputManager configTest = new OutputManager(configRef.getSharedErrorLogs());
517             mCodec = MediaCodec.createByCodecName(mCodecName);
518             mActivity.setScreenParams(getWidth(format), getHeight(format), false);
519             for (boolean isAsync : boolStates) {
520                 mOutputBuff = test;
521                 setUpSource(mTestFile);
522                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
523                 configureCodec(format, isAsync, true, false);
524 
525                 /* test reconfigure in stopped state */
526                 reConfigureCodec(format, !isAsync, false, false);
527                 mCodec.start();
528 
529                 /* test reconfigure in running state before queuing input */
530                 reConfigureCodec(format, !isAsync, false, false);
531                 mCodec.start();
532                 doWork(23);
533 
534                 /* test reconfigure codec in running state */
535                 reConfigureCodec(format, isAsync, true, false);
536                 mCodec.start();
537                 test.reset();
538                 mExtractor.seekTo(pts, mode);
539                 doWork(Integer.MAX_VALUE);
540                 queueEOS();
541                 waitForAllOutputs();
542                 endCodecSession(mCodec);
543                 if (!(mIsInterlaced ? ref.equalsDequeuedOutput(test) : ref.equals(test))) {
544                     fail("Decoder output in surface mode does not match with output in bytebuffer "
545                             + "mode \n" + mTestConfig + mTestEnv + test.getErrMsg());
546                 }
547 
548                 /* test reconfigure codec at eos state */
549                 reConfigureCodec(format, !isAsync, false, false);
550                 mCodec.start();
551                 test.reset();
552                 mExtractor.seekTo(pts, mode);
553                 doWork(Integer.MAX_VALUE);
554                 queueEOS();
555                 waitForAllOutputs();
556                 endCodecSession(mCodec);
557                 if (!(mIsInterlaced ? ref.equalsDequeuedOutput(test) : ref.equals(test))) {
558                     fail("Decoder output in surface mode does not match with output in bytebuffer "
559                             + "mode \n" + mTestConfig + mTestEnv + test.getErrMsg());
560                 }
561                 mExtractor.release();
562 
563                 /* test reconfigure codec for new file */
564                 mOutputBuff = configTest;
565                 setUpSource(mReconfigFile);
566                 mActivity.setScreenParams(getWidth(newFormat), getHeight(newFormat), true);
567                 reConfigureCodec(newFormat, isAsync, false, false);
568                 mCodec.start();
569                 configTest.reset();
570                 mExtractor.seekTo(pts, mode);
571                 doWork(Integer.MAX_VALUE);
572                 queueEOS();
573                 waitForAllOutputs();
574                 endCodecSession(mCodec);
575                 if (!(mIsInterlaced ? configRef.equalsDequeuedOutput(configTest) :
576                         configRef.equals(configTest))) {
577                     fail("Decoder output in surface mode does not match with output in bytebuffer "
578                             + "mode \n" + mTestConfig + mTestEnv + configTest.getErrMsg());
579                 }
580                 mExtractor.release();
581             }
582             mCodec.release();
583         }
584     }
585 
nativeTestSimpleDecode(String decoder, Surface surface, String mediaType, String testFile, String refFile, int colorFormat, float rmsError, long checksum, StringBuilder retMsg)586     private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mediaType,
587             String testFile, String refFile, int colorFormat, float rmsError, long checksum,
588             StringBuilder retMsg);
589 
590     /**
591      * Tests is similar to {@link #testSimpleDecodeToSurface()} but uses ndk api
592      */
593     @CddTest(requirements = {"2.2.2", "2.3.2", "2.5.2"})
594     @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface"})
595     @LargeTest
596     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeToSurfaceNative()597     public void testSimpleDecodeToSurfaceNative() throws IOException {
598         MediaFormat format = setUpSource(mTestFile);
599         mExtractor.release();
600         mActivity.setScreenParams(getWidth(format), getHeight(format), false);
601         boolean isPass = nativeTestSimpleDecode(mCodecName, mSurface, mMediaType, mTestFile,
602                 mReconfigFile, format.getInteger(MediaFormat.KEY_COLOR_FORMAT), -1.0f, 0L,
603                 mTestConfig);
604         assertTrue(mTestConfig.toString(), isPass);
605     }
606 
nativeTestFlush(String decoder, Surface surface, String mediaType, String testFile, int colorFormat, StringBuilder retMsg)607     private native boolean nativeTestFlush(String decoder, Surface surface, String mediaType,
608             String testFile, int colorFormat, StringBuilder retMsg);
609 
610     /**
611      * Test is similar to {@link #testFlush()} but uses ndk api
612      */
613     @ApiTest(apis = {"android.media.MediaCodec#flush"})
614     @LargeTest
615     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlushNative()616     public void testFlushNative() throws IOException {
617         MediaFormat format = setUpSource(mTestFile);
618         mExtractor.release();
619         mActivity.setScreenParams(getWidth(format), getHeight(format), true);
620         boolean isPass = nativeTestFlush(mCodecName, mSurface, mMediaType, mTestFile,
621                 format.getInteger(MediaFormat.KEY_COLOR_FORMAT), mTestConfig);
622         assertTrue(mTestConfig.toString(), isPass);
623     }
624 }
625