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