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