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