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