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.MediaExtractor;
22 import android.media.MediaFormat;
23 import android.util.Log;
24 import android.view.Surface;
25 
26 import androidx.test.filters.LargeTest;
27 import androidx.test.filters.SmallTest;
28 
29 import org.junit.Assume;
30 import org.junit.Ignore;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.Parameterized;
34 
35 import java.io.File;
36 import java.io.FileInputStream;
37 import java.io.IOException;
38 import java.nio.ByteBuffer;
39 import java.nio.ByteOrder;
40 import java.nio.channels.FileChannel;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collection;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Set;
47 
48 import static org.junit.Assert.assertTrue;
49 import static org.junit.Assert.fail;
50 
51 /**
52  * Validate decode functionality of listed decoder components
53  *
54  * The test aims to test all decoders advertised in MediaCodecList. Hence we are not using
55  * MediaCodecList#findDecoderForFormat to create codec. Further, it can so happen that the
56  * test clip chosen is not supported by component (codecCapabilities.isFormatSupported()
57  * fails), then it is better to replace the clip but not skip testing the component. The idea
58  * of these tests are not to cover CDD requirements but to test components and their plugins
59  */
60 @RunWith(Parameterized.class)
61 public class CodecDecoderTest extends CodecDecoderTestBase {
62     private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName();
63 
64     private final String mRefFile;
65     private final String mReconfigFile;
66     private final float mRmsError;
67 
CodecDecoderTest(String mime, String testFile, String refFile, String reconfigFile, float rmsError)68     public CodecDecoderTest(String mime, String testFile, String refFile, String reconfigFile,
69             float rmsError) {
70         super(mime, testFile);
71         mRefFile = refFile;
72         mReconfigFile = reconfigFile;
73         mRmsError = rmsError;
74     }
75 
setUpAudioReference()76     private short[] setUpAudioReference() throws IOException {
77         File refFile = new File(mInpPrefix + mRefFile);
78         short[] refData;
79         try (FileInputStream refStream = new FileInputStream(refFile)) {
80             FileChannel fileChannel = refStream.getChannel();
81             int length = (int) refFile.length();
82             ByteBuffer refBuffer = ByteBuffer.allocate(length);
83             refBuffer.order(ByteOrder.LITTLE_ENDIAN);
84             fileChannel.read(refBuffer);
85             refData = new short[length / 2];
86             refBuffer.position(0);
87             for (int i = 0; i < length / 2; i++) {
88                 refData[i] = refBuffer.getShort();
89             }
90         }
91         return refData;
92     }
93 
createSubFrames(ByteBuffer buffer, int sfCount)94     private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) {
95         int size = (int) mExtractor.getSampleSize();
96         if (size < 0) return null;
97         mExtractor.readSampleData(buffer, 0);
98         long pts = mExtractor.getSampleTime();
99         int flags = mExtractor.getSampleFlags();
100         if (size < sfCount) sfCount = size;
101         ArrayList<MediaCodec.BufferInfo> list = new ArrayList<>();
102         int offset = 0;
103         for (int i = 0; i < sfCount; i++) {
104             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
105             info.offset = offset;
106             info.presentationTimeUs = pts;
107             info.flags = 0;
108             if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
109                 info.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
110             }
111             if ((flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
112                 info.flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
113             }
114             if (i != sfCount - 1) {
115                 info.size = size / sfCount;
116                 info.flags |= MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME;
117             } else {
118                 info.size = size - offset;
119             }
120             list.add(info);
121             offset += info.size;
122         }
123         return list;
124     }
125 
126     @Parameterized.Parameters(name = "{index}({0})")
input()127     public static Collection<Object[]> input() {
128         Set<String> list = new HashSet<>();
129         if (hasAudioOutput()) {
130             // sec 5.1.2
131             list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
132             list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
133             list.add(MediaFormat.MIMETYPE_AUDIO_MPEG);
134             list.add(MediaFormat.MIMETYPE_AUDIO_VORBIS);
135             list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
136             list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
137         }
138         if (isHandheld() || isTv() || isAutomotive()) {
139             // sec 2.2.2, 2.3.2, 2.5.2
140             list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
141             list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
142             list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
143             list.add(MediaFormat.MIMETYPE_VIDEO_H263);
144             list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
145             list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
146         }
147         if (isHandheld()) {
148             // sec 2.2.2
149             list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
150             list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
151             list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
152         }
153         if (isTv()) {
154             // sec 2.3.2
155             list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
156             list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
157         }
158         ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
159         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
160                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3",
161                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3",
162                         91.022f * 1.05f},
163                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_16kHz_lame_vbr.mp3",
164                         "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3",
165                         119.256f * 1.05f},
166                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_2ch_44kHz_lame_cbr.mp3",
167                         "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_lame_vbr.mp3",
168                         103.60f * 1.05f},
169                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_2ch_44kHz_lame_vbr.mp3",
170                         "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_8kHz_lame_cbr.mp3",
171                         53.066f * 1.05f},
172                 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, "bbb_1ch_16kHz_16kbps_amrwb.3gp",
173                         "bbb_1ch_16kHz_s16le.raw", "bbb_1ch_16kHz_23kbps_amrwb.3gp",
174                         2393.598f * 1.05f},
175                 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, "bbb_1ch_8kHz_10kbps_amrnb.3gp",
176                         "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_8kbps_amrnb.3gp", -1.0f},
177                 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_1ch_16kHz_flac.mka",
178                         "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_flac.mka", 0.0f},
179                 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_2ch_44kHz_flac.mka",
180                         "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_flac.mka", 0.0f},
181                 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_1ch_16kHz.wav", "bbb_1ch_16kHz_s16le.raw",
182                         "bbb_2ch_44kHz.wav", 0.0f},
183                 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_2ch_44kHz.wav", "bbb_2ch_44kHz_s16le.raw",
184                         "bbb_1ch_16kHz.wav", 0.0f},
185                 {MediaFormat.MIMETYPE_AUDIO_G711_ALAW, "bbb_1ch_8kHz_alaw.wav",
186                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_alaw.wav", 23.08678f * 1.05f},
187                 {MediaFormat.MIMETYPE_AUDIO_G711_MLAW, "bbb_1ch_8kHz_mulaw.wav",
188                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_mulaw.wav", 24.4131f * 1.05f},
189                 {MediaFormat.MIMETYPE_AUDIO_MSGSM, "bbb_1ch_8kHz_gsm.wav",
190                         "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_gsm.wav", 946.02698f * 1.05f},
191                 {MediaFormat.MIMETYPE_AUDIO_VORBIS, "bbb_1ch_16kHz_vorbis.mka",
192                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_vorbis.mka", -1.0f},
193                 {MediaFormat.MIMETYPE_AUDIO_OPUS, "bbb_2ch_48kHz_opus.mka",
194                         "bbb_2ch_48kHz_s16le.raw", "bbb_1ch_48kHz_opus.mka", -1.0f},
195                 {MediaFormat.MIMETYPE_AUDIO_AAC, "bbb_1ch_16kHz_aac.mp4",
196                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_aac.mp4", -1.0f},
197                 {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4", null,
198                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", -1.0f},
199                 {MediaFormat.MIMETYPE_VIDEO_MPEG2,
200                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_2fields.mp4", null,
201                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", -1.0f},
202                 {MediaFormat.MIMETYPE_VIDEO_MPEG2,
203                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_1field.ts", null,
204                         "bbb_520x390_1mbps_30fps_mpeg2.mp4", -1.0f},
205                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4", null,
206                         "bbb_520x390_1mbps_30fps_avc.mp4", -1.0f},
207                 {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4", null,
208                         "bbb_340x280_768kbps_30fps_hevc.mp4", -1.0f},
209                 {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4",
210                         null, "bbb_176x144_192kbps_15fps_mpeg4.mp4", -1.0f},
211                 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp",
212                         null, "bbb_176x144_192kbps_10fps_h263.3gp", -1.0f},
213                 {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm", null,
214                         "bbb_520x390_1mbps_30fps_vp8.webm", -1.0f},
215                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null,
216                         "bbb_520x390_1mbps_30fps_vp9.webm", -1.0f},
217                 {MediaFormat.MIMETYPE_VIDEO_VP9,
218                         "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm", null,
219                         "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm", -1.0f},
220                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null,
221                         "bbb_520x390_1mbps_30fps_av1.mp4", -1.0f},
222         });
223         return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, false);
224     }
225 
226     /**
227      * Tests decoder for combinations:
228      * 1. Codec Sync Mode, Signal Eos with Last frame
229      * 2. Codec Sync Mode, Signal Eos Separately
230      * 3. Codec Async Mode, Signal Eos with Last frame
231      * 4. Codec Async Mode, Signal Eos Separately
232      * In all these scenarios, Timestamp ordering is verified, For audio the Rms of output has to be
233      * within the allowed tolerance. The output has to be consistent (not flaky) in all runs.
234      */
235     @LargeTest
236     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecode()237     public void testSimpleDecode() throws IOException, InterruptedException {
238         MediaFormat format = setUpSource(mTestFile);
239         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
240         if (listOfDecoders.isEmpty()) {
241             mExtractor.release();
242             fail("no suitable codecs found for mime: " + mMime);
243         }
244         boolean[] boolStates = {true, false};
245         mSaveToMem = true;
246         OutputManager ref = new OutputManager();
247         OutputManager test = new OutputManager();
248         for (String decoder : listOfDecoders) {
249             mCodec = MediaCodec.createByCodecName(decoder);
250             assertTrue("codec name act/got: " + mCodec.getName() + '/' + decoder,
251                     mCodec.getName().equals(decoder));
252             assertTrue("error! codec canonical name is null",
253                     mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty());
254             validateMetrics(decoder);
255             int loopCounter = 0;
256             for (boolean eosType : boolStates) {
257                 for (boolean isAsync : boolStates) {
258                     boolean validateFormat = true;
259                     String log = String.format("codec: %s, file: %s, mode: %s, eos type: %s:: ",
260                             decoder, mTestFile, (isAsync ? "async" : "sync"),
261                             (eosType ? "eos with last frame" : "eos separate"));
262                     mOutputBuff = loopCounter == 0 ? ref : test;
263                     mOutputBuff.reset();
264                     mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
265                     configureCodec(format, isAsync, eosType, false);
266                     MediaFormat defFormat = mCodec.getOutputFormat();
267                     if (isFormatSimilar(format, defFormat)) {
268                         if (ENABLE_LOGS) {
269                             Log.d("Input format is same as default for format for %s", decoder);
270                         }
271                         validateFormat = false;
272                     }
273                     mCodec.start();
274                     doWork(Integer.MAX_VALUE);
275                     queueEOS();
276                     waitForAllOutputs();
277                     validateMetrics(decoder, format);
278                     /* TODO(b/147348711) */
279                     if (false) mCodec.stop();
280                     else mCodec.reset();
281                     assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
282                     assertTrue(log + "no input sent", 0 != mInputCount);
283                     assertTrue(log + "output received", 0 != mOutputCount);
284                     if (loopCounter != 0) {
285                         assertTrue(log + "decoder output is flaky", ref.equals(test));
286                     } else {
287                         if (mIsAudio) {
288                             assertTrue(log + " pts is not strictly increasing",
289                                     ref.isPtsStrictlyIncreasing(mPrevOutputPts));
290                         } else {
291                             assertTrue(
292                                     log + " input pts list and output pts list are not identical",
293                                     ref.isOutPtsListIdenticalToInpPtsList(false));
294                         }
295                     }
296                     if (validateFormat) {
297                         assertTrue(log + "not received format change",
298                                 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
299                                         mSignalledOutFormatChanged);
300                         assertTrue(log + "configured format and output format are not similar",
301                                 isFormatSimilar(format,
302                                         mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
303                                                 mOutFormat));
304                     }
305                     loopCounter++;
306                 }
307             }
308             mCodec.release();
309             if (mSaveToMem && mRefFile != null && mRmsError >= 0) {
310                 short[] refData = setUpAudioReference();
311                 assertTrue(String.format("%s rms error too high", mTestFile),
312                         ref.getRmsError(refData) <= mRmsError);
313             }
314         }
315         mExtractor.release();
316     }
317 
nativeTestSimpleDecode(String decoder, Surface surface, String mime, String testFile, String refFile, float rmsError)318     private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
319             String testFile, String refFile, float rmsError);
320 
321 
322     @LargeTest
323     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeNative()324     public void testSimpleDecodeNative() {
325         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
326         if (listOfDecoders.isEmpty()) {
327             fail("no suitable codecs found for mime: " + mMime);
328         }
329         for (String decoder : listOfDecoders) {
330             assertTrue(nativeTestSimpleDecode(decoder, null, mMime, mInpPrefix + mTestFile,
331                     mInpPrefix + mRefFile, mRmsError));
332         }
333     }
334 
335     /**
336      * Tests flush when codec is in sync and async mode. In these scenarios, Timestamp
337      * ordering is verified. The output has to be consistent (not flaky) in all runs
338      */
339     @Ignore("TODO(b/147576107)")
340     @LargeTest
341     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlush()342     public void testFlush() throws IOException, InterruptedException {
343         MediaFormat format = setUpSource(mTestFile);
344         mExtractor.release();
345         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
346         if (listOfDecoders.isEmpty()) {
347             fail("no suitable codecs found for mime: " + mMime);
348         }
349         mCsdBuffers.clear();
350         for (int i = 0; ; i++) {
351             String csdKey = "csd-" + i;
352             if (format.containsKey(csdKey)) {
353                 mCsdBuffers.add(format.getByteBuffer(csdKey));
354             } else break;
355         }
356         final long pts = 500000;
357         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
358         boolean[] boolStates = {true, false};
359         OutputManager test = new OutputManager();
360         for (String decoder : listOfDecoders) {
361             decodeToMemory(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
362             OutputManager ref = mOutputBuff;
363             if (mIsAudio) {
364                 assertTrue("reference output pts is not strictly increasing",
365                         ref.isPtsStrictlyIncreasing(mPrevOutputPts));
366             } else {
367                 assertTrue("input pts list and output pts list are not identical",
368                         ref.isOutPtsListIdenticalToInpPtsList(false));
369             }
370             mOutputBuff = test;
371             setUpSource(mTestFile);
372             mCodec = MediaCodec.createByCodecName(decoder);
373             for (boolean isAsync : boolStates) {
374                 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
375                         mTestFile, (isAsync ? "async" : "sync"));
376                 mExtractor.seekTo(0, mode);
377                 configureCodec(format, isAsync, true, false);
378                 MediaFormat defFormat = mCodec.getOutputFormat();
379                 boolean validateFormat = true;
380                 if (isFormatSimilar(format, defFormat)) {
381                     if (ENABLE_LOGS) {
382                         Log.d("Input format is same as default for format for %s", decoder);
383                     }
384                     validateFormat = false;
385                 }
386                 mCodec.start();
387 
388                 /* test flush in running state before queuing input */
389                 flushCodec();
390                 if (mIsCodecInAsyncMode) mCodec.start();
391                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
392 
393                 doWork(1);
394                 flushCodec();
395                 if (mIsCodecInAsyncMode) mCodec.start();
396                 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
397 
398                 mExtractor.seekTo(0, mode);
399                 test.reset();
400                 doWork(23);
401                 assertTrue(log + " pts is not strictly increasing",
402                         test.isPtsStrictlyIncreasing(mPrevOutputPts));
403 
404                 boolean checkMetrics = (mOutputCount != 0);
405 
406                 /* test flush in running state */
407                 flushCodec();
408                 if (checkMetrics) validateMetrics(decoder, format);
409                 if (mIsCodecInAsyncMode) mCodec.start();
410                 mSaveToMem = true;
411                 test.reset();
412                 mExtractor.seekTo(pts, mode);
413                 doWork(Integer.MAX_VALUE);
414                 queueEOS();
415                 waitForAllOutputs();
416                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
417                 assertTrue(log + "no input sent", 0 != mInputCount);
418                 assertTrue(log + "output received", 0 != mOutputCount);
419                 assertTrue(log + "decoder output is flaky", ref.equals(test));
420 
421                 /* test flush in eos state */
422                 flushCodec();
423                 if (mIsCodecInAsyncMode) mCodec.start();
424                 test.reset();
425                 mExtractor.seekTo(pts, mode);
426                 doWork(Integer.MAX_VALUE);
427                 queueEOS();
428                 waitForAllOutputs();
429                 /* TODO(b/147348711) */
430                 if (false) mCodec.stop();
431                 else mCodec.reset();
432                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
433                 assertTrue(log + "no input sent", 0 != mInputCount);
434                 assertTrue(log + "output received", 0 != mOutputCount);
435                 assertTrue(log + "decoder output is flaky", ref.equals(test));
436                 if (validateFormat) {
437                     assertTrue(log + "not received format change",
438                             mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
439                                     mSignalledOutFormatChanged);
440                     assertTrue(log + "configured format and output format are not similar",
441                             isFormatSimilar(format,
442                                     mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
443                                             mOutFormat));
444                 }
445                 mSaveToMem = false;
446             }
447             mCodec.release();
448             mExtractor.release();
449         }
450     }
451 
nativeTestFlush(String decoder, Surface surface, String mime, String testFile)452     private native boolean nativeTestFlush(String decoder, Surface surface, String mime,
453             String testFile);
454 
455     @Ignore("TODO(b/147576107)")
456     @LargeTest
457     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testFlushNative()458     public void testFlushNative() {
459         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
460         if (listOfDecoders.isEmpty()) {
461             fail("no suitable codecs found for mime: " + mMime);
462         }
463         for (String decoder : listOfDecoders) {
464             assertTrue(nativeTestFlush(decoder, null, mMime, mInpPrefix + mTestFile));
465         }
466     }
467 
468     /**
469      * Tests reconfigure when codec is in sync and async mode. In these scenarios, Timestamp
470      * ordering is verified. The output has to be consistent (not flaky) in all runs
471      */
472     @Ignore("TODO(b/148523403)")
473     @LargeTest
474     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testReconfigure()475     public void testReconfigure() throws IOException, InterruptedException {
476         MediaFormat format = setUpSource(mTestFile);
477         mExtractor.release();
478         MediaFormat newFormat = setUpSource(mReconfigFile);
479         mExtractor.release();
480         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
481         if (listOfDecoders.isEmpty()) {
482             fail("no suitable codecs found for mime: " + mMime);
483         }
484         final long pts = 500000;
485         final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
486         boolean[] boolStates = {true, false};
487         OutputManager test = new OutputManager();
488         for (String decoder : listOfDecoders) {
489             decodeToMemory(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
490             OutputManager ref = mOutputBuff;
491             decodeToMemory(mReconfigFile, decoder, pts, mode, Integer.MAX_VALUE);
492             OutputManager configRef = mOutputBuff;
493             if (mIsAudio) {
494                 assertTrue("reference output pts is not strictly increasing",
495                         ref.isPtsStrictlyIncreasing(mPrevOutputPts));
496                 assertTrue("config reference output pts is not strictly increasing",
497                         configRef.isPtsStrictlyIncreasing(mPrevOutputPts));
498             } else {
499                 assertTrue("input pts list and reference pts list are not identical",
500                         ref.isOutPtsListIdenticalToInpPtsList(false));
501                 assertTrue("input pts list and reconfig ref output pts list are not identical",
502                         ref.isOutPtsListIdenticalToInpPtsList(false));
503             }
504             mOutputBuff = test;
505             mCodec = MediaCodec.createByCodecName(decoder);
506             for (boolean isAsync : boolStates) {
507                 setUpSource(mTestFile);
508                 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
509                         mTestFile, (isAsync ? "async" : "sync"));
510                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
511                 configureCodec(format, isAsync, true, false);
512                 MediaFormat defFormat = mCodec.getOutputFormat();
513                 boolean validateFormat = true;
514                 if (isFormatSimilar(format, defFormat)) {
515                     if (ENABLE_LOGS) {
516                         Log.d("Input format is same as default for format for %s", decoder);
517                     }
518                     validateFormat = false;
519                 }
520 
521                 /* test reconfigure in stopped state */
522                 reConfigureCodec(format, !isAsync, false, false);
523                 mCodec.start();
524 
525                 /* test reconfigure in running state before queuing input */
526                 reConfigureCodec(format, !isAsync, false, false);
527                 mCodec.start();
528                 doWork(23);
529 
530                 if (mOutputCount != 0) {
531                     if (validateFormat) {
532                         assertTrue(log + "not received format change",
533                                 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
534                                         mSignalledOutFormatChanged);
535                         assertTrue(log + "configured format and output format are not similar",
536                                 isFormatSimilar(format,
537                                         mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
538                                                 mOutFormat));
539                     }
540                     validateMetrics(decoder, format);
541                 }
542 
543                 /* test reconfigure codec in running state */
544                 reConfigureCodec(format, isAsync, true, false);
545                 mCodec.start();
546                 mSaveToMem = true;
547                 test.reset();
548                 mExtractor.seekTo(pts, mode);
549                 doWork(Integer.MAX_VALUE);
550                 queueEOS();
551                 waitForAllOutputs();
552                 /* TODO(b/147348711) */
553                 if (false) mCodec.stop();
554                 else mCodec.reset();
555                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
556                 assertTrue(log + "no input sent", 0 != mInputCount);
557                 assertTrue(log + "output received", 0 != mOutputCount);
558                 assertTrue(log + "decoder output is flaky", ref.equals(test));
559                 if (validateFormat) {
560                     assertTrue(log + "not received format change",
561                             mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
562                                     mSignalledOutFormatChanged);
563                     assertTrue(log + "configured format and output format are not similar",
564                             isFormatSimilar(format,
565                                     mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
566                                             mOutFormat));
567                 }
568 
569                 /* test reconfigure codec at eos state */
570                 reConfigureCodec(format, !isAsync, false, false);
571                 mCodec.start();
572                 test.reset();
573                 mExtractor.seekTo(pts, mode);
574                 doWork(Integer.MAX_VALUE);
575                 queueEOS();
576                 waitForAllOutputs();
577                 /* TODO(b/147348711) */
578                 if (false) mCodec.stop();
579                 else mCodec.reset();
580                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
581                 assertTrue(log + "no input sent", 0 != mInputCount);
582                 assertTrue(log + "output received", 0 != mOutputCount);
583                 assertTrue(log + "decoder output is flaky", ref.equals(test));
584                 if (validateFormat) {
585                     assertTrue(log + "not received format change",
586                             mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
587                                     mSignalledOutFormatChanged);
588                     assertTrue(log + "configured format and output format are not similar",
589                             isFormatSimilar(format,
590                                     mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
591                                             mOutFormat));
592                 }
593                 mExtractor.release();
594 
595                 /* test reconfigure codec for new file */
596                 setUpSource(mReconfigFile);
597                 log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
598                         mReconfigFile, (isAsync ? "async" : "sync"));
599                 reConfigureCodec(newFormat, isAsync, false, false);
600                 if (isFormatSimilar(newFormat, defFormat)) {
601                     if (ENABLE_LOGS) {
602                         Log.d("Input format is same as default for format for %s", decoder);
603                     }
604                     validateFormat = false;
605                 }
606                 mCodec.start();
607                 test.reset();
608                 mExtractor.seekTo(pts, mode);
609                 doWork(Integer.MAX_VALUE);
610                 queueEOS();
611                 waitForAllOutputs();
612                 validateMetrics(decoder, newFormat);
613                 /* TODO(b/147348711) */
614                 if (false) mCodec.stop();
615                 else mCodec.reset();
616                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
617                 assertTrue(log + "no input sent", 0 != mInputCount);
618                 assertTrue(log + "output received", 0 != mOutputCount);
619                 assertTrue(log + "decoder output is flaky", configRef.equals(test));
620                 if (validateFormat) {
621                     assertTrue(log + "not received format change",
622                             mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
623                                     mSignalledOutFormatChanged);
624                     assertTrue(log + "configured format and output format are not similar",
625                             isFormatSimilar(newFormat,
626                                     mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
627                                             mOutFormat));
628                 }
629                 mSaveToMem = false;
630                 mExtractor.release();
631             }
632             mCodec.release();
633         }
634     }
635 
636     /**
637      * Tests decoder for only EOS frame
638      */
639     @SmallTest
640     @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
testOnlyEos()641     public void testOnlyEos() throws IOException, InterruptedException {
642         MediaFormat format = setUpSource(mTestFile);
643         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
644         if (listOfDecoders.isEmpty()) {
645             mExtractor.release();
646             fail("no suitable codecs found for mime: " + mMime);
647         }
648         boolean[] boolStates = {true, false};
649         OutputManager ref = new OutputManager();
650         OutputManager test = new OutputManager();
651         mSaveToMem = true;
652         for (String decoder : listOfDecoders) {
653             mCodec = MediaCodec.createByCodecName(decoder);
654             int loopCounter = 0;
655             for (boolean isAsync : boolStates) {
656                 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
657                         mTestFile, (isAsync ? "async" : "sync"));
658                 configureCodec(format, isAsync, false, false);
659                 mOutputBuff = loopCounter == 0 ? ref : test;
660                 mOutputBuff.reset();
661                 mCodec.start();
662                 queueEOS();
663                 waitForAllOutputs();
664                 mCodec.stop();
665                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
666                 if (loopCounter != 0) {
667                     assertTrue(log + "decoder output is flaky", ref.equals(test));
668                 } else {
669                     if (mIsAudio) {
670                         assertTrue(log + " pts is not strictly increasing",
671                                 ref.isPtsStrictlyIncreasing(mPrevOutputPts));
672                     } else {
673                         assertTrue(
674                                 log + " input pts list and output pts list are not identical",
675                                 ref.isOutPtsListIdenticalToInpPtsList(false));
676                     }
677                 }
678                 loopCounter++;
679             }
680             mCodec.release();
681         }
682         mExtractor.release();
683     }
684 
nativeTestOnlyEos(String decoder, String mime, String testFile)685     private native boolean nativeTestOnlyEos(String decoder, String mime, String testFile);
686 
687     @SmallTest
688     @Test
testOnlyEosNative()689     public void testOnlyEosNative() {
690         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
691         if (listOfDecoders.isEmpty()) {
692             fail("no suitable codecs found for mime: " + mMime);
693         }
694         for (String decoder : listOfDecoders) {
695             assertTrue(nativeTestOnlyEos(decoder, mMime, mInpPrefix + mTestFile));
696         }
697     }
698 
699     /**
700      * Test Decoder by Queuing CSD separately
701      */
702     @LargeTest
703     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeQueueCSD()704     public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException {
705         MediaFormat format = setUpSource(mTestFile);
706         Assume.assumeTrue("Format has no CSD, ignoring test for mime:" + mMime, hasCSD(format));
707         ArrayList<MediaFormat> formats = new ArrayList<>();
708         formats.add(format);
709         formats.add(new MediaFormat(format));
710         for (int i = 0; ; i++) {
711             String csdKey = "csd-" + i;
712             if (format.containsKey(csdKey)) {
713                 mCsdBuffers.add(format.getByteBuffer(csdKey));
714                 format.removeKey(csdKey);
715             } else break;
716         }
717         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
718         if (listOfDecoders.isEmpty()) {
719             mExtractor.release();
720             fail("no suitable codecs found for mime: " + mMime);
721         }
722         boolean[] boolStates = {true, false};
723         mSaveToMem = true;
724         OutputManager ref = new OutputManager();
725         OutputManager test = new OutputManager();
726         for (String decoder : listOfDecoders) {
727             mCodec = MediaCodec.createByCodecName(decoder);
728             int loopCounter = 0;
729             for (MediaFormat fmt : formats) {
730                 for (boolean eosMode : boolStates) {
731                     for (boolean isAsync : boolStates) {
732                         boolean validateFormat = true;
733                         String log = String.format("codec: %s, file: %s, mode: %s, eos type: %s:: ",
734                                 decoder, mTestFile, (isAsync ? "async" : "sync"),
735                                 (eosMode ? "eos with last frame" : "eos separate"));
736                         mOutputBuff = loopCounter == 0 ? ref : test;
737                         mOutputBuff.reset();
738                         mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
739                         configureCodec(fmt, isAsync, eosMode, false);
740                         MediaFormat defFormat = mCodec.getOutputFormat();
741                         if (isFormatSimilar(defFormat, format)) {
742                             if (ENABLE_LOGS) {
743                                 Log.d("Input format is same as default for format for %s", decoder);
744                             }
745                             validateFormat = false;
746                         }
747                         mCodec.start();
748                         queueCodecConfig();
749                         doWork(Integer.MAX_VALUE);
750                         queueEOS();
751                         waitForAllOutputs();
752                         validateMetrics(decoder);
753                         /* TODO(b/147348711) */
754                         if (false) mCodec.stop();
755                         else mCodec.reset();
756                         assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
757                         assertTrue(log + "no input sent", 0 != mInputCount);
758                         assertTrue(log + "output received", 0 != mOutputCount);
759                         if (loopCounter != 0) {
760                             assertTrue(log + "decoder output is flaky", ref.equals(test));
761                         } else {
762                             if (mIsAudio) {
763                                 assertTrue(log + " pts is not strictly increasing",
764                                         ref.isPtsStrictlyIncreasing(mPrevOutputPts));
765                             } else {
766                                 assertTrue(
767                                         log + " input pts list and output pts list are not identical",
768                                         ref.isOutPtsListIdenticalToInpPtsList(false));
769                             }
770                         }
771                         if (validateFormat) {
772                             assertTrue(log + "not received format change",
773                                     mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() :
774                                             mSignalledOutFormatChanged);
775                             assertTrue(log + "configured format and output format are not similar",
776                                     isFormatSimilar(format,
777                                             mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() :
778                                                     mOutFormat));
779                         }
780                         loopCounter++;
781                     }
782                 }
783             }
784             mCodec.release();
785         }
786         mExtractor.release();
787     }
788 
nativeTestSimpleDecodeQueueCSD(String decoder, String mime, String testFile)789     private native boolean nativeTestSimpleDecodeQueueCSD(String decoder, String mime,
790             String testFile);
791 
792     @LargeTest
793     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleDecodeQueueCSDNative()794     public void testSimpleDecodeQueueCSDNative() throws IOException {
795         MediaFormat format = setUpSource(mTestFile);
796         Assume.assumeTrue("Format has no CSD, ignoring test for mime:" + mMime, hasCSD(format));
797         ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
798         if (listOfDecoders.isEmpty()) {
799             fail("no suitable codecs found for mime: " + mMime);
800         }
801         for (String decoder : listOfDecoders) {
802             assertTrue(nativeTestSimpleDecodeQueueCSD(decoder, mMime, mInpPrefix + mTestFile));
803         }
804         mExtractor.release();
805     }
806 
807     /**
808      * Test decoder for partial frame
809      */
810     @LargeTest
811     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
testDecodePartialFrame()812     public void testDecodePartialFrame() throws IOException, InterruptedException {
813         MediaFormat format = setUpSource(mTestFile);
814         ArrayList<String> listOfDecoders = selectCodecs(mMime, null,
815                 new String[]{MediaCodecInfo.CodecCapabilities.FEATURE_PartialFrame}, false);
816         boolean[] boolStates = {true, false};
817         int frameLimit = 10;
818         ByteBuffer buffer = ByteBuffer.allocate(4 * 1024 * 1024);
819         OutputManager test = new OutputManager();
820         for (String decoder : listOfDecoders) {
821             decodeToMemory(mTestFile, decoder, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC, frameLimit);
822             mCodec = MediaCodec.createByCodecName(decoder);
823             OutputManager ref = mOutputBuff;
824             if (mIsAudio) {
825                 assertTrue("reference output pts is not strictly increasing",
826                         ref.isPtsStrictlyIncreasing(mPrevOutputPts));
827             } else {
828                 assertTrue("input pts list and output pts list are not identical",
829                         ref.isOutPtsListIdenticalToInpPtsList(false));
830             }
831             mSaveToMem = true;
832             mOutputBuff = test;
833             for (boolean isAsync : boolStates) {
834                 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
835                         mTestFile, (isAsync ? "async" : "sync"));
836                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
837                 test.reset();
838                 configureCodec(format, isAsync, true, false);
839                 mCodec.start();
840                 doWork(frameLimit - 1);
841                 ArrayList<MediaCodec.BufferInfo> list = createSubFrames(buffer, 4);
842                 assertTrue("no sub frames in list received for " + mTestFile,
843                         list != null && list.size() > 0);
844                 doWork(buffer, list);
845                 queueEOS();
846                 waitForAllOutputs();
847                 /* TODO(b/147348711) */
848                 if (false) mCodec.stop();
849                 else mCodec.reset();
850                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
851                 assertTrue(log + "no input sent", 0 != mInputCount);
852                 assertTrue(log + "output received", 0 != mOutputCount);
853                 assertTrue(log + "decoder output is not consistent with ref", ref.equals(test));
854             }
855             mCodec.release();
856         }
857         mExtractor.release();
858     }
859 }
860