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