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