1 /*
2  * Copyright (C) 2019 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.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
20 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar;
21 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar;
22 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
23 import static android.mediav2.common.cts.CodecTestBase.SupportClass.CODEC_ALL;
24 import static android.mediav2.common.cts.CodecTestBase.SupportClass.CODEC_OPTIONAL;
25 
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 
30 import android.media.AudioFormat;
31 import android.media.MediaCodec;
32 import android.media.MediaCodecInfo;
33 import android.media.MediaExtractor;
34 import android.media.MediaFormat;
35 import android.mediav2.common.cts.CodecDecoderBlockModelTestBase;
36 import android.mediav2.common.cts.CodecDecoderTestBase;
37 import android.mediav2.common.cts.OutputManager;
38 import android.util.Log;
39 import android.view.Surface;
40 
41 import androidx.test.filters.LargeTest;
42 import androidx.test.filters.SmallTest;
43 
44 import com.android.compatibility.common.util.ApiTest;
45 import com.android.compatibility.common.util.CddTest;
46 import com.android.compatibility.common.util.Preconditions;
47 
48 import org.junit.Assume;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.junit.runners.Parameterized;
53 
54 import java.io.File;
55 import java.io.FileInputStream;
56 import java.io.IOException;
57 import java.nio.ByteBuffer;
58 import java.nio.ByteOrder;
59 import java.nio.channels.FileChannel;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Collection;
63 import java.util.List;
64 import java.util.stream.IntStream;
65 
66 /**
67  * Test mediacodec api, decoders and their interactions in byte buffer mode
68  * <p>
69  * The test decodes a compressed frame and stores the result in ByteBuffer. This allows
70  * validating the decoded output. Hence wherever possible we check if the decoded output is
71  * compliant.
72  * <ul>
73  *     <li>For Avc, Hevc, Vpx and Av1, the test expects the decoded output to be identical to
74  *     reference decoded output. The reference decoded output is represented by its CRC32
75  *     checksum and is sent to the test as a parameter along with the test clip.</li>
76  *     <li>For others codecs (mpeg2, mpeg4, h263, ...) the decoded output is checked for
77  *     consistency (doesn't change across runs). No crc32 verification is done because idct for
78  *     these standards are non-normative.</li>
79  *     <li>For lossless audio media types, the test verifies if the rms error between input and
80  *     output is 0.</li>
81  *     <li>For lossy audio media types, the test verifies if the rms error is within 5% of
82  *     reference rms error. The reference value is computed using reference decoder and is sent
83  *     to the test as a parameter along with the test clip.</li>
84  *     <li>For all video components, the test expects the output timestamp list to be identical to
85  *     input timestamp list.</li>
86  *     <li>For all audio components, the test expects the output timestamps to be strictly
87  *     increasing.</li>
88  *     <li>The test also verifies if the component reports a format change if the test clip does
89  *     not use the default format.</li>
90  * </ul>
91  * <p>
92  * The test runs mediacodec in synchronous and asynchronous mode.
93  */
94 @RunWith(Parameterized.class)
95 public class CodecDecoderTest extends CodecDecoderTestBase {
96     private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName();
97     private static final float RMS_ERROR_TOLERANCE = 1.05f;        // 5%
98     private static final String MEDIA_DIR = WorkDir.getMediaDirString();
99 
100     private final String mRefFile;
101     private final String mReconfigFile;
102     private final float mRmsError;
103     private final long mRefCRC;
104     private final SupportClass mSupportRequirements;
105 
106     static {
107         System.loadLibrary("ctsmediav2codecdec_jni");
108     }
109 
CodecDecoderTest(String decoder, String mediaType, String testFile, String refFile, String reconfigFile, float rmsError, long refCRC, SupportClass supportRequirements, String allTestParams)110     public CodecDecoderTest(String decoder, String mediaType, String testFile, String refFile,
111             String reconfigFile, float rmsError, long refCRC, SupportClass supportRequirements,
112             String allTestParams) {
113         super(decoder, mediaType, MEDIA_DIR + testFile, allTestParams);
114         mRefFile = MEDIA_DIR + refFile;
115         mReconfigFile = MEDIA_DIR + reconfigFile;
116         mRmsError = rmsError;
117         mRefCRC = refCRC;
118         mSupportRequirements = supportRequirements;
119     }
120 
readAudioReferenceFile(String file)121     static ByteBuffer readAudioReferenceFile(String file) throws IOException {
122         Preconditions.assertTestFileExists(file);
123         File refFile = new File(file);
124         ByteBuffer refBuffer;
125         try (FileInputStream refStream = new FileInputStream(refFile)) {
126             FileChannel fileChannel = refStream.getChannel();
127             int length = (int) refFile.length();
128             refBuffer = ByteBuffer.allocate(length);
129             refBuffer.order(ByteOrder.LITTLE_ENDIAN);
130             fileChannel.read(refBuffer);
131         }
132         return refBuffer;
133     }
134 
createSubFrames(ByteBuffer buffer, int sfCount)135     private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) {
136         int size = (int) mExtractor.getSampleSize();
137         if (size < 0) return null;
138         mExtractor.readSampleData(buffer, 0);
139         long pts = mExtractor.getSampleTime();
140         int flags = mExtractor.getSampleFlags();
141         if (size < sfCount) sfCount = size;
142         ArrayList<MediaCodec.BufferInfo> list = new ArrayList<>();
143         int offset = 0;
144         for (int i = 0; i < sfCount; i++) {
145             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
146             info.offset = offset;
147             info.presentationTimeUs = pts;
148             info.flags = 0;
149             if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
150                 info.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
151             }
152             if ((flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
153                 info.flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
154             }
155             if (i != sfCount - 1) {
156                 info.size = size / sfCount;
157                 info.flags |= MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME;
158             } else {
159                 info.size = size - offset;
160             }
161             list.add(info);
162             offset += info.size;
163         }
164         return list;
165     }
166 
167     @Parameterized.Parameters(name = "{index}_{0}_{1}")
input()168     public static Collection<Object[]> input() {
169         final boolean isEncoder = false;
170         final boolean needAudio = true;
171         final boolean needVideo = true;
172         // mediaType, testClip, referenceClip, reconfigureTestClip, refRmsError, refCRC32,
173         // SupportClass
174         final List<Object[]> exhaustiveArgsList = new ArrayList<>(Arrays.asList(new Object[][]{
175                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3",
176                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3", 91.026749f, -1L,
177                         CODEC_ALL},
178                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_2ch_44kHz_lame_cbr.mp3",
179                         "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_lame_vbr.mp3", 103.603081f, -1L,
180                         CODEC_ALL},
181                 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, "bbb_1ch_16kHz_16kbps_amrwb.3gp",
182                         "bbb_1ch_16kHz_s16le.raw", "bbb_1ch_16kHz_23kbps_amrwb.3gp", 2393.5979f,
183                         -1L, CODEC_ALL},
184                 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, "bbb_1ch_8kHz_10kbps_amrnb.3gp",
185                         "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_8kbps_amrnb.3gp", -1.0f, -1L,
186                         CODEC_ALL},
187                 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_1ch_16kHz_flac.mka",
188                         "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_flac.mka", 0.0f, -1L, CODEC_ALL},
189                 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_2ch_44kHz_flac.mka",
190                         "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_flac.mka", 0.0f, -1L, CODEC_ALL},
191                 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_1ch_16kHz.wav", "bbb_1ch_16kHz_s16le.raw",
192                         "bbb_2ch_44kHz.wav", 0.0f, -1L, CODEC_ALL},
193                 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_2ch_44kHz.wav", "bbb_2ch_44kHz_s16le.raw",
194                         "bbb_1ch_16kHz.wav", 0.0f, -1L, CODEC_ALL},
195                 {MediaFormat.MIMETYPE_AUDIO_G711_ALAW, "bbb_1ch_8kHz_alaw.wav",
196                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_alaw.wav", 23.087402f, -1L,
197                         CODEC_ALL},
198                 {MediaFormat.MIMETYPE_AUDIO_G711_MLAW, "bbb_1ch_8kHz_mulaw.wav",
199                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_mulaw.wav", 24.413954f, -1L,
200                         CODEC_ALL},
201                 {MediaFormat.MIMETYPE_AUDIO_MSGSM, "bbb_1ch_8kHz_gsm.wav",
202                         "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_gsm.wav", 946.026978f, -1L,
203                         CODEC_ALL},
204                 {MediaFormat.MIMETYPE_AUDIO_VORBIS, "bbb_1ch_16kHz_vorbis.mka",
205                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_vorbis.mka", -1.0f, -1L,
206                         CODEC_ALL},
207                 {MediaFormat.MIMETYPE_AUDIO_OPUS, "bbb_2ch_48kHz_opus.mka",
208                         "bbb_2ch_48kHz_s16le.raw", "bbb_1ch_48kHz_opus.mka", -1.0f, -1L, CODEC_ALL},
209                 {MediaFormat.MIMETYPE_AUDIO_AAC, "bbb_1ch_16kHz_aac.mp4",
210                         "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_aac.mp4", -1.0f, -1L, CODEC_ALL},
211                 {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4", null,
212                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", -1.0f, -1L, CODEC_ALL},
213                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4", null,
214                         "bbb_520x390_1mbps_30fps_avc.mp4", -1.0f, 1746312400L, CODEC_ALL},
215                 {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4", null,
216                         "bbb_340x280_768kbps_30fps_hevc.mp4", -1.0f, 3061322606L, CODEC_ALL},
217                 {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4",
218                         null, "bbb_176x144_192kbps_15fps_mpeg4.mp4", -1.0f, -1L, CODEC_ALL},
219                 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp",
220                         null, "bbb_176x144_192kbps_10fps_h263.3gp", -1.0f, -1L, CODEC_ALL},
221                 {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm", null,
222                         "bbb_520x390_1mbps_30fps_vp8.webm", -1.0f, 2030620796L, CODEC_ALL},
223                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null,
224                         "bbb_520x390_1mbps_30fps_vp9.webm", -1.0f, 4122701060L, CODEC_ALL},
225                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null,
226                         "bbb_520x390_1mbps_30fps_av1.mp4", -1.0f, 400672933L, CODEC_ALL},
227         }));
228         // P010 support was added in Android T, hence limit the following tests to Android T and
229         // above
230         if (IS_AT_LEAST_T) {
231             exhaustiveArgsList.addAll(Arrays.asList(new Object[][]{
232                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_340x280_24fps_crf22_avc_10bit.mkv",
233                             null, "cosmat_520x390_24fps_crf22_avc_10bit.mkv", -1.0f, 1462636611L,
234                             CODEC_OPTIONAL},
235                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_340x280_24fps_crf22_hevc_10bit.mkv",
236                             null, "cosmat_520x390_24fps_crf22_hevc_10bit.mkv", -1.0f, 2611796790L,
237                             CODEC_OPTIONAL},
238                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_340x280_24fps_crf22_vp9_10bit.mkv",
239                             null, "cosmat_520x390_24fps_crf22_vp9_10bit.mkv", -1.0f, 2419292938L,
240                             CODEC_OPTIONAL},
241                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_340x280_24fps_512kbps_av1_10bit.mkv",
242                             null, "cosmat_520x390_24fps_768kbps_av1_10bit.mkv", -1.0f, 1021109556L,
243                             CODEC_ALL},
244                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_340x280_24fps_crf22_avc_10bit.mkv",
245                             null, "bbb_520x390_1mbps_30fps_avc.mp4", -1.0f, 1462636611L,
246                             CODEC_OPTIONAL},
247                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_340x280_24fps_crf22_hevc_10bit.mkv",
248                             null, "bbb_520x390_1mbps_30fps_hevc.mp4", -1.0f, 2611796790L,
249                             CODEC_OPTIONAL},
250                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_340x280_24fps_crf22_vp9_10bit.mkv",
251                             null, "bbb_520x390_1mbps_30fps_vp9.webm", -1.0f, 2419292938L,
252                             CODEC_OPTIONAL},
253                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_340x280_24fps_512kbps_av1_10bit.mkv",
254                             null, "bbb_520x390_1mbps_30fps_av1.mp4", -1.0f, 1021109556L,
255                             CODEC_ALL},
256                     {MediaFormat.MIMETYPE_VIDEO_AVC, "cosmat_520x390_24fps_crf22_avc_10bit.mkv",
257                             null, "bbb_340x280_768kbps_30fps_avc.mp4", -1.0f, 2245243696L,
258                             CODEC_OPTIONAL},
259                     {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_520x390_24fps_crf22_hevc_10bit.mkv"
260                             , null, "bbb_340x280_768kbps_30fps_hevc.mp4", -1.0f, 2486118612L,
261                             CODEC_OPTIONAL},
262                     {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_520x390_24fps_crf22_vp9_10bit.mkv",
263                             null, "bbb_340x280_768kbps_30fps_vp9.webm", -1.0f, 3677982654L,
264                             CODEC_OPTIONAL},
265                     {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_520x390_24fps_768kbps_av1_10bit.mkv",
266                             null, "bbb_340x280_768kbps_30fps_av1.mp4", -1.0f, 1139081423L,
267                             CODEC_ALL},
268             }));
269         }
270         return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, true);
271     }
272 
verify(OutputManager outBuff, String refFile, float rmsError, int audioFormat, long refCRC, String msg)273     static void verify(OutputManager outBuff, String refFile, float rmsError, int audioFormat,
274             long refCRC, String msg) throws IOException {
275         if (rmsError >= 0) {
276             int bytesPerSample = AudioFormat.getBytesPerSample(audioFormat);
277             ByteBuffer bb = readAudioReferenceFile(refFile);
278             bb.position(0);
279             int bufferSize = bb.limit();
280             assertEquals("error, reference audio buffer contains partial samples\n" + msg, 0,
281                     bufferSize % bytesPerSample);
282             Object refObject = null;
283             int refObjectLen = bufferSize / bytesPerSample;
284             switch (audioFormat) {
285                 case AudioFormat.ENCODING_PCM_8BIT:
286                     refObject = new byte[refObjectLen];
287                     bb.get((byte[]) refObject);
288                     break;
289                 case AudioFormat.ENCODING_PCM_16BIT:
290                     refObject = new short[refObjectLen];
291                     bb.asShortBuffer().get((short[]) refObject);
292                     break;
293                 case AudioFormat.ENCODING_PCM_24BIT_PACKED:
294                     refObject = new int[refObjectLen];
295                     int[] refArray = (int[]) refObject;
296                     for (int i = 0, j = 0; i < bufferSize; i += 3, j++) {
297                         int byte1 = (bb.get() & 0xff);
298                         int byte2 = (bb.get() & 0xff);
299                         int byte3 = (bb.get() & 0xff);
300                         refArray[j] = byte1 | (byte2 << 8) | (byte3 << 16);
301                     }
302                     break;
303                 case AudioFormat.ENCODING_PCM_32BIT:
304                     refObject = new int[refObjectLen];
305                     bb.asIntBuffer().get((int[]) refObject);
306                     break;
307                 case AudioFormat.ENCODING_PCM_FLOAT:
308                     refObject = new float[refObjectLen];
309                     bb.asFloatBuffer().get((float[]) refObject);
310                     break;
311                 default:
312                     fail("unrecognized audio encoding type :- " + audioFormat + "\n" + msg);
313             }
314             float currError = outBuff.getRmsError(refObject, audioFormat);
315             float errMargin = rmsError * RMS_ERROR_TOLERANCE;
316             assertTrue(String.format("%s rms error too high ref/exp/got %f/%f/%f \n", refFile,
317                     rmsError, errMargin, currError) + msg, currError <= errMargin);
318         } else if (refCRC >= 0) {
319             assertEquals("checksum mismatch \n" + msg, refCRC, outBuff.getCheckSumImage());
320         }
321     }
322 
doOutputFormatChecks(MediaFormat defaultFormat, MediaFormat configuredFormat)323     void doOutputFormatChecks(MediaFormat defaultFormat, MediaFormat configuredFormat) {
324         String msg = String.format("Input test file format is not same as default format of"
325                         + " component, but test did not receive INFO_OUTPUT_FORMAT_CHANGED signal"
326                         + ".\nInput file format is :- %s \nDefault format is :- %s \n",
327                 configuredFormat, defaultFormat);
328         assertTrue(msg + mTestConfig + mTestEnv,
329                 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
330                         mSignalledOutFormatChanged);
331         MediaFormat outputFormat =
332                 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : mOutFormat;
333         msg = String.format("Configured input format and received output format are "
334                 + "not similar. \nConfigured Input format is :- %s \nReceived Output "
335                 + "format is :- %s \n", configuredFormat, outputFormat);
336         assertTrue(msg + mTestConfig + mTestEnv, isFormatSimilar(configuredFormat, outputFormat));
337     }
338 
339     @Before
setUp()340     public void setUp() throws IOException {
341         MediaFormat format = setUpSource(mTestFile);
342         mExtractor.release();
343         ArrayList<MediaFormat> formatList = new ArrayList<>();
344         formatList.add(format);
345         checkFormatSupport(mCodecName, mMediaType, false, formatList, null, mSupportRequirements);
346     }
347 
nativeTestSimpleDecode(String decoder, Surface surface, String mediaType, String testFile, String refFile, int colorFormat, float rmsError, long checksum, StringBuilder retMsg)348     private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mediaType,
349             String testFile, String refFile, int colorFormat, float rmsError, long checksum,
350             StringBuilder retMsg);
351 
352     /**
353      * Verifies if the component under test can decode the test file correctly. The decoding
354      * happens in synchronous, asynchronous mode, eos flag signalled with last compressed frame and
355      * eos flag signalled separately after sending all compressed frames. It expects consistent
356      * output in all these runs. That is, the ByteBuffer info and output timestamp list has to be
357      * same in all the runs. Further for audio, the output timestamp has to be strictly
358      * increasing, for lossless audio codec the rms error has to be 0 and for lossy audio codecs,
359      * the rms error has to be with in tolerance limit. For video the output timestamp list has
360      * to be same as input timestamp list (no frame drops) and for completely normative codecs,
361      * the output checksum has to be identical to reference checksum. For non-normative codecs,
362      * the output has to be consistent. The test also verifies if the component / framework
363      * behavior is consistent between SDK and NDK. The test also verifies if the
364      * component / framework behavior is consistent between block model mode and normal mode.
365      */
366     @CddTest(requirements = {"2.2.2", "2.3.2", "2.5.2", "5.1.2"})
367     @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatYUV420Flexible",
368             "android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatYUVP010",
369             "android.media.AudioFormat#ENCODING_PCM_16BIT",
370             "android.media.MediaCodec#CONFIGURE_FLAG_USE_BLOCK_MODEL"})
371     @LargeTest
372     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecode()373     public void testSimpleDecode() throws IOException, InterruptedException {
374         MediaFormat format = setUpSource(mTestFile);
375         boolean[] boolStates = {true, false};
376         mSaveToMem = true;
377         OutputManager ref = new OutputManager();
378         OutputManager test = new OutputManager(ref.getSharedErrorLogs());
379         {
380             mCodec = MediaCodec.createByCodecName(mCodecName);
381             assertEquals("codec name act/got: " + mCodec.getName() + '/' + mCodecName,
382                     mCodecName, mCodec.getName());
383             assertTrue("error! codec canonical name is null or empty",
384                     mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty());
385             validateMetrics(mCodecName);
386             int loopCounter = 0;
387             for (boolean eosType : boolStates) {
388                 for (boolean isAsync : boolStates) {
389                     boolean validateFormat = true;
390                     mOutputBuff = loopCounter == 0 ? ref : test;
391                     mOutputBuff.reset();
392                     mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
393                     configureCodec(format, isAsync, eosType, false);
394                     MediaFormat defFormat = mCodec.getOutputFormat();
395                     if (isFormatSimilar(format, defFormat)) {
396                         if (ENABLE_LOGS) {
397                             Log.d("Input format is same as default for format for %s", mCodecName);
398                         }
399                         validateFormat = false;
400                     }
401                     mCodec.start();
402                     doWork(Integer.MAX_VALUE);
403                     queueEOS();
404                     waitForAllOutputs();
405                     validateMetrics(mCodecName, format);
406                     endCodecSession(mCodec);
407                     if (loopCounter != 0 && !ref.equals(test)) {
408                         fail("Decoder output is not consistent across runs \n" + mTestConfig
409                                 + mTestEnv + test.getErrMsg());
410                     }
411                     if (validateFormat) {
412                         doOutputFormatChecks(defFormat, format);
413                     }
414                     loopCounter++;
415                 }
416             }
417             mCodec.release();
418             mExtractor.release();
419             // The following part of the test was added after Android U and is not guaranteed
420             // to work on versions before that, hence limit it to Android V and above.
421             if (IS_AT_LEAST_V && mSaveToMem && mIsAudio) {
422                 test.reset();
423                 CodecDecoderBlockModelTestBase cdbmtb = new CodecDecoderBlockModelTestBase(
424                         mCodecName, mMediaType, null, mAllTestParams);
425                 cdbmtb.decodeToMemory(mTestFile, mCodecName, test, 0,
426                         MediaExtractor.SEEK_TO_CLOSEST_SYNC, Integer.MAX_VALUE, true, false);
427                 if (!ref.equals(test)) {
428                     fail("Output in block model mode is not same as output in normal mode. \n"
429                             + mTestConfig + mTestEnv + test.getErrMsg());
430                 }
431             }
432             int colorFormat = mIsAudio ? 0 : format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
433             boolean isPass = nativeTestSimpleDecode(mCodecName, null, mMediaType, mTestFile,
434                     mRefFile, colorFormat, mRmsError, ref.getCheckSumBuffer(), mTestConfig);
435             assertTrue(mTestConfig.toString(), isPass);
436             if (mSaveToMem) {
437                 int audioEncoding = mIsAudio ? format.getInteger(MediaFormat.KEY_PCM_ENCODING,
438                         AudioFormat.ENCODING_PCM_16BIT) : AudioFormat.ENCODING_INVALID;
439                 Assume.assumeFalse("skip checksum due to tone mapping", mSkipChecksumVerification);
440                 verify(mOutputBuff, mRefFile, mRmsError, audioEncoding, mRefCRC,
441                         mTestConfig.toString() + mTestEnv.toString());
442             }
443         }
444     }
445 
446     /**
447      * Verifies component and framework behaviour to flush API when the codec is operating in
448      * byte buffer mode.
449      * <p>
450      * While the component is decoding the test clip, mediacodec flush() is called. The flush API
451      * is called at various points :-
452      * <ul>
453      *     <li>In running state but before queueing any input (might have to resubmit csd as they
454      *     may not have been processed).</li>
455      *     <li>In running state, after queueing 1 frame.</li>
456      *     <li>In running state, after queueing n frames.</li>
457      *     <li>In eos state.</li>
458      * </ul>
459      * <p>
460      * In all situations (pre-flush or post-flush), the test expects the output timestamps to be
461      * strictly increasing. The flush call makes the output received non-deterministic even for a
462      * given input. Hence, besides timestamp checks, no additional validation is done for outputs
463      * received before flush. Post flush, the decode begins from a sync frame. So the test
464      * expects consistent output and this needs to be identical to the reference.
465      * <p>
466      * The test runs mediacodec in synchronous and asynchronous mode.
467      */
468     @ApiTest(apis = {"android.media.MediaCodec#flush"})
469     @LargeTest
470     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlush()471     public void testFlush() throws IOException, InterruptedException {
472         MediaFormat format = setUpSource(mTestFile);
473         mExtractor.release();
474         mCsdBuffers.clear();
475         for (int i = 0; ; i++) {
476             String csdKey = "csd-" + i;
477             if (format.containsKey(csdKey)) {
478                 mCsdBuffers.add(format.getByteBuffer(csdKey));
479             } else break;
480         }
481         final long pts = 500000;
482         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
483         boolean[] boolStates = {true, false};
484         {
485             decodeToMemory(mTestFile, mCodecName, pts, mode, Integer.MAX_VALUE);
486             OutputManager ref = mOutputBuff;
487             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
488             mOutputBuff = test;
489             setUpSource(mTestFile);
490             mCodec = MediaCodec.createByCodecName(mCodecName);
491             for (boolean isAsync : boolStates) {
492                 if (isAsync) continue;  // TODO(b/147576107)
493                 mExtractor.seekTo(0, mode);
494                 configureCodec(format, isAsync, true, false);
495                 MediaFormat defFormat = mCodec.getOutputFormat();
496                 boolean validateFormat = true;
497                 if (isFormatSimilar(format, defFormat)) {
498                     if (ENABLE_LOGS) {
499                         Log.d("Input format is same as default for format for %s", mCodecName);
500                     }
501                     validateFormat = false;
502                 }
503                 mCodec.start();
504 
505                 /* test flush in running state before queuing input */
506                 flushCodec();
507                 if (mIsCodecInAsyncMode) mCodec.start();
508                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
509 
510                 doWork(1);
511                 flushCodec();
512                 if (mIsCodecInAsyncMode) mCodec.start();
513                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
514 
515                 mExtractor.seekTo(0, mode);
516                 test.reset();
517                 doWork(23);
518                 if (!test.isPtsStrictlyIncreasing(mPrevOutputPts)) {
519                     fail("Output timestamps are not strictly increasing \n" + mTestConfig + mTestEnv
520                             + test.getErrMsg());
521                 }
522 
523                 boolean checkMetrics = (mOutputCount != 0);
524 
525                 /* test flush in running state */
526                 flushCodec();
527                 if (checkMetrics) validateMetrics(mCodecName, format);
528                 if (mIsCodecInAsyncMode) mCodec.start();
529                 mSaveToMem = true;
530                 test.reset();
531                 mExtractor.seekTo(pts, mode);
532                 doWork(Integer.MAX_VALUE);
533                 queueEOS();
534                 waitForAllOutputs();
535                 if (isMediaTypeOutputUnAffectedBySeek(mMediaType) && (!ref.equals(test))) {
536                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
537                             + test.getErrMsg());
538                 }
539 
540                 /* test flush in eos state */
541                 flushCodec();
542                 if (mIsCodecInAsyncMode) mCodec.start();
543                 test.reset();
544                 mExtractor.seekTo(pts, mode);
545                 doWork(Integer.MAX_VALUE);
546                 queueEOS();
547                 waitForAllOutputs();
548                 endCodecSession(mCodec);
549                 if (isMediaTypeOutputUnAffectedBySeek(mMediaType) && (!ref.equals(test))) {
550                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
551                             + test.getErrMsg());
552                 }
553                 if (validateFormat) {
554                     doOutputFormatChecks(defFormat, format);
555                 }
556                 mSaveToMem = false;
557             }
558             mCodec.release();
559             mExtractor.release();
560         }
561     }
562 
nativeTestFlush(String decoder, Surface surface, String mediaType, String testFile, int colorFormat, StringBuilder retMsg)563     private native boolean nativeTestFlush(String decoder, Surface surface, String mediaType,
564             String testFile, int colorFormat, StringBuilder retMsg);
565 
566     /**
567      * Test is similar to {@link #testFlush()} but uses ndk api
568      */
569     @ApiTest(apis = {"android.media.MediaCodec#flush"})
570     @LargeTest
571     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlushNative()572     public void testFlushNative() throws IOException {
573         int colorFormat = 0;
574         if (mIsVideo) {
575             MediaFormat format = setUpSource(mTestFile);
576             mExtractor.release();
577             colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
578         }
579         boolean isPass = nativeTestFlush(mCodecName, null, mMediaType, mTestFile,
580                 colorFormat, mTestConfig);
581         assertTrue(mTestConfig.toString(), isPass);
582     }
583 
584     /**
585      * Verifies component and framework behaviour for resolution change in bytebuffer mode. The
586      * resolution change is not seamless (AdaptivePlayback) but done via reconfigure.
587      * <p>
588      * The reconfiguring of media codec component happens at various points :-
589      * <ul>
590      *     <li>After initial configuration (stopped state).</li>
591      *     <li>In running state, before queueing any input.</li>
592      *     <li>In running state, after queuing n frames.</li>
593      *     <li>In eos state.</li>
594      * </ul>
595      * In eos state,
596      * <ul>
597      *     <li>reconfigure with same clip.</li>
598      *     <li>reconfigure with different clip (different resolution).</li>
599      * </ul>
600      * <p>
601      * In all situations (pre-reconfigure or post-reconfigure), the test expects the output
602      * timestamps to be strictly increasing. The reconfigure call makes the output received
603      * non-deterministic even for a given input. Hence, besides timestamp checks, no additional
604      * validation is done for outputs received before reconfigure. Post reconfigure, the decode
605      * begins from a sync frame. So the test expects consistent output and this needs to be
606      * identical to the reference.
607      * <p>
608      * The test runs mediacodec in synchronous and asynchronous mode.
609      * <p>
610      * During reconfiguration, the mode of operation is toggled. That is, if first configure
611      * operates the codec in sync mode, then next configure operates the codec in async mode and
612      * so on.
613      */
614     @ApiTest(apis = "android.media.MediaCodec#configure")
615     @LargeTest
616     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testReconfigure()617     public void testReconfigure() throws IOException, InterruptedException {
618         Assume.assumeTrue("Test needs Android 11", IS_AT_LEAST_R);
619 
620         MediaFormat format = setUpSource(mTestFile);
621         mExtractor.release();
622         MediaFormat newFormat = setUpSource(mReconfigFile);
623         mExtractor.release();
624         ArrayList<MediaFormat> formatList = new ArrayList<>();
625         formatList.add(newFormat);
626         checkFormatSupport(mCodecName, mMediaType, false, formatList, null, mSupportRequirements);
627         final long startTs = 0;
628         final long seekTs = 500000;
629         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
630         boolean[] boolStates = {true, false};
631         {
632             decodeToMemory(mTestFile, mCodecName, startTs, mode, Integer.MAX_VALUE);
633             OutputManager ref = mOutputBuff;
634             decodeToMemory(mReconfigFile, mCodecName, seekTs, mode, Integer.MAX_VALUE);
635             OutputManager configRef = mOutputBuff;
636             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
637             OutputManager configTest = new OutputManager(configRef.getSharedErrorLogs());
638             mCodec = MediaCodec.createByCodecName(mCodecName);
639             for (boolean isAsync : boolStates) {
640                 mOutputBuff = test;
641                 setUpSource(mTestFile);
642                 mExtractor.seekTo(startTs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
643                 configureCodec(format, isAsync, true, false);
644                 MediaFormat defFormat = mCodec.getOutputFormat();
645                 boolean validateFormat = true;
646                 if (isFormatSimilar(format, defFormat)) {
647                     if (ENABLE_LOGS) {
648                         Log.d("Input format is same as default for format for %s", mCodecName);
649                     }
650                     validateFormat = false;
651                 }
652 
653                 /* test reconfigure in stopped state */
654                 reConfigureCodec(format, !isAsync, false, false);
655                 mCodec.start();
656 
657                 /* test reconfigure in running state before queuing input */
658                 reConfigureCodec(format, !isAsync, false, false);
659                 mCodec.start();
660                 doWork(23);
661 
662                 if (mOutputCount != 0) {
663                     if (validateFormat) {
664                         doOutputFormatChecks(defFormat, format);
665                     }
666                     validateMetrics(mCodecName, format);
667                 }
668 
669                 /* test reconfigure codec in running state */
670                 reConfigureCodec(format, isAsync, true, false);
671                 mCodec.start();
672                 mSaveToMem = true;
673                 test.reset();
674                 mExtractor.seekTo(startTs, mode);
675                 doWork(Integer.MAX_VALUE);
676                 queueEOS();
677                 waitForAllOutputs();
678                 endCodecSession(mCodec);
679                 if (!ref.equals(test)) {
680                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
681                             + test.getErrMsg());
682                 }
683                 if (validateFormat) {
684                     doOutputFormatChecks(defFormat, format);
685                 }
686 
687                 /* test reconfigure codec at eos state */
688                 reConfigureCodec(format, !isAsync, false, false);
689                 mCodec.start();
690                 test.reset();
691                 mExtractor.seekTo(startTs, mode);
692                 doWork(Integer.MAX_VALUE);
693                 queueEOS();
694                 waitForAllOutputs();
695                 endCodecSession(mCodec);
696                 if (!ref.equals(test)) {
697                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
698                             + test.getErrMsg());
699                 }
700                 if (validateFormat) {
701                     doOutputFormatChecks(defFormat, format);
702                 }
703                 mExtractor.release();
704 
705                 /* test reconfigure codec for new file */
706                 mOutputBuff = configTest;
707                 setUpSource(mReconfigFile);
708                 reConfigureCodec(newFormat, isAsync, false, false);
709                 if (isFormatSimilar(newFormat, defFormat)) {
710                     if (ENABLE_LOGS) {
711                         Log.d("Input format is same as default for format for %s", mCodecName);
712                     }
713                     validateFormat = false;
714                 }
715                 mCodec.start();
716                 configTest.reset();
717                 mExtractor.seekTo(seekTs, mode);
718                 doWork(Integer.MAX_VALUE);
719                 queueEOS();
720                 waitForAllOutputs();
721                 validateMetrics(mCodecName, newFormat);
722                 endCodecSession(mCodec);
723                 if (!configRef.equals(configTest)) {
724                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
725                             + configTest.getErrMsg());
726                 }
727                 if (validateFormat) {
728                     doOutputFormatChecks(defFormat, newFormat);
729                 }
730                 mSaveToMem = false;
731                 mExtractor.release();
732             }
733             mCodec.release();
734         }
735     }
736 
737     /**
738      * Test decoder for EOS only input. As BUFFER_FLAG_END_OF_STREAM is queued with an input buffer
739      * of size 0, during dequeue the test expects to receive BUFFER_FLAG_END_OF_STREAM with an
740      * output buffer of size 0. No input is given, so no output shall be received.
741      */
742     @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_END_OF_STREAM")
743     @SmallTest
744     @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
testOnlyEos()745     public void testOnlyEos() throws IOException, InterruptedException {
746         MediaFormat format = setUpSource(mTestFile);
747         boolean[] boolStates = {true, false};
748         OutputManager ref = new OutputManager();
749         OutputManager test = new OutputManager(ref.getSharedErrorLogs());
750         mSaveToMem = true;
751         {
752             mCodec = MediaCodec.createByCodecName(mCodecName);
753             int loopCounter = 0;
754             for (boolean isAsync : boolStates) {
755                 configureCodec(format, isAsync, false, false);
756                 mOutputBuff = loopCounter == 0 ? ref : test;
757                 mOutputBuff.reset();
758                 mCodec.start();
759                 queueEOS();
760                 waitForAllOutputs();
761                 endCodecSession(mCodec);
762                 if (loopCounter != 0 && !ref.equals(test)) {
763                     fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv
764                             + test.getErrMsg());
765                 }
766                 loopCounter++;
767             }
768             mCodec.release();
769         }
770         mExtractor.release();
771     }
772 
nativeTestOnlyEos(String decoder, String mediaType, String testFile, int colorFormat, StringBuilder retMsg)773     private native boolean nativeTestOnlyEos(String decoder, String mediaType, String testFile,
774             int colorFormat, StringBuilder retMsg);
775 
776     /**
777      * Test is similar to {@link #testOnlyEos()} but uses ndk api
778      */
779     @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_END_OF_STREAM")
780     @SmallTest
781     @Test
testOnlyEosNative()782     public void testOnlyEosNative() throws IOException {
783         int colorFormat = 0;
784         if (mIsVideo) {
785             MediaFormat format = setUpSource(mTestFile);
786             mExtractor.release();
787             colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
788         }
789         boolean isPass = nativeTestOnlyEos(mCodecName, mMediaType, mTestFile, colorFormat,
790                 mTestConfig);
791         assertTrue(mTestConfig.toString(), isPass);
792     }
793 
794     /**
795      * CSD buffers can be queued at configuration or can be queued separately as the first buffer(s)
796      * sent to the codec. This test ensures that both mechanisms function and that they are
797      * semantically the same.
798      */
799     @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_CODEC_CONFIG")
800     @LargeTest
801     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeQueueCSD()802     public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException {
803         MediaFormat format = setUpSource(mTestFile);
804         if (!hasCSD(format)) {
805             mExtractor.release();
806             return;
807         }
808         ArrayList<MediaFormat> formats = new ArrayList<>();
809         formats.add(format);
810         formats.add(new MediaFormat(format));
811         for (int i = 0; ; i++) {
812             String csdKey = "csd-" + i;
813             if (format.containsKey(csdKey)) {
814                 mCsdBuffers.add(format.getByteBuffer(csdKey).duplicate());
815                 format.removeKey(csdKey);
816             } else break;
817         }
818         boolean[] boolStates = {true, false};
819         mSaveToMem = true;
820         OutputManager ref = new OutputManager();
821         OutputManager test = new OutputManager(ref.getSharedErrorLogs());
822         {
823             mCodec = MediaCodec.createByCodecName(mCodecName);
824             int loopCounter = 0;
825             for (int i = 0; i < formats.size(); i++) {
826                 MediaFormat fmt = formats.get(i);
827                 for (boolean eosMode : boolStates) {
828                     for (boolean isAsync : boolStates) {
829                         boolean validateFormat = true;
830                         mOutputBuff = loopCounter == 0 ? ref : test;
831                         mOutputBuff.reset();
832                         mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
833                         configureCodec(fmt, isAsync, eosMode, false);
834                         MediaFormat defFormat = mCodec.getOutputFormat();
835                         if (isFormatSimilar(defFormat, format)) {
836                             if (ENABLE_LOGS) {
837                                 Log.d("Input format is same as default for format for %s",
838                                         mCodecName);
839                             }
840                             validateFormat = false;
841                         }
842                         mCodec.start();
843                         if (i == 0) queueCodecConfig();
844                         doWork(Integer.MAX_VALUE);
845                         queueEOS();
846                         waitForAllOutputs();
847                         validateMetrics(mCodecName);
848                         endCodecSession(mCodec);
849                         if (loopCounter != 0 && !ref.equals(test)) {
850                             fail("Decoder output is not consistent across runs \n" + mTestConfig
851                                     + mTestEnv + test.getErrMsg());
852                         }
853                         if (validateFormat) {
854                             doOutputFormatChecks(defFormat, format);
855                         }
856                         loopCounter++;
857                     }
858                 }
859             }
860             mCodec.release();
861         }
862         mExtractor.release();
863     }
864 
nativeTestSimpleDecodeQueueCSD(String decoder, String mediaType, String testFile, int colorFormat, StringBuilder retMsg)865     private native boolean nativeTestSimpleDecodeQueueCSD(String decoder, String mediaType,
866             String testFile, int colorFormat, StringBuilder retMsg);
867 
868     /**
869      * Test is similar to {@link #testSimpleDecodeQueueCSD()} but uses ndk api
870      */
871     @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_CODEC_CONFIG")
872     @LargeTest
873     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeQueueCSDNative()874     public void testSimpleDecodeQueueCSDNative() throws IOException {
875         MediaFormat format = setUpSource(mTestFile);
876         if (!hasCSD(format)) {
877             mExtractor.release();
878             return;
879         }
880         mExtractor.release();
881         int colorFormat = mIsAudio ? 0 : format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
882         boolean isPass = nativeTestSimpleDecodeQueueCSD(mCodecName, mMediaType, mTestFile,
883                 colorFormat, mTestConfig);
884         assertTrue(mTestConfig.toString(), isPass);
885     }
886 
887     /**
888      * Test decoder for partial frame inputs. The test expects decoder to give same output for a
889      * regular sequence and when any frames of that sequence are delivered in parts using the
890      * PARTIAL_FRAME flag.
891      */
892     @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_PartialFrame",
893             "android.media.MediaCodec#BUFFER_FLAG_PARTIAL_FRAME"})
894     @LargeTest
895     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testDecodePartialFrame()896     public void testDecodePartialFrame() throws IOException, InterruptedException {
897         Assume.assumeTrue("codec: " + mCodecName + " does not advertise FEATURE_PartialFrame",
898                 isFeatureSupported(mCodecName, mMediaType,
899                         MediaCodecInfo.CodecCapabilities.FEATURE_PartialFrame));
900         MediaFormat format = setUpSource(mTestFile);
901         boolean[] boolStates = {true, false};
902         int frameLimit = 10;
903         ByteBuffer buffer = ByteBuffer.allocate(4 * 1024 * 1024);
904         {
905             decodeToMemory(mTestFile, mCodecName, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC,
906                     frameLimit);
907             mCodec = MediaCodec.createByCodecName(mCodecName);
908             OutputManager ref = mOutputBuff;
909             mSaveToMem = true;
910             OutputManager test = new OutputManager(ref.getSharedErrorLogs());
911             mOutputBuff = test;
912             for (boolean isAsync : boolStates) {
913                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
914                 test.reset();
915                 configureCodec(format, isAsync, true, false);
916                 mCodec.start();
917                 doWork(frameLimit - 1);
918                 ArrayList<MediaCodec.BufferInfo> list = createSubFrames(buffer, 4);
919                 assertTrue("no sub frames in list received for " + mTestFile,
920                         list != null && list.size() > 0);
921                 doWork(buffer, list);
922                 queueEOS();
923                 waitForAllOutputs();
924                 endCodecSession(mCodec);
925                 if (!ref.equals(test)) {
926                     fail("Decoder output of a compressed stream segmented at frame/access unit "
927                             + "boundaries is different from a compressed stream segmented at "
928                             + "arbitrary byte boundary \n"
929                             + mTestConfig + mTestEnv + test.getErrMsg());
930                 }
931             }
932             mCodec.release();
933         }
934         mExtractor.release();
935     }
936 
937     /**
938      * Test if decoder outputs 8-bit data for 8-bit as well as 10-bit content by default. The
939      * test removes the key "KEY_COLOR_FORMAT" from the input format to the decoder and verifies if
940      * the component decodes to an 8 bit color format by default.
941      * <p>
942      * The test is applicable for video components only.
943      */
944     @CddTest(requirements = {"5.1.7/C-4-2"})
945     @SmallTest
946     @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
testDefaultOutputColorFormat()947     public void testDefaultOutputColorFormat() throws IOException, InterruptedException {
948         Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T);
949         Assume.assumeTrue("Test is applicable for video decoders", mIsVideo);
950 
951         MediaFormat format = setUpSource(mTestFile);
952         format.removeKey(MediaFormat.KEY_COLOR_FORMAT);
953 
954         mOutputBuff = new OutputManager();
955         mCodec = MediaCodec.createByCodecName(mCodecName);
956         mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
957         configureCodec(format, true, true, false);
958         mCodec.start();
959         doWork(1);
960         queueEOS();
961         waitForAllOutputs();
962         MediaFormat outputFormat = mCodec.getOutputFormat();
963         endCodecSession(mCodec);
964         mCodec.release();
965 
966         assertTrue("Output format from decoder does not contain KEY_COLOR_FORMAT \n" + mTestConfig
967                 + mTestEnv, outputFormat.containsKey(MediaFormat.KEY_COLOR_FORMAT));
968         // 8-bit color formats
969         int[] defaultOutputColorFormatList =
970                 new int[]{COLOR_FormatYUV420Flexible, COLOR_FormatYUV420Planar,
971                         COLOR_FormatYUV420PackedPlanar, COLOR_FormatYUV420SemiPlanar};
972         int outputColorFormat = outputFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
973         assertTrue(String.format("Unexpected output color format %x \n", outputColorFormat)
974                         + mTestConfig + mTestEnv,
975                 IntStream.of(defaultOutputColorFormatList).anyMatch(x -> x == outputColorFormat));
976     }
977 }
978