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 android.media.MediaCodec; 20 import android.media.MediaCodecInfo; 21 import android.media.MediaFormat; 22 import android.os.Bundle; 23 import android.util.Log; 24 25 import androidx.test.filters.LargeTest; 26 import androidx.test.filters.SmallTest; 27 28 import org.junit.Assume; 29 import org.junit.Ignore; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.Parameterized; 33 34 import java.io.IOException; 35 import java.nio.ByteBuffer; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collection; 39 import java.util.List; 40 41 import static org.junit.Assert.assertFalse; 42 import static org.junit.Assert.assertTrue; 43 import static org.junit.Assert.fail; 44 45 /** 46 * Validate encode functionality of listed encoder components 47 * 48 * The test aims to test all encoders advertised in MediaCodecList. Hence we are not using 49 * MediaCodecList#findEncoderForFormat to create codec. Further, it can so happen that the 50 * test clip chosen is not supported by component (codecCapabilities.isFormatSupported() 51 * fails), then it is better to remove the format but not skip testing the component. The idea 52 * of these tests are not to cover CDD requirements but to test components and their plugins 53 */ 54 @RunWith(Parameterized.class) 55 public class CodecEncoderTest extends CodecEncoderTestBase { 56 private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName(); 57 private int mNumSyncFramesReceived; 58 private ArrayList<Integer> mSyncFramesPos; 59 CodecEncoderTest(String encoder, String mime, int[] bitrates, int[] encoderInfo1, int[] encoderInfo2)60 public CodecEncoderTest(String encoder, String mime, int[] bitrates, int[] encoderInfo1, 61 int[] encoderInfo2) { 62 super(encoder, mime, bitrates, encoderInfo1, encoderInfo2); 63 mSyncFramesPos = new ArrayList<>(); 64 } 65 66 @Override resetContext(boolean isAsync, boolean signalEOSWithLastFrame)67 void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { 68 super.resetContext(isAsync, signalEOSWithLastFrame); 69 mNumSyncFramesReceived = 0; 70 mSyncFramesPos.clear(); 71 } 72 73 @Override flushCodec()74 void flushCodec() { 75 super.flushCodec(); 76 mNumSyncFramesReceived = 0; 77 mSyncFramesPos.clear(); 78 } 79 dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)80 void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { 81 if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { 82 mNumSyncFramesReceived += 1; 83 mSyncFramesPos.add(mOutputCount); 84 } 85 super.dequeueOutput(bufferIndex, info); 86 } 87 forceSyncFrame()88 private void forceSyncFrame() { 89 final Bundle syncFrame = new Bundle(); 90 syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); 91 if (ENABLE_LOGS) { 92 Log.v(LOG_TAG, "requesting key frame"); 93 } 94 mCodec.setParameters(syncFrame); 95 } 96 updateBitrate(int bitrate)97 private void updateBitrate(int bitrate) { 98 final Bundle bitrateUpdate = new Bundle(); 99 bitrateUpdate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitrate); 100 if (ENABLE_LOGS) { 101 Log.v(LOG_TAG, "requesting bitrate to be changed to " + bitrate); 102 } 103 mCodec.setParameters(bitrateUpdate); 104 } 105 106 @Parameterized.Parameters(name = "{index}({0}_{1})") input()107 public static Collection<Object[]> input() { 108 final boolean isEncoder = true; 109 final boolean needAudio = true; 110 final boolean needVideo = true; 111 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 112 // Audio - CodecMime, arrays of bit-rates, sample rates, channel counts 113 {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 11025, 114 22050, 44100, 48000}, new int[]{1, 2}}, 115 {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{6600, 8850, 12650, 14250, 15850, 116 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}}, 117 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950, 118 10200, 12200}, new int[]{8000}, new int[]{1}}, 119 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850, 120 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}}, 121 /* TODO(169310292) */ 122 {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{/* 0, 1, 2, */ 3, 4, 5, 6, 7, 8}, 123 new int[]{8000, 48000, 96000, 192000}, new int[]{1, 2}}, 124 125 // Video - CodecMime, arrays of bit-rates, height, width 126 {MediaFormat.MIMETYPE_VIDEO_H263, new int[]{32000, 64000}, new int[]{176}, 127 new int[]{144}}, 128 {MediaFormat.MIMETYPE_VIDEO_MPEG4, new int[]{32000, 64000}, new int[]{176}, 129 new int[]{144}}, 130 {MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000, 512000}, new int[]{176, 352, 131 352, 480}, new int[]{144, 240, 288, 360}}, 132 {MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000, 512000}, new int[]{176, 352, 133 352, 480}, new int[]{144, 240, 288, 360}}, 134 {MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{256000, 512000}, new int[]{176, 352, 135 352, 480}, new int[]{144, 240, 288, 360}}, 136 {MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000, 512000}, new int[]{176, 352, 137 352, 480}, new int[]{144, 240, 288, 360}}, 138 {MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000, 512000}, new int[]{176, 352, 139 352, 480}, new int[]{144, 240, 288, 360}}, 140 }); 141 return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, true); 142 } 143 144 /** 145 * Tests encoder for combinations: 146 * 1. Codec Sync Mode, Signal Eos with Last frame 147 * 2. Codec Sync Mode, Signal Eos Separately 148 * 3. Codec Async Mode, Signal Eos with Last frame 149 * 4. Codec Async Mode, Signal Eos Separately 150 * In all these scenarios, Timestamp ordering is verified. The output has to be 151 * consistent (not flaky) in all runs. 152 */ 153 @LargeTest 154 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleEncode()155 public void testSimpleEncode() throws IOException, InterruptedException { 156 setUpParams(Integer.MAX_VALUE); 157 boolean[] boolStates = {true, false}; 158 setUpSource(mInputFile); 159 OutputManager ref = new OutputManager(); 160 OutputManager test = new OutputManager(); 161 { 162 mCodec = MediaCodec.createByCodecName(mCodecName); 163 assertTrue("codec name act/got: " + mCodec.getName() + '/' + mCodecName, 164 mCodec.getName().equals(mCodecName)); 165 assertTrue("error! codec canonical name is null", 166 mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty()); 167 /* TODO(b/149027258) */ 168 if (true) mSaveToMem = false; 169 else mSaveToMem = true; 170 for (MediaFormat format : mFormats) { 171 int loopCounter = 0; 172 if (mIsAudio) { 173 mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); 174 mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); 175 } else { 176 mWidth = format.getInteger(MediaFormat.KEY_WIDTH); 177 mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); 178 } 179 for (boolean eosType : boolStates) { 180 for (boolean isAsync : boolStates) { 181 String log = String.format( 182 "format: %s \n codec: %s, file: %s, mode: %s, eos type: %s:: ", 183 format, mCodecName, mInputFile, (isAsync ? "async" : "sync"), 184 (eosType ? "eos with last frame" : "eos separate")); 185 mOutputBuff = loopCounter == 0 ? ref : test; 186 mOutputBuff.reset(); 187 mInfoList.clear(); 188 validateMetrics(mCodecName); 189 configureCodec(format, isAsync, eosType, true); 190 mCodec.start(); 191 doWork(Integer.MAX_VALUE); 192 queueEOS(); 193 waitForAllOutputs(); 194 validateMetrics(mCodecName, format); 195 /* TODO(b/147348711) */ 196 if (false) mCodec.stop(); 197 else mCodec.reset(); 198 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 199 assertTrue(log + "no input sent", 0 != mInputCount); 200 assertTrue(log + "output received", 0 != mOutputCount); 201 if (!mIsAudio) { 202 assertTrue( 203 log + "input count != output count, act/exp: " + mOutputCount + 204 " / " + mInputCount, mInputCount == mOutputCount); 205 } 206 if (loopCounter != 0) { 207 assertTrue(log + "encoder output is flaky", ref.equals(test)); 208 } else { 209 if (mIsAudio) { 210 assertTrue(log + " pts is not strictly increasing", 211 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 212 } else { 213 assertTrue( 214 log + " input pts list and output pts list are not identical", 215 ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 216 } 217 } 218 loopCounter++; 219 } 220 } 221 } 222 mCodec.release(); 223 } 224 } 225 isCodecLossless(String mime)226 private boolean isCodecLossless(String mime) { 227 return mime.equals(MediaFormat.MIMETYPE_AUDIO_FLAC) || 228 mime.equals(MediaFormat.MIMETYPE_AUDIO_RAW); 229 } 230 231 /** 232 * Identity test for encoder 233 */ 234 @LargeTest 235 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testLosslessEncodeDecode()236 public void testLosslessEncodeDecode() throws IOException, InterruptedException { 237 Assume.assumeTrue(isCodecLossless(mMime)); 238 setUpParams(Integer.MAX_VALUE); 239 setUpSource(mInputFile); 240 mOutputBuff = new OutputManager(); 241 { 242 mCodec = MediaCodec.createByCodecName(mCodecName); 243 mSaveToMem = true; 244 for (MediaFormat format : mFormats) { 245 if (mIsAudio) { 246 mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); 247 mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); 248 } else { 249 mWidth = format.getInteger(MediaFormat.KEY_WIDTH); 250 mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); 251 } 252 String log = String.format("format: %s \n codec: %s, file: %s :: ", format, 253 mCodecName, mInputFile); 254 mOutputBuff.reset(); 255 mInfoList.clear(); 256 configureCodec(format, true, true, true); 257 mCodec.start(); 258 doWork(Integer.MAX_VALUE); 259 queueEOS(); 260 waitForAllOutputs(); 261 /* TODO(b/147348711) */ 262 if (false) mCodec.stop(); 263 else mCodec.reset(); 264 assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError()); 265 assertTrue(log + "no input sent", 0 != mInputCount); 266 assertTrue(log + "no output received", 0 != mOutputCount); 267 if (!mIsAudio) { 268 assertTrue( 269 log + "input count != output count, act/exp: " + mOutputCount + 270 " / " + mInputCount, mInputCount == mOutputCount); 271 } 272 if (mIsAudio) { 273 assertTrue(log + " pts is not strictly increasing", 274 mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); 275 } else { 276 assertTrue( 277 log + " input pts list and output pts list are not identical", 278 mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 279 } 280 ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false); 281 assertFalse("no suitable codecs found for mime: " + mMime, 282 listOfDecoders.isEmpty()); 283 for (String decoder : listOfDecoders) { 284 ByteBuffer out = decodeElementaryStream(decoder, format, 285 mOutputBuff.getBuffer(), mInfoList); 286 if (!out.equals(ByteBuffer.wrap(mInputData))) { 287 fail(log + "identity test failed"); 288 } 289 } 290 } 291 mCodec.release(); 292 } 293 } 294 nativeTestSimpleEncode(String encoder, String file, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)295 private native boolean nativeTestSimpleEncode(String encoder, String file, String mime, 296 int[] list0, int[] list1, int[] list2, int colorFormat); 297 298 @LargeTest 299 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleEncodeNative()300 public void testSimpleEncodeNative() throws IOException { 301 int colorFormat = -1; 302 { 303 if (!mIsAudio) { 304 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 305 assertTrue("no valid color formats received", colorFormat != -1); 306 } 307 assertTrue(nativeTestSimpleEncode(mCodecName, mInpPrefix + mInputFile, mMime, mBitrates, 308 mEncParamList1, mEncParamList2, colorFormat)); 309 } 310 } 311 312 /** 313 * Tests flush when codec is in sync and async mode. In these scenarios, Timestamp 314 * ordering is verified. The output has to be consistent (not flaky) in all runs 315 */ 316 @Ignore("TODO(b/147576107, b/148652492, b/148651699)") 317 @LargeTest 318 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testFlush()319 public void testFlush() throws IOException, InterruptedException { 320 setUpParams(1); 321 setUpSource(mInputFile); 322 boolean[] boolStates = {true, false}; 323 mOutputBuff = new OutputManager(); 324 { 325 MediaFormat inpFormat = mFormats.get(0); 326 if (mIsAudio) { 327 mSampleRate = inpFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); 328 mChannels = inpFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); 329 } else { 330 mWidth = inpFormat.getInteger(MediaFormat.KEY_WIDTH); 331 mHeight = inpFormat.getInteger(MediaFormat.KEY_HEIGHT); 332 } 333 mCodec = MediaCodec.createByCodecName(mCodecName); 334 for (boolean isAsync : boolStates) { 335 String log = String.format("encoder: %s, input file: %s, mode: %s:: ", mCodecName, 336 mInputFile, (isAsync ? "async" : "sync")); 337 configureCodec(inpFormat, isAsync, true, true); 338 mCodec.start(); 339 340 /* test flush in running state before queuing input */ 341 flushCodec(); 342 mOutputBuff.reset(); 343 mInfoList.clear(); 344 if (mIsCodecInAsyncMode) mCodec.start(); 345 doWork(23); 346 assertTrue(log + " pts is not strictly increasing", 347 mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); 348 boolean checkMetrics = (mOutputCount != 0); 349 350 /* test flush in running state */ 351 flushCodec(); 352 mOutputBuff.reset(); 353 mInfoList.clear(); 354 if (mIsCodecInAsyncMode) mCodec.start(); 355 if (checkMetrics) validateMetrics(mCodecName, inpFormat); 356 doWork(Integer.MAX_VALUE); 357 queueEOS(); 358 waitForAllOutputs(); 359 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 360 assertTrue(log + "no input sent", 0 != mInputCount); 361 assertTrue(log + "output received", 0 != mOutputCount); 362 if (!mIsAudio) { 363 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + 364 " / " + mInputCount, mInputCount == mOutputCount); 365 assertTrue( 366 log + " input pts list and output pts list are not identical", 367 mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 368 } else { 369 assertTrue(log + " pts is not strictly increasing", 370 mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); 371 } 372 373 /* test flush in eos state */ 374 flushCodec(); 375 mOutputBuff.reset(); 376 mInfoList.clear(); 377 if (mIsCodecInAsyncMode) mCodec.start(); 378 doWork(Integer.MAX_VALUE); 379 queueEOS(); 380 waitForAllOutputs(); 381 /* TODO(b/147348711) */ 382 if (false) mCodec.stop(); 383 else mCodec.reset(); 384 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 385 assertTrue(log + "no input sent", 0 != mInputCount); 386 assertTrue(log + "output received", 0 != mOutputCount); 387 if (!mIsAudio) { 388 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + 389 " / " + mInputCount, mInputCount == mOutputCount); 390 assertTrue( 391 log + " input pts list and output pts list are not identical", 392 mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 393 } else { 394 assertTrue(log + " pts is not strictly increasing", 395 mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); 396 } 397 } 398 mCodec.release(); 399 } 400 } 401 nativeTestFlush(String encoder, String file, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)402 private native boolean nativeTestFlush(String encoder, String file, String mime, 403 int[] list0, int[] list1, int[] list2, int colorFormat); 404 405 @Ignore("TODO(b/147576107, b/148652492, b/148651699)") 406 @LargeTest 407 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testFlushNative()408 public void testFlushNative() throws IOException { 409 int colorFormat = -1; 410 { 411 if (!mIsAudio) { 412 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 413 assertTrue("no valid color formats received", colorFormat != -1); 414 } 415 assertTrue(nativeTestFlush(mCodecName, mInpPrefix + mInputFile, mMime, mBitrates, 416 mEncParamList1, mEncParamList2, colorFormat)); 417 } 418 } 419 420 /** 421 * Tests reconfigure when codec is in sync and async mode. In these 422 * scenarios, Timestamp ordering is verified. The output has to be consistent (not flaky) 423 * in all runs 424 */ 425 @Ignore("TODO(b/148523403)") 426 @LargeTest 427 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigure()428 public void testReconfigure() throws IOException, InterruptedException { 429 setUpParams(2); 430 setUpSource(mInputFile); 431 boolean[] boolStates = {true, false}; 432 OutputManager test = new OutputManager(); 433 { 434 boolean saveToMem = false; /* TODO(b/149027258) */ 435 OutputManager configRef = null; 436 if (mFormats.size() > 1) { 437 MediaFormat format = mFormats.get(1); 438 encodeToMemory(mInputFile, mCodecName, Integer.MAX_VALUE, format, saveToMem); 439 configRef = mOutputBuff; 440 if (mIsAudio) { 441 assertTrue("config reference output pts is not strictly increasing", 442 configRef.isPtsStrictlyIncreasing(mPrevOutputPts)); 443 } else { 444 assertTrue("input pts list and reconfig ref output pts list are not identical", 445 configRef.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 446 } 447 } 448 MediaFormat format = mFormats.get(0); 449 encodeToMemory(mInputFile, mCodecName, Integer.MAX_VALUE, format, saveToMem); 450 OutputManager ref = mOutputBuff; 451 if (mIsAudio) { 452 assertTrue("reference output pts is not strictly increasing", 453 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 454 } else { 455 assertTrue("input pts list and ref output pts list are not identical", 456 ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 457 } 458 mOutputBuff = test; 459 mCodec = MediaCodec.createByCodecName(mCodecName); 460 for (boolean isAsync : boolStates) { 461 String log = String.format("encoder: %s, input file: %s, mode: %s:: ", mCodecName, 462 mInputFile, (isAsync ? "async" : "sync")); 463 configureCodec(format, isAsync, true, true); 464 465 /* test reconfigure in stopped state */ 466 reConfigureCodec(format, !isAsync, false, true); 467 mCodec.start(); 468 469 /* test reconfigure in running state before queuing input */ 470 reConfigureCodec(format, !isAsync, false, true); 471 mCodec.start(); 472 doWork(23); 473 474 if (mOutputCount != 0) validateMetrics(mCodecName, format); 475 476 /* test reconfigure codec in running state */ 477 reConfigureCodec(format, isAsync, true, true); 478 mCodec.start(); 479 mSaveToMem = saveToMem; 480 test.reset(); 481 doWork(Integer.MAX_VALUE); 482 queueEOS(); 483 waitForAllOutputs(); 484 /* TODO(b/147348711) */ 485 if (false) mCodec.stop(); 486 else mCodec.reset(); 487 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 488 assertTrue(log + "no input sent", 0 != mInputCount); 489 assertTrue(log + "output received", 0 != mOutputCount); 490 if (!mIsAudio) { 491 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + 492 " / " + mInputCount, mInputCount == mOutputCount); 493 } 494 assertTrue(log + "encoder output is flaky", ref.equals(test)); 495 496 /* test reconfigure codec at eos state */ 497 reConfigureCodec(format, !isAsync, false, true); 498 mCodec.start(); 499 test.reset(); 500 doWork(Integer.MAX_VALUE); 501 queueEOS(); 502 waitForAllOutputs(); 503 /* TODO(b/147348711) */ 504 if (false) mCodec.stop(); 505 else mCodec.reset(); 506 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 507 assertTrue(log + "no input sent", 0 != mInputCount); 508 assertTrue(log + "output received", 0 != mOutputCount); 509 if (!mIsAudio) { 510 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + 511 " / " + mInputCount, mInputCount == mOutputCount); 512 } 513 assertTrue(log + "encoder output is flaky", ref.equals(test)); 514 515 /* test reconfigure codec for new format */ 516 if (mFormats.size() > 1) { 517 reConfigureCodec(mFormats.get(1), isAsync, false, true); 518 mCodec.start(); 519 test.reset(); 520 doWork(Integer.MAX_VALUE); 521 queueEOS(); 522 waitForAllOutputs(); 523 /* TODO(b/147348711) */ 524 if (false) mCodec.stop(); 525 else mCodec.reset(); 526 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 527 assertTrue(log + "no input sent", 0 != mInputCount); 528 assertTrue(log + "output received", 0 != mOutputCount); 529 if (!mIsAudio) { 530 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + 531 " / " + mInputCount, mInputCount == mOutputCount); 532 } 533 assertTrue(log + "encoder output is flaky", configRef.equals(test)); 534 } 535 mSaveToMem = false; 536 } 537 mCodec.release(); 538 } 539 } 540 nativeTestReconfigure(String encoder, String file, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)541 private native boolean nativeTestReconfigure(String encoder, String file, String mime, 542 int[] list0, int[] list1, int[] list2, int colorFormat); 543 544 @Ignore("TODO(b/147348711, b/149981033)") 545 @LargeTest 546 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigureNative()547 public void testReconfigureNative() throws IOException { 548 int colorFormat = -1; 549 { 550 if (!mIsAudio) { 551 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 552 assertTrue("no valid color formats received", colorFormat != -1); 553 } 554 assertTrue(nativeTestReconfigure(mCodecName, mInpPrefix + mInputFile, mMime, mBitrates, 555 mEncParamList1, mEncParamList2, colorFormat)); 556 } 557 } 558 559 /** 560 * Tests encoder for only EOS frame 561 */ 562 @SmallTest 563 @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) testOnlyEos()564 public void testOnlyEos() throws IOException, InterruptedException { 565 setUpParams(1); 566 boolean[] boolStates = {true, false}; 567 OutputManager ref = new OutputManager(); 568 OutputManager test = new OutputManager(); 569 { 570 mCodec = MediaCodec.createByCodecName(mCodecName); 571 /* TODO(b/149027258) */ 572 if (true) mSaveToMem = false; 573 else mSaveToMem = true; 574 int loopCounter = 0; 575 for (boolean isAsync : boolStates) { 576 String log = String.format("encoder: %s, input file: %s, mode: %s:: ", mCodecName, 577 mInputFile, (isAsync ? "async" : "sync")); 578 configureCodec(mFormats.get(0), isAsync, false, true); 579 mOutputBuff = loopCounter == 0 ? ref : test; 580 mOutputBuff.reset(); 581 mInfoList.clear(); 582 mCodec.start(); 583 queueEOS(); 584 waitForAllOutputs(); 585 /* TODO(b/147348711) */ 586 if (false) mCodec.stop(); 587 else mCodec.reset(); 588 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 589 if (loopCounter != 0) { 590 assertTrue(log + "encoder output is flaky", ref.equals(test)); 591 } else { 592 if (mIsAudio) { 593 assertTrue(log + " pts is not strictly increasing", 594 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 595 } else { 596 assertTrue( 597 log + " input pts list and output pts list are not identical", 598 ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 599 } 600 } 601 loopCounter++; 602 } 603 mCodec.release(); 604 } 605 } 606 nativeTestOnlyEos(String encoder, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)607 private native boolean nativeTestOnlyEos(String encoder, String mime, int[] list0, int[] list1, 608 int[] list2, int colorFormat); 609 610 @SmallTest 611 @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) testOnlyEosNative()612 public void testOnlyEosNative() throws IOException { 613 int colorFormat = -1; 614 { 615 if (!mIsAudio) { 616 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 617 assertTrue("no valid color formats received", colorFormat != -1); 618 } 619 assertTrue(nativeTestOnlyEos(mCodecName, mMime, mBitrates, mEncParamList1, 620 mEncParamList2, colorFormat)); 621 } 622 } 623 624 /** 625 * Test set parameters : force key frame 626 */ 627 @LargeTest 628 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSetForceSyncFrame()629 public void testSetForceSyncFrame() throws IOException, InterruptedException { 630 Assume.assumeTrue(!mIsAudio); 631 // Maximum allowed key frame interval variation from the target value. 632 final int MAX_KEYFRAME_INTERVAL_VARIATION = 3; 633 setUpParams(1); 634 boolean[] boolStates = {true, false}; 635 setUpSource(mInputFile); 636 MediaFormat format = mFormats.get(0); 637 format.removeKey(MediaFormat.KEY_I_FRAME_INTERVAL); 638 format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 500.f); 639 mWidth = format.getInteger(MediaFormat.KEY_WIDTH); 640 mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); 641 final int KEY_FRAME_INTERVAL = 2; // force key frame every 2 seconds. 642 final int KEY_FRAME_POS = mFrameRate * KEY_FRAME_INTERVAL; 643 final int NUM_KEY_FRAME_REQUESTS = 7; 644 mOutputBuff = new OutputManager(); 645 { 646 mCodec = MediaCodec.createByCodecName(mCodecName); 647 for (boolean isAsync : boolStates) { 648 String log = String.format( 649 "format: %s \n codec: %s, file: %s, mode: %s:: ", format, mCodecName, 650 mInputFile, (isAsync ? "async" : "sync")); 651 mOutputBuff.reset(); 652 mInfoList.clear(); 653 configureCodec(format, isAsync, false, true); 654 mCodec.start(); 655 for (int i = 0; i < NUM_KEY_FRAME_REQUESTS; i++) { 656 doWork(KEY_FRAME_POS); 657 assertTrue(!mSawInputEOS); 658 forceSyncFrame(); 659 mNumBytesSubmitted = 0; 660 } 661 queueEOS(); 662 waitForAllOutputs(); 663 /* TODO(b/147348711) */ 664 if (false) mCodec.stop(); 665 else mCodec.reset(); 666 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 667 assertTrue(log + "no input sent", 0 != mInputCount); 668 assertTrue(log + "output received", 0 != mOutputCount); 669 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " + 670 mInputCount, mInputCount == mOutputCount); 671 assertTrue(log + " input pts list and output pts list are not identical", 672 mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 673 assertTrue(log + "sync frames exp/act: " + NUM_KEY_FRAME_REQUESTS + " / " + 674 mNumSyncFramesReceived, mNumSyncFramesReceived >= NUM_KEY_FRAME_REQUESTS); 675 for (int i = 0, expPos = 0, index = 0; i < NUM_KEY_FRAME_REQUESTS; i++) { 676 int j = index; 677 for (; j < mSyncFramesPos.size(); j++) { 678 // Check key frame intervals: 679 // key frame position should not be greater than target value + 3 680 // key frame position should not be less than target value - 3 681 if (Math.abs(expPos - mSyncFramesPos.get(j)) <= 682 MAX_KEYFRAME_INTERVAL_VARIATION) { 683 index = j; 684 break; 685 } 686 } 687 if (j == mSyncFramesPos.size()) { 688 Log.w(LOG_TAG, "requested key frame at frame index " + expPos + 689 " none found near by"); 690 } 691 expPos += KEY_FRAME_POS; 692 } 693 } 694 mCodec.release(); 695 } 696 } 697 nativeTestSetForceSyncFrame(String encoder, String file, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)698 private native boolean nativeTestSetForceSyncFrame(String encoder, String file, String mime, 699 int[] list0, int[] list1, int[] list2, int colorFormat); 700 701 @Ignore("TODO(b/) = test sometimes timesout") 702 @LargeTest 703 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSetForceSyncFrameNative()704 public void testSetForceSyncFrameNative() throws IOException { 705 Assume.assumeTrue(!mIsAudio); 706 int colorFormat = -1; 707 { 708 if (!mIsAudio) { 709 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 710 assertTrue("no valid color formats received", colorFormat != -1); 711 } 712 assertTrue(nativeTestSetForceSyncFrame(mCodecName, mInpPrefix + mInputFile, mMime, 713 mBitrates, mEncParamList1, mEncParamList2, colorFormat)); 714 } 715 } 716 717 /** 718 * Test set parameters : change bitrate dynamically 719 */ 720 @LargeTest 721 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testAdaptiveBitRate()722 public void testAdaptiveBitRate() throws IOException, InterruptedException { 723 Assume.assumeTrue(!mIsAudio); 724 setUpParams(1); 725 boolean[] boolStates = {true, false}; 726 setUpSource(mInputFile); 727 MediaFormat format = mFormats.get(0); 728 mWidth = format.getInteger(MediaFormat.KEY_WIDTH); 729 mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); 730 final int ADAPTIVE_BR_INTERVAL = 3; // change br every 3 seconds. 731 final int ADAPTIVE_BR_DUR_FRM = mFrameRate * ADAPTIVE_BR_INTERVAL; 732 final int BR_CHANGE_REQUESTS = 7; 733 mOutputBuff = new OutputManager(); 734 mSaveToMem = true; 735 { 736 /* TODO(b/147574800) */ 737 if (mCodecName.equals("c2.android.hevc.encoder")) return; 738 mCodec = MediaCodec.createByCodecName(mCodecName); 739 format.removeKey(MediaFormat.KEY_BITRATE_MODE); 740 MediaCodecInfo.EncoderCapabilities cap = 741 mCodec.getCodecInfo().getCapabilitiesForType(mMime).getEncoderCapabilities(); 742 if (cap.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) { 743 format.setInteger(MediaFormat.KEY_BITRATE_MODE, 744 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR); 745 } else { 746 format.setInteger(MediaFormat.KEY_BITRATE_MODE, 747 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR); 748 } 749 for (boolean isAsync : boolStates) { 750 String log = String.format( 751 "format: %s \n codec: %s, file: %s, mode: %s:: ", format, mCodecName, 752 mInputFile, (isAsync ? "async" : "sync")); 753 mOutputBuff.reset(); 754 mInfoList.clear(); 755 configureCodec(format, isAsync, false, true); 756 mCodec.start(); 757 int expOutSize = 0; 758 int bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE); 759 for (int i = 0; i < BR_CHANGE_REQUESTS; i++) { 760 doWork(ADAPTIVE_BR_DUR_FRM); 761 assertTrue(!mSawInputEOS); 762 expOutSize += ADAPTIVE_BR_INTERVAL * bitrate; 763 if ((i & 1) == 1) bitrate *= 2; 764 else bitrate /= 2; 765 updateBitrate(bitrate); 766 mNumBytesSubmitted = 0; 767 } 768 queueEOS(); 769 waitForAllOutputs(); 770 /* TODO(b/147348711) */ 771 if (false) mCodec.stop(); 772 else mCodec.reset(); 773 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 774 assertTrue(log + "no input sent", 0 != mInputCount); 775 assertTrue(log + "output received", 0 != mOutputCount); 776 assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " + 777 mInputCount, mInputCount == mOutputCount); 778 assertTrue(log + " input pts list and output pts list are not identical", 779 mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); 780 /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */ 781 int outSize = mOutputBuff.getOutStreamSize() * 8; 782 float brDev = Math.abs(expOutSize - outSize) * 100.0f / expOutSize; 783 if (ENABLE_LOGS) { 784 Log.d(LOG_TAG, log + "relative br error is " + brDev + '%'); 785 } 786 if (brDev > 50) { 787 fail(log + "relative br error is too large " + brDev + '%'); 788 } 789 } 790 mCodec.release(); 791 } 792 } 793 nativeTestAdaptiveBitRate(String encoder, String file, String mime, int[] list0, int[] list1, int[] list2, int colorFormat)794 private native boolean nativeTestAdaptiveBitRate(String encoder, String file, String mime, 795 int[] list0, int[] list1, int[] list2, int colorFormat); 796 797 @Ignore("TODO(b/) = test sometimes timesout") 798 @LargeTest 799 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testAdaptiveBitRateNative()800 public void testAdaptiveBitRateNative() throws IOException { 801 Assume.assumeTrue(!mIsAudio); 802 int colorFormat = -1; 803 { 804 /* TODO(b/147574800) */ 805 if (mCodecName.equals("c2.android.hevc.encoder")) return; 806 if (!mIsAudio) { 807 colorFormat = findByteBufferColorFormat(mCodecName, mMime); 808 assertTrue("no valid color formats received", colorFormat != -1); 809 } 810 assertTrue(nativeTestAdaptiveBitRate(mCodecName, mInpPrefix + mInputFile, mMime, 811 mBitrates, mEncParamList1, mEncParamList2, colorFormat)); 812 } 813 } 814 } 815