1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.mediav2.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.media.MediaCodec; 26 import android.media.MediaFormat; 27 import android.mediav2.common.cts.CodecEncoderTestBase; 28 import android.mediav2.common.cts.EncoderConfigParams; 29 import android.mediav2.common.cts.OutputManager; 30 import android.os.Bundle; 31 import android.util.Log; 32 33 import androidx.test.filters.LargeTest; 34 import androidx.test.filters.SmallTest; 35 36 import com.android.compatibility.common.util.ApiTest; 37 import com.android.compatibility.common.util.CddTest; 38 39 import org.junit.Assume; 40 import org.junit.Before; 41 import org.junit.Ignore; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.junit.runners.Parameterized; 45 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Collection; 50 import java.util.List; 51 52 /** 53 * Test mediacodec api, encoders and their interactions in bytebuffer mode. 54 * <p> 55 * The test feeds raw input data (audio/video) to the component and receives compressed bitstream 56 * from the component. 57 * <p> 58 * At the end of encoding process, the test enforces following checks :- 59 * <ul> 60 * <li> For audio components, the test expects the output timestamps to be strictly 61 * increasing.</li> 62 * <li>For video components the test expects the output frame count to be identical to input 63 * frame count and the output timestamp list to be identical to input timestamp list.</li> 64 * <li>As encoders are expected to give consistent output for a given input and configuration 65 * parameters, the test checks for consistency across runs. For now, this attribute is not 66 * strictly enforced in this test.</li> 67 * </ul> 68 * <p> 69 * The test does not validate the integrity of the encoder output. That is done by 70 * CodecEncoderValidationTest. This test checks only the framework <-> plugin <-> encoder 71 * interactions. 72 * <p> 73 * The test runs mediacodec in synchronous and asynchronous mode. 74 */ 75 @RunWith(Parameterized.class) 76 public class CodecEncoderTest extends CodecEncoderTestBase { 77 private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName(); 78 private static final ArrayList<String> ABR_MEDIATYPE_LIST = new ArrayList<>(); 79 80 private boolean mGotCSD; 81 private int mNumSyncFramesReceived; 82 private final ArrayList<Integer> mSyncFramesPos = new ArrayList<>(); 83 84 static { 85 System.loadLibrary("ctsmediav2codecenc_jni"); 86 87 ABR_MEDIATYPE_LIST.add(MediaFormat.MIMETYPE_VIDEO_AVC); 88 ABR_MEDIATYPE_LIST.add(MediaFormat.MIMETYPE_VIDEO_HEVC); 89 ABR_MEDIATYPE_LIST.add(MediaFormat.MIMETYPE_VIDEO_VP8); 90 ABR_MEDIATYPE_LIST.add(MediaFormat.MIMETYPE_VIDEO_VP9); 91 ABR_MEDIATYPE_LIST.add(MediaFormat.MIMETYPE_VIDEO_AV1); 92 } 93 CodecEncoderTest(String encoder, String mediaType, EncoderConfigParams[] cfgParams, String allTestParams)94 public CodecEncoderTest(String encoder, String mediaType, EncoderConfigParams[] cfgParams, 95 String allTestParams) { 96 super(encoder, mediaType, cfgParams, allTestParams); 97 } 98 99 @Override resetContext(boolean isAsync, boolean signalEOSWithLastFrame)100 protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { 101 super.resetContext(isAsync, signalEOSWithLastFrame); 102 mGotCSD = false; 103 mNumSyncFramesReceived = 0; 104 mSyncFramesPos.clear(); 105 } 106 107 @Override dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)108 protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { 109 if (info.size > 0 && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0)) { 110 mGotCSD = true; 111 } 112 if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { 113 mNumSyncFramesReceived += 1; 114 mSyncFramesPos.add(mOutputCount); 115 } 116 super.dequeueOutput(bufferIndex, info); 117 } 118 forceSyncFrame()119 private void forceSyncFrame() { 120 final Bundle syncFrame = new Bundle(); 121 syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); 122 if (ENABLE_LOGS) { 123 Log.v(LOG_TAG, "requesting key frame"); 124 } 125 mCodec.setParameters(syncFrame); 126 } 127 updateBitrate(int bitrate)128 private void updateBitrate(int bitrate) { 129 final Bundle bitrateUpdate = new Bundle(); 130 bitrateUpdate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitrate); 131 if (ENABLE_LOGS) { 132 Log.v(LOG_TAG, "requesting bitrate to be changed to " + bitrate); 133 } 134 mCodec.setParameters(bitrateUpdate); 135 } 136 getVideoEncoderCfgParam(String mediaType, int width, int height, int bitRate, int maxBFrames)137 private static EncoderConfigParams getVideoEncoderCfgParam(String mediaType, int width, 138 int height, int bitRate, int maxBFrames) { 139 return new EncoderConfigParams.Builder(mediaType).setWidth(width).setHeight(height) 140 .setMaxBFrames(maxBFrames).setBitRate(bitRate).build(); 141 } 142 getAudioEncoderCfgParam(String mediaType, int sampleRate, int channelCount, int qualityPreset)143 private static EncoderConfigParams getAudioEncoderCfgParam(String mediaType, int sampleRate, 144 int channelCount, int qualityPreset) { 145 EncoderConfigParams.Builder foreman = 146 new EncoderConfigParams.Builder(mediaType).setSampleRate(sampleRate) 147 .setChannelCount(channelCount); 148 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) { 149 foreman = foreman.setCompressionLevel(qualityPreset); 150 } else { 151 foreman = foreman.setBitRate(qualityPreset); 152 } 153 return foreman.build(); 154 } 155 getAacCfgParams()156 private static EncoderConfigParams[] getAacCfgParams() { 157 EncoderConfigParams[] params = new EncoderConfigParams[2]; 158 params[0] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1, 128000); 159 params[1] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 2, 128000); 160 return params; 161 } 162 getOpusCfgParams()163 private static EncoderConfigParams[] getOpusCfgParams() { 164 EncoderConfigParams[] params = new EncoderConfigParams[2]; 165 params[0] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_OPUS, 16000, 1, 64000); 166 params[1] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_OPUS, 16000, 1, 128000); 167 return params; 168 } 169 getAmrnbCfgParams()170 private static EncoderConfigParams[] getAmrnbCfgParams() { 171 EncoderConfigParams[] params = new EncoderConfigParams[2]; 172 params[0] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AMR_NB, 8000, 1, 4750); 173 params[1] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AMR_NB, 8000, 1, 12200); 174 return params; 175 } 176 getAmrwbCfgParams()177 private static EncoderConfigParams[] getAmrwbCfgParams() { 178 EncoderConfigParams[] params = new EncoderConfigParams[2]; 179 params[0] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AMR_WB, 16000, 1, 6600); 180 params[1] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_AMR_WB, 16000, 1, 23850); 181 return params; 182 } 183 getFlacCfgParams()184 private static EncoderConfigParams[] getFlacCfgParams() { 185 EncoderConfigParams[] params = new EncoderConfigParams[2]; 186 params[0] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_FLAC, 8000, 1, 6); 187 params[1] = getAudioEncoderCfgParam(MediaFormat.MIMETYPE_AUDIO_FLAC, 48000, 2, 5); 188 return params; 189 } 190 getH263CfgParams()191 private static EncoderConfigParams[] getH263CfgParams() { 192 EncoderConfigParams[] params = new EncoderConfigParams[2]; 193 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_H263, 176, 144, 32000, 0); 194 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_H263, 176, 144, 64000, 0); 195 return params; 196 } 197 getMpeg4CfgParams()198 private static EncoderConfigParams[] getMpeg4CfgParams() { 199 EncoderConfigParams[] params = new EncoderConfigParams[2]; 200 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_MPEG4, 176, 144, 32000, 0); 201 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_MPEG4, 176, 144, 64000, 0); 202 return params; 203 } 204 getAvcCfgParams()205 private static EncoderConfigParams[] getAvcCfgParams() { 206 EncoderConfigParams[] params = new EncoderConfigParams[2]; 207 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AVC, 176, 144, 512000, 0); 208 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AVC, 352, 288, 512000, 0); 209 return params; 210 } 211 getHevcCfgParams()212 private static EncoderConfigParams[] getHevcCfgParams() { 213 EncoderConfigParams[] params = new EncoderConfigParams[2]; 214 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_HEVC, 176, 144, 512000, 0); 215 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_HEVC, 352, 288, 512000, 0); 216 return params; 217 } 218 getAvcCfgParamsWithBFrames()219 private static EncoderConfigParams[] getAvcCfgParamsWithBFrames() { 220 EncoderConfigParams[] params = new EncoderConfigParams[2]; 221 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AVC, 320, 240, 512000, 2); 222 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 360, 768000, 2); 223 return params; 224 } 225 getHevcCfgParamsWithBFrames()226 private static EncoderConfigParams[] getHevcCfgParamsWithBFrames() { 227 EncoderConfigParams[] params = new EncoderConfigParams[2]; 228 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_HEVC, 320, 240, 384000, 2); 229 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_HEVC, 480, 360, 512000, 2); 230 return params; 231 } 232 getVp8CfgParams()233 private static EncoderConfigParams[] getVp8CfgParams() { 234 EncoderConfigParams[] params = new EncoderConfigParams[2]; 235 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_VP8, 176, 144, 512000, 0); 236 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_VP8, 352, 288, 512000, 0); 237 return params; 238 } 239 getVp9CfgParams()240 private static EncoderConfigParams[] getVp9CfgParams() { 241 EncoderConfigParams[] params = new EncoderConfigParams[2]; 242 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_VP9, 176, 144, 512000, 0); 243 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_VP9, 352, 288, 512000, 0); 244 return params; 245 } 246 getAv1CfgParams()247 private static EncoderConfigParams[] getAv1CfgParams() { 248 EncoderConfigParams[] params = new EncoderConfigParams[2]; 249 params[0] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AV1, 176, 144, 512000, 0); 250 params[1] = getVideoEncoderCfgParam(MediaFormat.MIMETYPE_VIDEO_AV1, 352, 288, 512000, 0); 251 return params; 252 } 253 254 @Parameterized.Parameters(name = "{index}_{0}_{1}") input()255 public static Collection<Object[]> input() { 256 final boolean isEncoder = true; 257 final boolean needAudio = true; 258 final boolean needVideo = true; 259 final List<Object[]> exhaustiveArgsList = new ArrayList<>(Arrays.asList(new Object[][]{ 260 // mediaType, cfg params 261 {MediaFormat.MIMETYPE_AUDIO_AAC, getAacCfgParams()}, 262 {MediaFormat.MIMETYPE_AUDIO_OPUS, getOpusCfgParams()}, 263 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, getAmrnbCfgParams()}, 264 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, getAmrwbCfgParams()}, 265 {MediaFormat.MIMETYPE_AUDIO_FLAC, getFlacCfgParams()}, 266 {MediaFormat.MIMETYPE_VIDEO_H263, getH263CfgParams()}, 267 {MediaFormat.MIMETYPE_VIDEO_MPEG4, getMpeg4CfgParams()}, 268 {MediaFormat.MIMETYPE_VIDEO_AVC, getAvcCfgParams()}, 269 {MediaFormat.MIMETYPE_VIDEO_AVC, getAvcCfgParamsWithBFrames()}, 270 {MediaFormat.MIMETYPE_VIDEO_HEVC, getHevcCfgParams()}, 271 {MediaFormat.MIMETYPE_VIDEO_HEVC, getHevcCfgParamsWithBFrames()}, 272 {MediaFormat.MIMETYPE_VIDEO_VP8, getVp8CfgParams()}, 273 {MediaFormat.MIMETYPE_VIDEO_VP9, getVp9CfgParams()}, 274 {MediaFormat.MIMETYPE_VIDEO_AV1, getAv1CfgParams()}, 275 })); 276 return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, true); 277 } 278 279 @Before setUp()280 public void setUp() throws IOException { 281 mActiveEncCfg = mEncCfgParams[0]; 282 mActiveRawRes = EncoderInput.getRawResource(mActiveEncCfg); 283 assertNotNull("no raw resource found for testing config : " + mActiveEncCfg + mTestConfig 284 + mTestEnv, mActiveRawRes); 285 } 286 validateCSD()287 private void validateCSD() { 288 boolean requireCSD = false; 289 if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { 290 if (!IS_AT_LEAST_V) { 291 assertFalse("components that support mediaType: " + mMediaType 292 + " must not generate CodecPrivateData before Android V\n" 293 + mTestConfig + mTestEnv, mGotCSD); 294 } else if (BOARD_FIRST_SDK_IS_AT_LEAST_202404) { 295 // For devices launching with Android V, CSD is mandated for VP9 encoders 296 requireCSD = true; 297 } else { 298 // For devices upgrading to Android V, CSD is not mandated for VP9 encoders 299 requireCSD = false; 300 } 301 } else if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC) 302 || mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_OPUS) 303 || mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC) 304 || mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4) 305 || mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_AVC) 306 || mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) { 307 requireCSD = true; 308 } 309 310 if (requireCSD) { 311 assertTrue("components that support mediaType: " + mMediaType 312 + " must generate CodecPrivateData \n" + mTestConfig + mTestEnv, mGotCSD); 313 } 314 } 315 316 /** 317 * Checks if the component under test can encode the test file correctly. The encoding 318 * happens in synchronous, asynchronous mode, eos flag signalled with last raw frame and 319 * eos flag signalled separately after sending all raw frames. It expects consistent 320 * output in all these runs. That is, the ByteBuffer info and output timestamp list has to be 321 * same in all the runs. Further for audio, the output timestamp has to be strictly 322 * increasing. For video the output timestamp list has to be same as input timestamp list. As 323 * encoders are expected to give consistent output for a given input and configuration 324 * parameters, the test checks for consistency across runs. Although the test collects the 325 * output in a byte buffer, no analysis is done that checks the integrity of the bitstream. 326 */ 327 @CddTest(requirements = {"2.2.2", "2.3.2", "2.5.2", "5.1.1", "5.2/C-1-1", "5.2.4/C-1-3"}) 328 @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatYUV420Flexible", 329 "android.media.AudioFormat#ENCODING_PCM_16BIT"}) 330 @LargeTest 331 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleEncode()332 public void testSimpleEncode() throws IOException, InterruptedException { 333 boolean[] boolStates = {true, false}; 334 setUpSource(mActiveRawRes.mFileName); 335 OutputManager ref = new OutputManager(); 336 OutputManager test = new OutputManager(ref.getSharedErrorLogs()); 337 { 338 mCodec = MediaCodec.createByCodecName(mCodecName); 339 assertEquals("codec name act/got: " + mCodec.getName() + '/' + mCodecName, 340 mCodec.getName(), mCodecName); 341 assertTrue("error! codec canonical name is null or empty", 342 mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty()); 343 mSaveToMem = false; /* TODO(b/149027258) */ 344 MediaFormat format = mActiveEncCfg.getFormat(); 345 { 346 int loopCounter = 0; 347 for (boolean eosType : boolStates) { 348 for (boolean isAsync : boolStates) { 349 mOutputBuff = loopCounter == 0 ? ref : test; 350 mOutputBuff.reset(); 351 mInfoList.clear(); 352 validateMetrics(mCodecName); 353 configureCodec(format, isAsync, eosType, true); 354 mCodec.start(); 355 doWork(Integer.MAX_VALUE); 356 queueEOS(); 357 waitForAllOutputs(); 358 validateMetrics(mCodecName, format); 359 validateCSD(); 360 /* TODO(b/147348711) */ 361 if (false) mCodec.stop(); 362 else mCodec.reset(); 363 if (loopCounter != 0 && !ref.equals(test)) { 364 fail("Encoder output is not consistent across runs \n" + mTestConfig 365 + mTestEnv + test.getErrMsg()); 366 } 367 loopCounter++; 368 } 369 } 370 } 371 mCodec.release(); 372 } 373 } 374 nativeTestSimpleEncode(String encoder, String file, String mediaType, String cfgParams, String separator, StringBuilder retMsg)375 private native boolean nativeTestSimpleEncode(String encoder, String file, String mediaType, 376 String cfgParams, String separator, StringBuilder retMsg); 377 378 /** 379 * Test is similar to {@link #testSimpleEncode()} but uses ndk api 380 */ 381 @CddTest(requirements = {"2.2.2", "2.3.2", "2.5.2", "5.1.1", "5.1.7/C-1-3"}) 382 @ApiTest(apis = {"android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatYUV420SemiPlanar", 383 "android.media.MediaCodecInfo.CodecCapabilities#COLOR_FormatYUV420Planar", 384 "android.media.AudioFormat#ENCODING_PCM_16BIT"}) 385 @LargeTest 386 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleEncodeNative()387 public void testSimpleEncodeNative() throws IOException, CloneNotSupportedException { 388 MediaFormat format = mActiveEncCfg.getFormat(); 389 if (mIsVideo) { 390 int colorFormat = findByteBufferColorFormat(mCodecName, mMediaType); 391 assertTrue("no valid color formats received \n" + mTestConfig + mTestEnv, 392 colorFormat != -1); 393 format = mActiveEncCfg.getBuilder().setColorFormat(colorFormat).build().getFormat(); 394 } 395 boolean isPass = nativeTestSimpleEncode(mCodecName, mActiveRawRes.mFileName, mMediaType, 396 EncoderConfigParams.serializeMediaFormat(format), 397 EncoderConfigParams.TOKEN_SEPARATOR, mTestConfig); 398 assertTrue(mTestConfig.toString(), isPass); 399 } 400 401 /** 402 * Checks component and framework behaviour on parameter (resolution, samplerate/channel 403 * count, ...) change. The reconfiguring of media codec component happens at various points. 404 * <ul> 405 * <li>After initial configuration (stopped state).</li> 406 * <li>In running state, before queueing any input.</li> 407 * <li>In running state, after queueing n frames.</li> 408 * <li>In eos state.</li> 409 * </ul> 410 * In eos state, 411 * <ul> 412 * <li>reconfigure with same clip.</li> 413 * <li>reconfigure with different clip (different resolution).</li> 414 * </ul> 415 * <p> 416 * In all situations (pre-reconfigure or post-reconfigure), the test expects the output 417 * timestamps to be strictly increasing. The reconfigure call makes the output received 418 * non-deterministic even for a given input. Hence, besides timestamp checks, no additional 419 * validation is done for outputs received before reconfigure. Post reconfigure, the encode 420 * begins from a sync frame. So the test expects consistent output and this needs to be 421 * identical to the reference. 422 * <p> 423 * The test runs mediacodec in synchronous and asynchronous mode. 424 * <p> 425 * During reconfiguration, the mode of operation is toggled. That is, if first configure 426 * operates the codec in sync mode, then next configure operates the codec in async mode and 427 * so on. 428 */ 429 @Ignore("TODO(b/148523403)") 430 @ApiTest(apis = {"android.media.MediaCodec#configure"}) 431 @LargeTest 432 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigure()433 public void testReconfigure() throws IOException, InterruptedException { 434 435 boolean[] boolStates = {true, false}; 436 { 437 boolean saveToMem = false; /* TODO(b/149027258) */ 438 OutputManager configRef = null; 439 OutputManager configTest = null; 440 if (mEncCfgParams.length > 1) { 441 encodeToMemory(mCodecName, mEncCfgParams[1], mActiveRawRes, Integer.MAX_VALUE, 442 saveToMem, mMuxOutput); 443 configRef = mOutputBuff; 444 configTest = new OutputManager(configRef.getSharedErrorLogs()); 445 } 446 encodeToMemory(mCodecName, mEncCfgParams[0], mActiveRawRes, Integer.MAX_VALUE, 447 saveToMem, mMuxOutput); 448 OutputManager ref = mOutputBuff; 449 OutputManager test = new OutputManager(ref.getSharedErrorLogs()); 450 MediaFormat format = mEncCfgParams[0].getFormat(); 451 mCodec = MediaCodec.createByCodecName(mCodecName); 452 for (boolean isAsync : boolStates) { 453 mOutputBuff = test; 454 configureCodec(format, isAsync, true, true); 455 456 /* test reconfigure in stopped state */ 457 reConfigureCodec(format, !isAsync, false, true); 458 mCodec.start(); 459 460 /* test reconfigure in running state before queuing input */ 461 reConfigureCodec(format, !isAsync, false, true); 462 mCodec.start(); 463 doWork(23); 464 465 if (mOutputCount != 0) validateMetrics(mCodecName, format); 466 467 /* test reconfigure codec in running state */ 468 reConfigureCodec(format, isAsync, true, true); 469 mCodec.start(); 470 mSaveToMem = saveToMem; 471 test.reset(); 472 doWork(Integer.MAX_VALUE); 473 queueEOS(); 474 waitForAllOutputs(); 475 /* TODO(b/147348711) */ 476 if (false) mCodec.stop(); 477 else mCodec.reset(); 478 if (!ref.equals(test)) { 479 fail("Encoder output is not consistent across runs \n" + mTestConfig 480 + mTestEnv + test.getErrMsg()); 481 } 482 483 /* test reconfigure codec at eos state */ 484 reConfigureCodec(format, !isAsync, false, true); 485 mCodec.start(); 486 test.reset(); 487 doWork(Integer.MAX_VALUE); 488 queueEOS(); 489 waitForAllOutputs(); 490 /* TODO(b/147348711) */ 491 if (false) mCodec.stop(); 492 else mCodec.reset(); 493 if (!ref.equals(test)) { 494 fail("Encoder output is not consistent across runs \n" + mTestConfig 495 + mTestEnv + test.getErrMsg()); 496 } 497 498 /* test reconfigure codec for new format */ 499 if (mEncCfgParams.length > 1) { 500 mOutputBuff = configTest; 501 reConfigureCodec(mEncCfgParams[1].getFormat(), isAsync, false, true); 502 mCodec.start(); 503 configTest.reset(); 504 doWork(Integer.MAX_VALUE); 505 queueEOS(); 506 waitForAllOutputs(); 507 /* TODO(b/147348711) */ 508 if (false) mCodec.stop(); 509 else mCodec.reset(); 510 if (!configRef.equals(configTest)) { 511 fail("Encoder output is not consistent across runs \n" + mTestConfig 512 + mTestEnv + configTest.getErrMsg()); 513 } 514 } 515 mSaveToMem = false; 516 } 517 mCodec.release(); 518 } 519 } 520 nativeTestReconfigure(String encoder, String file, String mediaType, String cfgParams, String cfgReconfigParams, String separator, StringBuilder retMsg)521 private native boolean nativeTestReconfigure(String encoder, String file, String mediaType, 522 String cfgParams, String cfgReconfigParams, String separator, StringBuilder retMsg); 523 524 /** 525 * Test is similar to {@link #testReconfigure()} but uses ndk api 526 */ 527 @Ignore("TODO(b/147348711, b/149981033)") 528 @ApiTest(apis = {"android.media.MediaCodec#configure"}) 529 @LargeTest 530 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigureNative()531 public void testReconfigureNative() throws IOException, CloneNotSupportedException { 532 MediaFormat format = mEncCfgParams[0].getFormat(); 533 MediaFormat reconfigFormat = mEncCfgParams.length > 1 ? mEncCfgParams[1].getFormat() : null; 534 if (mIsVideo) { 535 int colorFormat = findByteBufferColorFormat(mCodecName, mMediaType); 536 assertTrue("no valid color formats received \n" + mTestConfig + mTestEnv, 537 colorFormat != -1); 538 format = mEncCfgParams[0].getBuilder().setColorFormat(colorFormat).build().getFormat(); 539 if (mEncCfgParams.length > 1) { 540 reconfigFormat = mEncCfgParams[1].getBuilder().setColorFormat(colorFormat).build() 541 .getFormat(); 542 } 543 } 544 boolean isPass = nativeTestReconfigure(mCodecName, mActiveRawRes.mFileName, mMediaType, 545 EncoderConfigParams.serializeMediaFormat(format), reconfigFormat == null ? null : 546 EncoderConfigParams.serializeMediaFormat(reconfigFormat), 547 EncoderConfigParams.TOKEN_SEPARATOR, mTestConfig); 548 assertTrue(mTestConfig.toString(), isPass); 549 } 550 551 /** 552 * Test encoder for EOS only input. As BUFFER_FLAG_END_OF_STREAM is queued with an input buffer 553 * of size 0, during dequeue the test expects to receive BUFFER_FLAG_END_OF_STREAM with an 554 * output buffer of size 0. No input is given, so no output shall be received. 555 */ 556 @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_END_OF_STREAM") 557 @SmallTest 558 @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) testOnlyEos()559 public void testOnlyEos() throws IOException, InterruptedException { 560 boolean[] boolStates = {true, false}; 561 OutputManager ref = new OutputManager(); 562 OutputManager test = new OutputManager(ref.getSharedErrorLogs()); 563 { 564 mCodec = MediaCodec.createByCodecName(mCodecName); 565 mSaveToMem = false; /* TODO(b/149027258) */ 566 int loopCounter = 0; 567 MediaFormat format = mActiveEncCfg.getFormat(); 568 for (boolean isAsync : boolStates) { 569 configureCodec(format, isAsync, false, true); 570 mOutputBuff = loopCounter == 0 ? ref : test; 571 mOutputBuff.reset(); 572 mInfoList.clear(); 573 mCodec.start(); 574 queueEOS(); 575 waitForAllOutputs(); 576 /* TODO(b/147348711) */ 577 if (false) mCodec.stop(); 578 else mCodec.reset(); 579 if (loopCounter != 0 && !ref.equals(test)) { 580 fail("Encoder output is not consistent across runs \n" + mTestConfig 581 + mTestEnv + test.getErrMsg()); 582 } 583 loopCounter++; 584 } 585 mCodec.release(); 586 } 587 } 588 nativeTestOnlyEos(String encoder, String mediaType, String cfgParams, String separator, StringBuilder retMsg)589 private native boolean nativeTestOnlyEos(String encoder, String mediaType, String cfgParams, 590 String separator, StringBuilder retMsg); 591 592 /** 593 * Test is similar to {@link #testOnlyEos()} but uses ndk api 594 */ 595 @ApiTest(apis = "android.media.MediaCodec#BUFFER_FLAG_END_OF_STREAM") 596 @SmallTest 597 @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) testOnlyEosNative()598 public void testOnlyEosNative() throws IOException, CloneNotSupportedException { 599 MediaFormat format = mActiveEncCfg.getFormat(); 600 if (mIsVideo) { 601 int colorFormat = findByteBufferColorFormat(mCodecName, mMediaType); 602 assertTrue("no valid color formats received \n" + mTestConfig + mTestEnv, 603 colorFormat != -1); 604 format = mActiveEncCfg.getBuilder().setColorFormat(colorFormat).build().getFormat(); 605 } 606 boolean isPass = nativeTestOnlyEos(mCodecName, mMediaType, 607 EncoderConfigParams.serializeMediaFormat(format), 608 EncoderConfigParams.TOKEN_SEPARATOR, mTestConfig); 609 assertTrue(mTestConfig.toString(), isPass); 610 } 611 612 /** 613 * Test video encoders for feature "request-sync". Video encoders are expected to give a sync 614 * frame upon request. The test requests encoder to provide key frame every 'n' seconds. The 615 * test feeds encoder input for 'm' seconds. At the end, it expects to receive m/n key frames 616 * at least. Also it checks if the key frame received is not too far from the point of request. 617 */ 618 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_REQUEST_SYNC_FRAME") 619 @LargeTest 620 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSetForceSyncFrame()621 public void testSetForceSyncFrame() 622 throws IOException, InterruptedException, CloneNotSupportedException { 623 Assume.assumeTrue("Test is applicable only for video encoders", mIsVideo); 624 EncoderConfigParams currCfg = mActiveEncCfg.getBuilder().setKeyFrameInterval(500.f).build(); 625 MediaFormat format = currCfg.getFormat(); 626 // Maximum allowed key frame interval variation from the target value. 627 final int maxKeyframeIntervalVariation = 3; 628 final int keyFrameInterval = 2; // force key frame every 2 seconds. 629 final int keyFramePos = currCfg.mFrameRate * keyFrameInterval; 630 final int numKeyFrameRequests = 7; 631 632 setUpSource(mActiveRawRes.mFileName); 633 mOutputBuff = new OutputManager(); 634 boolean[] boolStates = {true, false}; 635 { 636 mCodec = MediaCodec.createByCodecName(mCodecName); 637 for (boolean isAsync : boolStates) { 638 mOutputBuff.reset(); 639 mInfoList.clear(); 640 configureCodec(format, isAsync, false, true); 641 mCodec.start(); 642 for (int i = 0; i < numKeyFrameRequests; i++) { 643 doWork(keyFramePos); 644 if (mSawInputEOS) { 645 fail(String.format("Unable to encode %d frames as the input resource " 646 + "contains only %d frames \n", keyFramePos, mInputCount)); 647 } 648 forceSyncFrame(); 649 mInputBufferReadOffset = 0; 650 } 651 queueEOS(); 652 waitForAllOutputs(); 653 /* TODO(b/147348711) */ 654 if (false) mCodec.stop(); 655 else mCodec.reset(); 656 String msg = String.format("Received only %d key frames for %d key frame " 657 + "requests \n", mNumSyncFramesReceived, numKeyFrameRequests); 658 assertTrue(msg + mTestConfig + mTestEnv, 659 mNumSyncFramesReceived >= numKeyFrameRequests); 660 for (int i = 0, expPos = 0, index = 0; i < numKeyFrameRequests; i++) { 661 int j = index; 662 for (; j < mSyncFramesPos.size(); j++) { 663 // Check key frame intervals: 664 // key frame position should not be greater than target value + 3 665 // key frame position should not be less than target value - 3 666 if (Math.abs(expPos - mSyncFramesPos.get(j)) <= 667 maxKeyframeIntervalVariation) { 668 index = j; 669 break; 670 } 671 } 672 if (j == mSyncFramesPos.size()) { 673 Log.w(LOG_TAG, "requested key frame at frame index " + expPos + 674 " none found near by"); 675 } 676 expPos += keyFramePos; 677 } 678 } 679 mCodec.release(); 680 } 681 } 682 nativeTestSetForceSyncFrame(String encoder, String file, String mediaType, String cfgParams, String separator, StringBuilder retMsg)683 private native boolean nativeTestSetForceSyncFrame(String encoder, String file, 684 String mediaType, String cfgParams, String separator, StringBuilder retMsg); 685 686 /** 687 * Test is similar to {@link #testSetForceSyncFrame()} but uses ndk api 688 */ 689 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_REQUEST_SYNC_FRAME") 690 @LargeTest 691 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSetForceSyncFrameNative()692 public void testSetForceSyncFrameNative() throws IOException, CloneNotSupportedException { 693 Assume.assumeTrue("Test is applicable only for encoders", mIsVideo); 694 695 int colorFormat = findByteBufferColorFormat(mCodecName, mMediaType); 696 assertTrue("no valid color formats received \n" + mTestConfig + mTestEnv, 697 colorFormat != -1); 698 MediaFormat format = 699 mActiveEncCfg.getBuilder().setColorFormat(colorFormat).setKeyFrameInterval(500.f) 700 .build().getFormat(); 701 boolean isPass = nativeTestSetForceSyncFrame(mCodecName, mActiveRawRes.mFileName, 702 mMediaType, EncoderConfigParams.serializeMediaFormat(format), 703 EncoderConfigParams.TOKEN_SEPARATOR, mTestConfig); 704 assertTrue(mTestConfig.toString(), isPass); 705 } 706 707 /** 708 * Test video encoders for feature adaptive bitrate. Video encoders are expected to honor 709 * bitrate changes upon request. The test requests encoder to encode at new bitrate every 'n' 710 * seconds. The test feeds encoder input for 'm' seconds. At the end, it expects the output 711 * file size to be around {sum of (n * Bi) for i in the range [0, (m/n)]} and Bi is the 712 * bitrate chosen for the interval 'n' seconds 713 */ 714 @CddTest(requirements = "5.2/C-2-1") 715 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_VIDEO_BITRATE") 716 @LargeTest 717 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testAdaptiveBitRate()718 public void testAdaptiveBitRate() throws IOException, InterruptedException { 719 Assume.assumeTrue("Skipping AdaptiveBitrate test for " + mMediaType, 720 ABR_MEDIATYPE_LIST.contains(mMediaType)); 721 MediaFormat format = mActiveEncCfg.getFormat(); 722 final int adaptiveBrInterval = 3; // change br every 3 seconds. 723 final int adaptiveBrDurFrm = mActiveEncCfg.mFrameRate * adaptiveBrInterval; 724 final int brChangeRequests = 7; 725 // TODO(b/251265293) Reduce the allowed deviation after improving the test conditions 726 final float maxBitrateDeviation = 60.0f; // allowed bitrate deviation in % 727 728 boolean[] boolStates = {true, false}; 729 setUpSource(mActiveRawRes.mFileName); 730 mOutputBuff = new OutputManager(); 731 mSaveToMem = true; 732 { 733 mCodec = MediaCodec.createByCodecName(mCodecName); 734 for (boolean isAsync : boolStates) { 735 mOutputBuff.reset(); 736 mInfoList.clear(); 737 configureCodec(format, isAsync, false, true); 738 mCodec.start(); 739 int expOutSize = 0; 740 int bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE); 741 for (int i = 0; i < brChangeRequests; i++) { 742 doWork(adaptiveBrDurFrm); 743 if (mSawInputEOS) { 744 fail(String.format("Unable to encode %d frames as the input resource " 745 + "contains only %d frames \n", adaptiveBrDurFrm, mInputCount)); 746 } 747 expOutSize += adaptiveBrInterval * bitrate; 748 if ((i & 1) == 1) bitrate *= 2; 749 else bitrate /= 2; 750 updateBitrate(bitrate); 751 mInputBufferReadOffset = 0; 752 } 753 queueEOS(); 754 waitForAllOutputs(); 755 /* TODO(b/147348711) */ 756 if (false) mCodec.stop(); 757 else mCodec.reset(); 758 /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */ 759 int outSize = mOutputBuff.getOutStreamSize() * 8; 760 float brDev = Math.abs(expOutSize - outSize) * 100.0f / expOutSize; 761 if (brDev > maxBitrateDeviation) { 762 fail("Relative Bitrate error is too large " + brDev + "\n" + mTestConfig 763 + mTestEnv); 764 } 765 } 766 mCodec.release(); 767 } 768 } 769 nativeTestAdaptiveBitRate(String encoder, String file, String mediaType, String cfgParams, String separator, StringBuilder retMsg)770 private native boolean nativeTestAdaptiveBitRate(String encoder, String file, String mediaType, 771 String cfgParams, String separator, StringBuilder retMsg); 772 773 /** 774 * Test is similar to {@link #testAdaptiveBitRate()} but uses ndk api 775 */ 776 @CddTest(requirements = "5.2/C-2-1") 777 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_VIDEO_BITRATE") 778 @LargeTest 779 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testAdaptiveBitRateNative()780 public void testAdaptiveBitRateNative() throws IOException, CloneNotSupportedException { 781 Assume.assumeTrue("Skipping Native AdaptiveBitrate test for " + mMediaType, 782 ABR_MEDIATYPE_LIST.contains(mMediaType)); 783 int colorFormat = findByteBufferColorFormat(mCodecName, mMediaType); 784 assertTrue("no valid color formats received \n" + mTestConfig + mTestEnv, 785 colorFormat != -1); 786 MediaFormat format = 787 mActiveEncCfg.getBuilder().setColorFormat(colorFormat).build().getFormat(); 788 boolean isPass = nativeTestAdaptiveBitRate(mCodecName, mActiveRawRes.mFileName, mMediaType, 789 EncoderConfigParams.serializeMediaFormat(format), 790 EncoderConfigParams.TOKEN_SEPARATOR, mTestConfig); 791 assertTrue(mTestConfig.toString(), isPass); 792 } 793 } 794