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