1 /* 2 * Copyright (C) 2023 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.common.cts; 18 19 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; 20 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010; 21 import static android.mediav2.common.cts.CodecEncoderTestBase.getMuxerFormatForMediaType; 22 import static android.mediav2.common.cts.CodecTestBase.hasSupportForColorFormat; 23 import static android.mediav2.common.cts.CodecTestBase.isHardwareAcceleratedCodec; 24 import static android.mediav2.common.cts.CodecTestBase.isSoftwareCodec; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertNotEquals; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.annotation.TargetApi; 34 import android.media.MediaCodec; 35 import android.media.MediaExtractor; 36 import android.media.MediaFormat; 37 import android.media.MediaMuxer; 38 import android.util.Log; 39 import android.util.Pair; 40 import android.view.Surface; 41 42 import com.android.compatibility.common.util.Preconditions; 43 44 import org.junit.After; 45 import org.junit.Assume; 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.rules.TestName; 49 50 import java.io.IOException; 51 import java.nio.ByteBuffer; 52 import java.util.ArrayList; 53 import java.util.stream.IntStream; 54 55 /** 56 * Wrapper class for trying and testing encoder components in surface mode. 57 */ 58 public class CodecEncoderSurfaceTestBase { 59 private static final String LOG_TAG = CodecEncoderSurfaceTestBase.class.getSimpleName(); 60 private static final boolean ENABLE_LOGS = false; 61 62 protected final String mEncoderName; 63 protected final String mEncMediaType; 64 protected final String mDecoderName; 65 protected final String mTestFileMediaType; 66 protected final String mTestFile; 67 protected final EncoderConfigParams mEncCfgParams; 68 protected final int mDecColorFormat; 69 protected final boolean mIsOutputToneMapped; 70 protected final boolean mUsePersistentSurface; 71 protected final String mTestArgs; 72 73 protected MediaExtractor mExtractor; 74 protected MediaCodec mEncoder; 75 protected MediaFormat mEncoderFormat; 76 protected final CodecAsyncHandler mAsyncHandleEncoder = new CodecAsyncHandler(); 77 protected MediaCodec mDecoder; 78 protected MediaFormat mDecoderFormat; 79 protected final CodecAsyncHandler mAsyncHandleDecoder = new CodecAsyncHandler(); 80 protected boolean mIsCodecInAsyncMode; 81 protected boolean mSignalEOSWithLastFrame; 82 protected boolean mSawDecInputEOS; 83 protected boolean mSawDecOutputEOS; 84 protected boolean mSawEncOutputEOS; 85 protected int mDecInputCount; 86 protected int mDecOutputCount; 87 protected int mEncOutputCount; 88 protected int mLatency; 89 protected boolean mReviseLatency; 90 91 protected final StringBuilder mTestConfig = new StringBuilder(); 92 protected final StringBuilder mTestEnv = new StringBuilder(); 93 94 protected boolean mSaveToMem; 95 protected OutputManager mOutputBuff; 96 97 protected Surface mSurface; 98 99 protected MediaMuxer mMuxer; 100 protected int mTrackID = -1; 101 CodecEncoderSurfaceTestBase(String encoder, String mediaType, String decoder, String testFileMediaType, String testFile, EncoderConfigParams encCfgParams, int decColorFormat, boolean isOutputToneMapped, boolean usePersistentSurface, String allTestParams)102 public CodecEncoderSurfaceTestBase(String encoder, String mediaType, String decoder, 103 String testFileMediaType, String testFile, EncoderConfigParams encCfgParams, 104 int decColorFormat, boolean isOutputToneMapped, boolean usePersistentSurface, 105 String allTestParams) { 106 mEncoderName = encoder; 107 mEncMediaType = mediaType; 108 mDecoderName = decoder; 109 mTestFileMediaType = testFileMediaType; 110 mTestFile = testFile; 111 mEncCfgParams = encCfgParams; 112 mDecColorFormat = decColorFormat; 113 mIsOutputToneMapped = isOutputToneMapped; 114 mUsePersistentSurface = usePersistentSurface; 115 mTestArgs = allTestParams; 116 mLatency = mEncCfgParams.mMaxBFrames; 117 mReviseLatency = false; 118 } 119 120 @Rule 121 public TestName mTestName = new TestName(); 122 123 @Before setUpCodecEncoderSurfaceTestBase()124 public void setUpCodecEncoderSurfaceTestBase() throws IOException, CloneNotSupportedException { 125 mTestConfig.setLength(0); 126 mTestConfig.append("\n################## Test Details ####################\n"); 127 mTestConfig.append("Test Name :- ").append(mTestName.getMethodName()).append("\n"); 128 mTestConfig.append("Test Parameters :- ").append(mTestArgs).append("\n"); 129 if (mEncoderName.startsWith(CodecTestBase.INVALID_CODEC) || mDecoderName.startsWith( 130 CodecTestBase.INVALID_CODEC)) { 131 fail("no valid component available for current test. \n" + mTestConfig); 132 } 133 mDecoderFormat = setUpSource(mTestFile); 134 mExtractor.release(); 135 ArrayList<MediaFormat> decoderFormatList = new ArrayList<>(); 136 decoderFormatList.add(mDecoderFormat); 137 Assume.assumeTrue("Decoder: " + mDecoderName + " doesn't support format: " + mDecoderFormat, 138 CodecTestBase.areFormatsSupported(mDecoderName, mTestFileMediaType, 139 decoderFormatList)); 140 if (CodecTestBase.doesAnyFormatHaveHDRProfile(mTestFileMediaType, decoderFormatList) 141 || mTestFile.contains("10bit")) { 142 // Check if encoder is capable of supporting HDR profiles. 143 // Previous check doesn't verify this as profile isn't set in the format 144 Assume.assumeTrue(mEncoderName + " doesn't support HDR encoding", 145 CodecTestBase.doesCodecSupportHDRProfile(mEncoderName, mEncMediaType)); 146 } 147 148 if (mDecColorFormat == COLOR_FormatSurface) { 149 // TODO(b/253492870) Remove the following assumption check once this is supported 150 Assume.assumeFalse(mDecoderName + "is hardware accelerated and " + mEncoderName 151 + "is software only.", 152 isHardwareAcceleratedCodec(mDecoderName) && isSoftwareCodec(mEncoderName)); 153 } else { 154 // findDecoderForFormat() ignores color-format and decoder returned may not be 155 // supporting the color format set in mDecoderFormat. Following check will 156 // skip the test if decoder doesn't support the color format that is set. 157 boolean decoderSupportsColorFormat = 158 hasSupportForColorFormat(mDecoderName, mTestFileMediaType, mDecColorFormat); 159 if (mDecColorFormat == COLOR_FormatYUVP010) { 160 assumeTrue(mDecoderName + " doesn't support P010 output.", 161 decoderSupportsColorFormat); 162 } else { 163 assertTrue(mDecoderName + " doesn't support 420p 888 flexible output.", 164 decoderSupportsColorFormat); 165 } 166 } 167 EncoderConfigParams.Builder foreman = mEncCfgParams.getBuilder() 168 .setWidth(mDecoderFormat.getInteger(MediaFormat.KEY_WIDTH)) 169 .setHeight(mDecoderFormat.getInteger(MediaFormat.KEY_HEIGHT)); 170 mEncoderFormat = foreman.build().getFormat(); 171 } 172 173 @After tearDownCodecEncoderSurfaceTestBase()174 public void tearDownCodecEncoderSurfaceTestBase() { 175 if (mDecoder != null) { 176 mDecoder.release(); 177 mDecoder = null; 178 } 179 if (mSurface != null) { 180 mSurface.release(); 181 mSurface = null; 182 } 183 if (mEncoder != null) { 184 mEncoder.release(); 185 mEncoder = null; 186 } 187 if (mExtractor != null) { 188 mExtractor.release(); 189 mExtractor = null; 190 } 191 if (mMuxer != null) { 192 mMuxer.release(); 193 mMuxer = null; 194 } 195 } 196 hasSeenError()197 protected boolean hasSeenError() { 198 return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError(); 199 } 200 201 @TargetApi(33) setUpSource(String srcFile)202 protected MediaFormat setUpSource(String srcFile) throws IOException { 203 Preconditions.assertTestFileExists(srcFile); 204 mExtractor = new MediaExtractor(); 205 mExtractor.setDataSource(srcFile); 206 for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) { 207 MediaFormat format = mExtractor.getTrackFormat(trackID); 208 String mediaType = format.getString(MediaFormat.KEY_MIME); 209 if (mediaType.equals(mTestFileMediaType)) { 210 mExtractor.selectTrack(trackID); 211 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mDecColorFormat); 212 if (mIsOutputToneMapped) { 213 format.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, 214 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 215 } 216 return format; 217 } 218 } 219 mExtractor.release(); 220 fail("No video track found in file: " + srcFile + ". \n" + mTestConfig + mTestEnv); 221 return null; 222 } 223 resetContext(boolean isAsync, boolean signalEOSWithLastFrame)224 protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { 225 mAsyncHandleDecoder.resetContext(); 226 mAsyncHandleEncoder.resetContext(); 227 mIsCodecInAsyncMode = isAsync; 228 mSignalEOSWithLastFrame = signalEOSWithLastFrame; 229 mSawDecInputEOS = false; 230 mSawDecOutputEOS = false; 231 mSawEncOutputEOS = false; 232 mDecInputCount = 0; 233 mDecOutputCount = 0; 234 mEncOutputCount = 0; 235 } 236 configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync, boolean signalEOSWithLastFrame)237 protected void configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync, 238 boolean signalEOSWithLastFrame) { 239 resetContext(isAsync, signalEOSWithLastFrame); 240 mAsyncHandleEncoder.setCallBack(mEncoder, isAsync); 241 mEncoder.configure(encFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null); 242 if (mEncoder.getInputFormat().containsKey(MediaFormat.KEY_LATENCY)) { 243 mReviseLatency = true; 244 mLatency = mEncoder.getInputFormat().getInteger(MediaFormat.KEY_LATENCY); 245 } 246 if (mUsePersistentSurface) { 247 mSurface = MediaCodec.createPersistentInputSurface(); 248 mEncoder.setInputSurface(mSurface); 249 } else { 250 mSurface = mEncoder.createInputSurface(); 251 } 252 assertTrue("Surface is not valid", mSurface.isValid()); 253 mAsyncHandleDecoder.setCallBack(mDecoder, isAsync); 254 mDecoder.configure(decFormat, mSurface, null, 0); 255 mTestEnv.setLength(0); 256 mTestEnv.append("################### Test Environment #####################\n"); 257 mTestEnv.append(String.format("Encoder under test :- %s \n", mEncoderName)); 258 mTestEnv.append(String.format("Format under test :- %s \n", encFormat)); 259 mTestEnv.append(String.format("Encoder is fed with output of :- %s \n", mDecoderName)); 260 mTestEnv.append(String.format("Format of Decoder Input :- %s", decFormat)); 261 mTestEnv.append(String.format("Encoder and Decoder are operating in :- %s mode \n", 262 (isAsync ? "asynchronous" : "synchronous"))); 263 mTestEnv.append(String.format("Components received input eos :- %s \n", 264 (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer"))); 265 if (ENABLE_LOGS) { 266 Log.v(LOG_TAG, "codec configured"); 267 } 268 } 269 enqueueDecoderEOS(int bufferIndex)270 protected void enqueueDecoderEOS(int bufferIndex) { 271 if (!mSawDecInputEOS) { 272 mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 273 mSawDecInputEOS = true; 274 if (ENABLE_LOGS) { 275 Log.v(LOG_TAG, "Queued End of Stream"); 276 } 277 } 278 } 279 enqueueDecoderInput(int bufferIndex)280 protected void enqueueDecoderInput(int bufferIndex) { 281 if (mExtractor.getSampleSize() < 0) { 282 enqueueDecoderEOS(bufferIndex); 283 } else { 284 ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex); 285 mExtractor.readSampleData(inputBuffer, 0); 286 int size = (int) mExtractor.getSampleSize(); 287 long pts = mExtractor.getSampleTime(); 288 int extractorFlags = mExtractor.getSampleFlags(); 289 int codecFlags = 0; 290 if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) { 291 codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME; 292 } 293 if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) { 294 codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME; 295 } 296 if (!mExtractor.advance() && mSignalEOSWithLastFrame) { 297 codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; 298 mSawDecInputEOS = true; 299 } 300 if (ENABLE_LOGS) { 301 Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts 302 + " flags: " + codecFlags); 303 } 304 mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags); 305 if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG 306 | MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) { 307 mOutputBuff.saveInPTS(pts); 308 mDecInputCount++; 309 } 310 } 311 } 312 dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info)313 protected void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) { 314 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 315 mSawDecOutputEOS = true; 316 } 317 if (ENABLE_LOGS) { 318 Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " 319 + info.size + " timestamp: " + info.presentationTimeUs); 320 } 321 if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { 322 mDecOutputCount++; 323 } 324 mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null); 325 } 326 dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info)327 protected void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) { 328 if (ENABLE_LOGS) { 329 Log.v(LOG_TAG, "encoder output: id: " + bufferIndex + " flags: " + info.flags 330 + " size: " + info.size + " timestamp: " + info.presentationTimeUs); 331 } 332 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 333 mSawEncOutputEOS = true; 334 } 335 if (info.size > 0) { 336 ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex); 337 if (mSaveToMem) { 338 mOutputBuff.saveToMemory(buf, info); 339 } 340 if (mMuxer != null) { 341 if (mTrackID == -1) { 342 mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat()); 343 mMuxer.start(); 344 } 345 if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { 346 mMuxer.writeSampleData(mTrackID, buf, info); 347 } 348 } 349 if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { 350 mOutputBuff.saveOutPTS(info.presentationTimeUs); 351 mEncOutputCount++; 352 } 353 } 354 mEncoder.releaseOutputBuffer(bufferIndex, false); 355 } 356 tryEncoderOutput(long timeOutUs)357 protected void tryEncoderOutput(long timeOutUs) throws InterruptedException { 358 if (mIsCodecInAsyncMode) { 359 if (!hasSeenError() && !mSawEncOutputEOS) { 360 while (mReviseLatency) { 361 mAsyncHandleEncoder.waitOnFormatChange(); 362 mReviseLatency = false; 363 int actualLatency = mAsyncHandleEncoder.getOutputFormat() 364 .getInteger(MediaFormat.KEY_LATENCY, mLatency); 365 if (mLatency < actualLatency) { 366 mLatency = actualLatency; 367 return; 368 } 369 } 370 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput(); 371 if (element != null) { 372 dequeueEncoderOutput(element.first, element.second); 373 } 374 } 375 } else { 376 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 377 if (!mSawEncOutputEOS) { 378 int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, timeOutUs); 379 if (outputBufferId >= 0) { 380 dequeueEncoderOutput(outputBufferId, outInfo); 381 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 382 mLatency = mEncoder.getOutputFormat() 383 .getInteger(MediaFormat.KEY_LATENCY, mLatency); 384 } 385 } 386 } 387 } 388 queueEOS()389 protected void queueEOS() throws InterruptedException { 390 if (mIsCodecInAsyncMode) { 391 while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) { 392 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork(); 393 if (element != null) { 394 int bufferID = element.first; 395 MediaCodec.BufferInfo info = element.second; 396 if (info != null) { 397 dequeueDecoderOutput(bufferID, info); 398 } else { 399 enqueueDecoderEOS(element.first); 400 } 401 } 402 } 403 } else { 404 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 405 while (!mSawDecInputEOS) { 406 int outputBufferId = 407 mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US); 408 if (outputBufferId >= 0) { 409 dequeueDecoderOutput(outputBufferId, outInfo); 410 } 411 int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US); 412 if (inputBufferId != -1) { 413 enqueueDecoderEOS(inputBufferId); 414 } 415 } 416 } 417 if (mIsCodecInAsyncMode) { 418 while (!hasSeenError() && !mSawDecOutputEOS) { 419 Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput(); 420 if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second); 421 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream(); 422 if (mDecOutputCount - mEncOutputCount > mLatency) { 423 tryEncoderOutput(-1); 424 } 425 } 426 } else { 427 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 428 while (!mSawDecOutputEOS) { 429 int outputBufferId = 430 mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US); 431 if (outputBufferId >= 0) { 432 dequeueDecoderOutput(outputBufferId, outInfo); 433 } 434 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream(); 435 if (mDecOutputCount - mEncOutputCount > mLatency) { 436 tryEncoderOutput(-1); 437 } 438 } 439 } 440 } 441 doWork(int frameLimit)442 protected void doWork(int frameLimit) throws InterruptedException { 443 int frameCnt = 0; 444 if (mIsCodecInAsyncMode) { 445 // dequeue output after inputEOS is expected to be done in waitForAllOutputs() 446 while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) { 447 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork(); 448 if (element != null) { 449 int bufferID = element.first; 450 MediaCodec.BufferInfo info = element.second; 451 if (info != null) { 452 // <id, info> corresponds to output callback. Handle it accordingly 453 dequeueDecoderOutput(bufferID, info); 454 } else { 455 // <id, null> corresponds to input callback. Handle it accordingly 456 enqueueDecoderInput(bufferID); 457 frameCnt++; 458 } 459 } 460 // check decoder EOS 461 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream(); 462 // encoder output 463 if (mDecOutputCount - mEncOutputCount > mLatency) { 464 tryEncoderOutput(-1); 465 } 466 } 467 } else { 468 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 469 while (!mSawDecInputEOS && frameCnt < frameLimit) { 470 // decoder input 471 int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US); 472 if (inputBufferId != -1) { 473 enqueueDecoderInput(inputBufferId); 474 frameCnt++; 475 } 476 // decoder output 477 int outputBufferId = 478 mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US); 479 if (outputBufferId >= 0) { 480 dequeueDecoderOutput(outputBufferId, outInfo); 481 } 482 // check decoder EOS 483 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream(); 484 // encoder output 485 if (mDecOutputCount - mEncOutputCount > mLatency) { 486 tryEncoderOutput(-1); 487 } 488 } 489 } 490 } 491 waitForAllEncoderOutputs()492 protected void waitForAllEncoderOutputs() throws InterruptedException { 493 if (mIsCodecInAsyncMode) { 494 while (!hasSeenError() && !mSawEncOutputEOS) { 495 tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US); 496 } 497 } else { 498 while (!mSawEncOutputEOS) { 499 tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US); 500 } 501 } 502 validateTestState(); 503 } 504 validateTestState()505 private void validateTestState() { 506 assertFalse("Decoder has encountered error in async mode. \n" 507 + mTestConfig + mTestEnv + mAsyncHandleDecoder.getErrMsg(), 508 mAsyncHandleDecoder.hasSeenError()); 509 assertFalse("Encoder has encountered error in async mode. \n" 510 + mTestConfig + mTestEnv + mAsyncHandleEncoder.getErrMsg(), 511 mAsyncHandleEncoder.hasSeenError()); 512 assertTrue("Decoder has not received any input \n" + mTestConfig + mTestEnv, 513 0 != mDecInputCount); 514 assertTrue("Decoder has not sent any output \n" + mTestConfig + mTestEnv, 515 0 != mDecOutputCount); 516 assertTrue("Encoder has not sent any output \n" + mTestConfig + mTestEnv, 517 0 != mEncOutputCount); 518 assertEquals("Decoder output count is not equal to decoder input count \n" 519 + mTestConfig + mTestEnv, mDecInputCount, mDecOutputCount); 520 assertEquals("Encoder output count is not equal to Decoder input count \n" 521 + mTestConfig + mTestEnv, mDecInputCount, mEncOutputCount); 522 if (!mOutputBuff.isOutPtsListIdenticalToInpPtsList((mEncCfgParams.mMaxBFrames != 0))) { 523 fail("Input pts list and Output pts list are not identical \n" + mTestConfig 524 + mTestEnv + mOutputBuff.getErrMsg()); 525 } 526 if (mEncCfgParams.mMaxBFrames == 0 && !mOutputBuff.isPtsStrictlyIncreasing( 527 Long.MIN_VALUE)) { 528 fail("Output timestamps are not strictly increasing \n" + mTestConfig + mTestEnv 529 + mOutputBuff.getErrMsg()); 530 } 531 } 532 validateToneMappedFormat(MediaFormat format, String descriptor)533 protected void validateToneMappedFormat(MediaFormat format, String descriptor) { 534 assertEquals("unexpected color transfer in " + descriptor + " after tone mapping", 535 MediaFormat.COLOR_TRANSFER_SDR_VIDEO, 536 format.getInteger(MediaFormat.KEY_COLOR_TRANSFER, 0)); 537 assertNotEquals("unexpected color standard in " + descriptor + " after tone mapping", 538 MediaFormat.COLOR_STANDARD_BT2020, 539 format.getInteger(MediaFormat.KEY_COLOR_STANDARD, 0)); 540 541 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1); 542 int[] profileArray = CodecTestBase.PROFILE_HDR_MAP.get(mEncMediaType); 543 assertFalse(descriptor + " must not contain HDR profile after tone mapping", 544 IntStream.of(profileArray).anyMatch(x -> x == profile)); 545 } 546 547 @TargetApi(33) encodeToMemory(boolean isAsync, boolean signalEOSWithLastFrame, boolean saveToMem, OutputManager outBuff, boolean muxOutput, String outPath)548 protected void encodeToMemory(boolean isAsync, boolean signalEOSWithLastFrame, 549 boolean saveToMem, OutputManager outBuff, boolean muxOutput, String outPath) 550 throws IOException, InterruptedException { 551 encodeToMemory(isAsync, signalEOSWithLastFrame, saveToMem, outBuff, muxOutput, outPath, 552 Integer.MAX_VALUE); 553 } 554 555 @TargetApi(33) encodeToMemory(boolean isAsync, boolean signalEOSWithLastFrame, boolean saveToMem, OutputManager outBuff, boolean muxOutput, String outPath, int frameLimit)556 protected void encodeToMemory(boolean isAsync, boolean signalEOSWithLastFrame, 557 boolean saveToMem, OutputManager outBuff, boolean muxOutput, String outPath, 558 int frameLimit) throws IOException, InterruptedException { 559 mSaveToMem = saveToMem; 560 mOutputBuff = outBuff; 561 mOutputBuff.reset(); 562 if (muxOutput) { 563 int muxerFormat = getMuxerFormatForMediaType(mEncMediaType); 564 mMuxer = new MediaMuxer(outPath, muxerFormat); 565 } 566 setUpSource(mTestFile); 567 mDecoder = MediaCodec.createByCodecName(mDecoderName); 568 mEncoder = MediaCodec.createByCodecName(mEncoderName); 569 configureCodec(mDecoderFormat, mEncoderFormat, isAsync, signalEOSWithLastFrame); 570 if (mIsOutputToneMapped) { 571 MediaFormat inpFormat = mDecoder.getInputFormat(); 572 int transferRequest = inpFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, 0); 573 assumeTrue(mDecoderName + " does not support HDR to SDR tone mapping", 574 0 != transferRequest); 575 } 576 mEncoder.start(); 577 mDecoder.start(); 578 doWork(frameLimit); 579 queueEOS(); 580 waitForAllEncoderOutputs(); 581 if (muxOutput) { 582 if (mTrackID != -1) { 583 mMuxer.stop(); 584 mTrackID = -1; 585 } 586 if (mMuxer != null) { 587 mMuxer.release(); 588 mMuxer = null; 589 } 590 } 591 if (mIsOutputToneMapped) { 592 MediaFormat encoderOutputFormat = mEncoder.getOutputFormat(); 593 MediaFormat decoderOutputFormat = mDecoder.getOutputFormat(); 594 validateToneMappedFormat(decoderOutputFormat, "decoder output format"); 595 validateToneMappedFormat(encoderOutputFormat, "encoder output format"); 596 if (outPath != null) { 597 MediaExtractor extractor = new MediaExtractor(); 598 extractor.setDataSource(outPath); 599 MediaFormat extractorFormat = extractor.getTrackFormat(0); 600 extractor.release(); 601 validateToneMappedFormat(extractorFormat, "extractor format"); 602 } 603 } 604 mDecoder.reset(); 605 mEncoder.reset(); 606 mSurface.release(); 607 mSurface = null; 608 mDecoder.release(); 609 mEncoder.release(); 610 mExtractor.release(); 611 mSaveToMem = false; 612 } 613 } 614