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