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