1 /*
2  * Copyright (C) 2012 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.media.cts;
18 
19 import android.media.cts.R;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.content.res.AssetFileDescriptor;
24 import android.content.res.Resources;
25 import android.graphics.ImageFormat;
26 import android.media.cts.CodecUtils;
27 import android.media.Image;
28 import android.media.AudioFormat;
29 import android.media.AudioManager;
30 import android.media.MediaCodec;
31 import android.media.MediaCodec.BufferInfo;
32 import android.media.MediaCodecList;
33 import android.media.MediaCodecInfo;
34 import android.media.MediaCodecInfo.CodecCapabilities;
35 import android.media.MediaExtractor;
36 import android.media.MediaFormat;
37 import android.platform.test.annotations.AppModeFull;
38 import android.util.Log;
39 import android.view.Surface;
40 import android.net.Uri;
41 
42 import com.android.compatibility.common.util.CddTest;
43 import com.android.compatibility.common.util.DeviceReportLog;
44 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
45 import com.android.compatibility.common.util.MediaUtils;
46 import com.android.compatibility.common.util.ResultType;
47 import com.android.compatibility.common.util.ResultUnit;
48 
49 import java.io.BufferedInputStream;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.nio.ByteBuffer;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56 import java.util.zip.CRC32;
57 import java.util.concurrent.CountDownLatch;
58 import java.util.concurrent.TimeUnit;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61 
62 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
63 
64 @MediaHeavyPresubmitTest
65 @AppModeFull(reason = "There should be no instant apps specific behavior related to decoders")
66 public class DecoderTest extends MediaPlayerTestBase {
67     private static final String TAG = "DecoderTest";
68 
69     private static final int RESET_MODE_NONE = 0;
70     private static final int RESET_MODE_RECONFIGURE = 1;
71     private static final int RESET_MODE_FLUSH = 2;
72     private static final int RESET_MODE_EOS_FLUSH = 3;
73 
74     private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
75 
76     private static final int CONFIG_MODE_NONE = 0;
77     private static final int CONFIG_MODE_QUEUE = 1;
78 
79     private Resources mResources;
80     short[] mMasterBuffer;
81 
82     private MediaCodecTunneledPlayer mMediaCodecPlayer;
83     private static final int SLEEP_TIME_MS = 1000;
84     private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
85 
86     private static final String AUDIO_URL_KEY = "decoder_test_audio_url";
87     private static final String VIDEO_URL_KEY = "decoder_test_video_url";
88     private static final String MODULE_NAME = "CtsMediaTestCases";
89     private DynamicConfigDeviceSide dynamicConfig;
90 
91     @Override
setUp()92     protected void setUp() throws Exception {
93         super.setUp();
94         mResources = mContext.getResources();
95 
96         // read primary file into memory
97         AssetFileDescriptor masterFd = mResources.openRawResourceFd(R.raw.sinesweepraw);
98         long masterLength = masterFd.getLength();
99         mMasterBuffer = new short[(int) (masterLength / 2)];
100         InputStream is = masterFd.createInputStream();
101         BufferedInputStream bis = new BufferedInputStream(is);
102         for (int i = 0; i < mMasterBuffer.length; i++) {
103             int lo = bis.read();
104             int hi = bis.read();
105             if (hi >= 128) {
106                 hi -= 256;
107             }
108             int sample = hi * 256 + lo;
109             mMasterBuffer[i] = (short) sample;
110         }
111         bis.close();
112         masterFd.close();
113 
114         dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
115     }
116 
117     @Override
tearDown()118     protected void tearDown() throws Exception {
119         // ensure MediaCodecPlayer resources are released even if an exception is thrown.
120         if (mMediaCodecPlayer != null) {
121             mMediaCodecPlayer.reset();
122             mMediaCodecPlayer = null;
123         }
124     }
125 
126     // TODO: add similar tests for other audio and video formats
testBug11696552()127     public void testBug11696552() throws Exception {
128         MediaCodec mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
129         MediaFormat mFormat = MediaFormat.createAudioFormat(
130                 MediaFormat.MIMETYPE_AUDIO_AAC, 48000 /* frequency */, 2 /* channels */);
131         mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} ));
132         mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
133         mMediaCodec.configure(mFormat, null, null, 0);
134         mMediaCodec.start();
135         int index = mMediaCodec.dequeueInputBuffer(250000);
136         mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
137         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
138         mMediaCodec.dequeueOutputBuffer(info, 250000);
139     }
140 
141     // The allowed errors in the following tests are the actual maximum measured
142     // errors with the standard decoders, plus 10%.
143     // This should allow for some variation in decoders, while still detecting
144     // phase and delay errors, channel swap, etc.
testDecodeMp3Lame()145     public void testDecodeMp3Lame() throws Exception {
146         decode(R.raw.sinesweepmp3lame, 804.f);
147         testTimeStampOrdering(R.raw.sinesweepmp3lame);
148     }
testDecodeMp3Smpb()149     public void testDecodeMp3Smpb() throws Exception {
150         decode(R.raw.sinesweepmp3smpb, 413.f);
151         testTimeStampOrdering(R.raw.sinesweepmp3smpb);
152     }
testDecodeM4a()153     public void testDecodeM4a() throws Exception {
154         decode(R.raw.sinesweepm4a, 124.f);
155         testTimeStampOrdering(R.raw.sinesweepm4a);
156     }
testDecodeOgg()157     public void testDecodeOgg() throws Exception {
158         decode(R.raw.sinesweepogg, 168.f);
159         testTimeStampOrdering(R.raw.sinesweepogg);
160     }
testDecodeOggMkv()161     public void testDecodeOggMkv() throws Exception {
162         decode(R.raw.sinesweepoggmkv, 168.f);
163         testTimeStampOrdering(R.raw.sinesweepoggmkv);
164     }
testDecodeOggMp4()165     public void testDecodeOggMp4() throws Exception {
166         decode(R.raw.sinesweepoggmp4, 168.f);
167         testTimeStampOrdering(R.raw.sinesweepoggmp4);
168     }
testDecodeWav()169     public void testDecodeWav() throws Exception {
170         decode(R.raw.sinesweepwav, 0.0f);
171         testTimeStampOrdering(R.raw.sinesweepwav);
172     }
testDecodeFlacMkv()173     public void testDecodeFlacMkv() throws Exception {
174         decode(R.raw.sinesweepflacmkv, 0.0f);
175         testTimeStampOrdering(R.raw.sinesweepflacmkv);
176     }
testDecodeFlac()177     public void testDecodeFlac() throws Exception {
178         decode(R.raw.sinesweepflac, 0.0f);
179         testTimeStampOrdering(R.raw.sinesweepflac);
180     }
testDecodeFlacMp4()181     public void testDecodeFlacMp4() throws Exception {
182         decode(R.raw.sinesweepflacmp4, 0.0f);
183         testTimeStampOrdering(R.raw.sinesweepflacmp4);
184     }
185 
testDecodeMonoMp3()186     public void testDecodeMonoMp3() throws Exception {
187         monoTest(R.raw.monotestmp3, 44100);
188         testTimeStampOrdering(R.raw.monotestmp3);
189     }
190 
testDecodeMonoM4a()191     public void testDecodeMonoM4a() throws Exception {
192         monoTest(R.raw.monotestm4a, 44100);
193         testTimeStampOrdering(R.raw.monotestm4a);
194     }
195 
testDecodeMonoOgg()196     public void testDecodeMonoOgg() throws Exception {
197         monoTest(R.raw.monotestogg, 44100);
198         testTimeStampOrdering(R.raw.monotestogg);
199     }
testDecodeMonoOggMkv()200     public void testDecodeMonoOggMkv() throws Exception {
201         monoTest(R.raw.monotestoggmkv, 44100);
202         testTimeStampOrdering(R.raw.monotestoggmkv);
203     }
testDecodeMonoOggMp4()204     public void testDecodeMonoOggMp4() throws Exception {
205         monoTest(R.raw.monotestoggmp4, 44100);
206         testTimeStampOrdering(R.raw.monotestoggmp4);
207     }
208 
testDecodeMonoGsm()209     public void testDecodeMonoGsm() throws Exception {
210         if (MediaUtils.hasCodecsForResource(mContext, R.raw.monotestgsm)) {
211             monoTest(R.raw.monotestgsm, 8000);
212             testTimeStampOrdering(R.raw.monotestgsm);
213         } else {
214             MediaUtils.skipTest("not mandatory");
215         }
216     }
217 
testDecodeAacTs()218     public void testDecodeAacTs() throws Exception {
219         testTimeStampOrdering(R.raw.sinesweeptsaac);
220     }
221 
testDecodeVorbis()222     public void testDecodeVorbis() throws Exception {
223         testTimeStampOrdering(R.raw.sinesweepvorbis);
224     }
testDecodeVorbisMp4()225     public void testDecodeVorbisMp4() throws Exception {
226         testTimeStampOrdering(R.raw.sinesweepvorbismp4);
227     }
228 
testDecodeOpus()229     public void testDecodeOpus() throws Exception {
230         testTimeStampOrdering(R.raw.sinesweepopus);
231     }
testDecodeOpusMp4()232     public void testDecodeOpusMp4() throws Exception {
233         testTimeStampOrdering(R.raw.sinesweepopusmp4);
234     }
235 
236     @CddTest(requirement="5.1.3")
testDecodeG711ChannelsAndRates()237     public void testDecodeG711ChannelsAndRates() throws Exception {
238         String[] mimetypes = { MediaFormat.MIMETYPE_AUDIO_G711_ALAW,
239                                MediaFormat.MIMETYPE_AUDIO_G711_MLAW };
240         int[] sampleRates = { 8000 };
241         int[] channelMasks = { AudioFormat.CHANNEL_OUT_MONO,
242                                AudioFormat.CHANNEL_OUT_STEREO,
243                                AudioFormat.CHANNEL_OUT_5POINT1 };
244 
245         verifyChannelsAndRates(mimetypes, sampleRates, channelMasks);
246     }
247 
248     @CddTest(requirement="5.1.3")
testDecodeOpusChannelsAndRates()249     public void testDecodeOpusChannelsAndRates() throws Exception {
250         String[] mimetypes = { MediaFormat.MIMETYPE_AUDIO_OPUS };
251         int[] sampleRates = { 8000, 12000, 16000, 24000, 48000 };
252         int[] channelMasks = { AudioFormat.CHANNEL_OUT_MONO,
253                                AudioFormat.CHANNEL_OUT_STEREO,
254                                AudioFormat.CHANNEL_OUT_5POINT1 };
255 
256         verifyChannelsAndRates(mimetypes, sampleRates, channelMasks);
257     }
258 
verifyChannelsAndRates(String[] mimetypes, int[] sampleRates, int[] channelMasks)259     private void verifyChannelsAndRates(String[] mimetypes, int[] sampleRates,
260                                        int[] channelMasks) throws Exception {
261 
262         for (String mimetype : mimetypes) {
263             ArrayList<MediaCodecInfo> codecInfoList = getDecoderMediaCodecInfoList(mimetype);
264             if (codecInfoList == null) {
265                 continue;
266             }
267             for (MediaCodecInfo codecInfo : codecInfoList) {
268                 MediaCodec codec = MediaCodec.createByCodecName(codecInfo.getName());
269                 for (int sampleRate : sampleRates) {
270                     for (int channelMask : channelMasks) {
271                         int channelCount = AudioFormat.channelCountFromOutChannelMask(channelMask);
272 
273                         codec.reset();
274                         MediaFormat desiredFormat = MediaFormat.createAudioFormat(
275                                 mimetype,
276                                 sampleRate,
277                                 channelCount);
278                         codec.configure(desiredFormat, null, null, 0);
279 
280                         Log.d(TAG, "codec: " + codecInfo.getName() +
281                                 " sample rate: " + sampleRate +
282                                 " channelcount:" + channelCount);
283 
284                         MediaFormat actual = codec.getInputFormat();
285                         int actualChannels = actual.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -1);
286                         int actualSampleRate = actual.getInteger(MediaFormat.KEY_SAMPLE_RATE, -1);
287                         assertTrue("channels: configured " + actualChannels +
288                                    " != desired " + channelCount, actualChannels == channelCount);
289                         assertTrue("sample rate: configured " + actualSampleRate +
290                                    " != desired " + sampleRate, actualSampleRate == sampleRate);
291                     }
292                 }
293                 codec.release();
294             }
295         }
296     }
297 
getDecoderMediaCodecInfoList(String mimeType)298     private ArrayList<MediaCodecInfo> getDecoderMediaCodecInfoList(String mimeType) {
299         MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
300         ArrayList<MediaCodecInfo> decoderInfos = new ArrayList<MediaCodecInfo>();
301         for (MediaCodecInfo codecInfo : mediaCodecList.getCodecInfos()) {
302             if (!codecInfo.isEncoder() && isMimeTypeSupported(codecInfo, mimeType)) {
303                 decoderInfos.add(codecInfo);
304             }
305         }
306         return decoderInfos;
307     }
308 
isMimeTypeSupported(MediaCodecInfo codecInfo, String mimeType)309     private boolean isMimeTypeSupported(MediaCodecInfo codecInfo, String mimeType) {
310         for (String type : codecInfo.getSupportedTypes()) {
311             if (type.equalsIgnoreCase(mimeType)) {
312                 return true;
313             }
314         }
315         return false;
316     }
317 
testDecode51M4a()318     public void testDecode51M4a() throws Exception {
319         decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
320     }
321 
testTimeStampOrdering(int res)322     private void testTimeStampOrdering(int res) throws Exception {
323         List<Long> timestamps = new ArrayList<Long>();
324         decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps);
325         Long lastTime = Long.MIN_VALUE;
326         for (int i = 0; i < timestamps.size(); i++) {
327             Long thisTime = timestamps.get(i);
328             assertTrue("timetravel occurred: " + lastTime + " > " + thisTime, thisTime >= lastTime);
329             lastTime = thisTime;
330         }
331     }
332 
testTrackSelection()333     public void testTrackSelection() throws Exception {
334         testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz);
335         testTrackSelection(
336                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
337         testTrackSelection(
338                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
339     }
340 
testTrackSelectionMkv()341     public void testTrackSelectionMkv() throws Exception {
342         Log.d(TAG, "testTrackSelectionMkv!!!!!! ");
343         testTrackSelection(R.raw.mkv_avc_adpcm_ima);
344         Log.d(TAG, "mkv_avc_adpcm_ima finished!!!!!! ");
345         testTrackSelection(R.raw.mkv_avc_adpcm_ms);
346         Log.d(TAG, "mkv_avc_adpcm_ms finished!!!!!! ");
347         testTrackSelection(R.raw.mkv_avc_wma);
348         Log.d(TAG, "mkv_avc_wma finished!!!!!! ");
349         testTrackSelection(R.raw.mkv_avc_mp2);
350         Log.d(TAG, "mkv_avc_mp2 finished!!!!!! ");
351     }
352 
testBFrames()353     public void testBFrames() throws Exception {
354         int testsRun =
355             testBFrames(R.raw.video_h264_main_b_frames) +
356             testBFrames(R.raw.video_h264_main_b_frames_frag);
357         if (testsRun == 0) {
358             MediaUtils.skipTest("no codec found");
359         }
360     }
361 
testBFrames(int res)362     public int testBFrames(int res) throws Exception {
363         AssetFileDescriptor fd = mResources.openRawResourceFd(res);
364         MediaExtractor ex = new MediaExtractor();
365         ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
366         MediaFormat format = ex.getTrackFormat(0);
367         String mime = format.getString(MediaFormat.KEY_MIME);
368         assertTrue("not a video track. Wrong test file?", mime.startsWith("video/"));
369         if (!MediaUtils.canDecode(format)) {
370             ex.release();
371             fd.close();
372             return 0; // skip
373         }
374         MediaCodec dec = MediaCodec.createDecoderByType(mime);
375         Surface s = getActivity().getSurfaceHolder().getSurface();
376         dec.configure(format, s, null, 0);
377         dec.start();
378         ByteBuffer[] buf = dec.getInputBuffers();
379         ex.selectTrack(0);
380         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
381         long lastPresentationTimeUsFromExtractor = -1;
382         long lastPresentationTimeUsFromDecoder = -1;
383         boolean inputoutoforder = false;
384         while(true) {
385             int flags = ex.getSampleFlags();
386             long time = ex.getSampleTime();
387             if (time >= 0 && time < lastPresentationTimeUsFromExtractor) {
388                 inputoutoforder = true;
389             }
390             lastPresentationTimeUsFromExtractor = time;
391             int bufidx = dec.dequeueInputBuffer(5000);
392             if (bufidx >= 0) {
393                 int n = ex.readSampleData(buf[bufidx], 0);
394                 if (n < 0) {
395                     flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
396                     time = 0;
397                     n = 0;
398                 }
399                 dec.queueInputBuffer(bufidx, 0, n, time, flags);
400                 ex.advance();
401             }
402             int status = dec.dequeueOutputBuffer(info, 5000);
403             if (status >= 0) {
404                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
405                     break;
406                 }
407                 assertTrue("out of order timestamp from decoder",
408                         info.presentationTimeUs > lastPresentationTimeUsFromDecoder);
409                 dec.releaseOutputBuffer(status, true);
410                 lastPresentationTimeUsFromDecoder = info.presentationTimeUs;
411             }
412         }
413         assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder);
414         dec.release();
415         ex.release();
416         fd.close();
417         return 1;
418       }
419 
420     /**
421      * Test ColorAspects of all the AVC decoders. Decoders should handle
422      * the colors aspects presented in both the mp4 atom 'colr' and VUI
423      * in the bitstream correctly. The following table lists the color
424      * aspects contained in the color box and VUI for the test stream.
425      * P = primaries, T = transfer, M = coeffs, R = range. '-' means
426      * empty value.
427      *                                      |     colr     |    VUI
428      * -------------------------------------------------------------------
429      *         File Name                    |  P  T  M  R  |  P  T  M  R
430      * -------------------------------------------------------------------
431      *  color_176x144_bt709_lr_sdr_h264     |  1  1  1  0  |  -  -  -  -
432      *  color_176x144_bt601_625_fr_sdr_h264 |  1  6  6  0  |  5  2  2  1
433      *  color_176x144_bt601_525_lr_sdr_h264 |  6  5  4  0  |  2  6  6  0
434      *  color_176x144_srgb_lr_sdr_h264      |  2  0  2  1  |  1  13 1  0
435      */
testH264ColorAspects()436     public void testH264ColorAspects() throws Exception {
437         testColorAspects(
438                 R.raw.color_176x144_bt709_lr_sdr_h264, 1 /* testId */,
439                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
440                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
441         testColorAspects(
442                 R.raw.color_176x144_bt601_625_fr_sdr_h264, 2 /* testId */,
443                 MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_PAL,
444                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
445         testColorAspects(
446                 R.raw.color_176x144_bt601_525_lr_sdr_h264, 3 /* testId */,
447                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC,
448                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
449         testColorAspects(
450                 R.raw.color_176x144_srgb_lr_sdr_h264, 4 /* testId */,
451                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
452                 2 /* MediaFormat.COLOR_TRANSFER_SRGB */);
453     }
454 
455     /**
456      * Test ColorAspects of all the HEVC decoders. Decoders should handle
457      * the colors aspects presented in both the mp4 atom 'colr' and VUI
458      * in the bitstream correctly. The following table lists the color
459      * aspects contained in the color box and VUI for the test stream.
460      * P = primaries, T = transfer, M = coeffs, R = range. '-' means
461      * empty value.
462      *                                      |     colr     |    VUI
463      * -------------------------------------------------------------------
464      *         File Name                    |  P  T  M  R  |  P  T  M  R
465      * -------------------------------------------------------------------
466      *  color_176x144_bt709_lr_sdr_h265     |  1  1  1  0  |  -  -  -  -
467      *  color_176x144_bt601_625_fr_sdr_h265 |  1  6  6  0  |  5  2  2  1
468      *  color_176x144_bt601_525_lr_sdr_h265 |  6  5  4  0  |  2  6  6  0
469      *  color_176x144_srgb_lr_sdr_h265      |  2  0  2  1  |  1  13 1  0
470      */
testH265ColorAspects()471     public void testH265ColorAspects() throws Exception {
472         testColorAspects(
473                 R.raw.color_176x144_bt709_lr_sdr_h265, 1 /* testId */,
474                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
475                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
476         testColorAspects(
477                 R.raw.color_176x144_bt601_625_fr_sdr_h265, 2 /* testId */,
478                 MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_PAL,
479                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
480         testColorAspects(
481                 R.raw.color_176x144_bt601_525_lr_sdr_h265, 3 /* testId */,
482                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC,
483                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
484         testColorAspects(
485                 R.raw.color_176x144_srgb_lr_sdr_h265, 4 /* testId */,
486                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
487                 2 /* MediaFormat.COLOR_TRANSFER_SRGB */);
488         // Test the main10 streams with surface as the decoder might
489         // support opaque buffers only.
490         testColorAspects(
491                 R.raw.color_176x144_bt2020_lr_smpte2084_h265, 5 /* testId */,
492                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020,
493                 MediaFormat.COLOR_TRANSFER_ST2084,
494                 getActivity().getSurfaceHolder().getSurface());
495         testColorAspects(
496                 R.raw.color_176x144_bt2020_lr_hlg_h265, 6 /* testId */,
497                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020,
498                 MediaFormat.COLOR_TRANSFER_HLG,
499                 getActivity().getSurfaceHolder().getSurface());
500     }
501 
502     /**
503      * Test ColorAspects of all the MPEG2 decoders if avaiable. Decoders should
504      * handle the colors aspects presented in both the mp4 atom 'colr' and Sequence
505      * in the bitstream correctly. The following table lists the color aspects
506      * contained in the color box and SeqInfo for the test stream.
507      * P = primaries, T = transfer, M = coeffs, R = range. '-' means
508      * empty value.
509      *                                       |     colr     |    SeqInfo
510      * -------------------------------------------------------------------
511      *         File Name                     |  P  T  M  R  |  P  T  M  R
512      * -------------------------------------------------------------------
513      *  color_176x144_bt709_lr_sdr_mpeg2     |  1  1  1  0  |  -  -  -  -
514      *  color_176x144_bt601_625_lr_sdr_mpeg2 |  1  6  6  0  |  5  2  2  0
515      *  color_176x144_bt601_525_lr_sdr_mpeg2 |  6  5  4  0  |  2  6  6  0
516      *  color_176x144_srgb_lr_sdr_mpeg2      |  2  0  2  0  |  1  13 1  0
517      */
testMPEG2ColorAspectsTV()518     public void testMPEG2ColorAspectsTV() throws Exception {
519         testColorAspects(
520                 R.raw.color_176x144_bt709_lr_sdr_mpeg2, 1 /* testId */,
521                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
522                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
523         testColorAspects(
524                 R.raw.color_176x144_bt601_625_lr_sdr_mpeg2, 2 /* testId */,
525                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_PAL,
526                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
527         testColorAspects(
528                 R.raw.color_176x144_bt601_525_lr_sdr_mpeg2, 3 /* testId */,
529                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC,
530                 MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
531         testColorAspects(
532                 R.raw.color_176x144_srgb_lr_sdr_mpeg2, 4 /* testId */,
533                 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
534                 2 /* MediaFormat.COLOR_TRANSFER_SRGB */);
535     }
536 
testColorAspects( int res, int testId, int expectRange, int expectStandard, int expectTransfer)537     private void testColorAspects(
538             int res, int testId, int expectRange, int expectStandard, int expectTransfer)
539             throws Exception {
540         testColorAspects(
541                 res, testId, expectRange, expectStandard, expectTransfer, null /*surface*/);
542     }
543 
testColorAspects( int res, int testId, int expectRange, int expectStandard, int expectTransfer, Surface surface)544     private void testColorAspects(
545             int res, int testId, int expectRange, int expectStandard, int expectTransfer,
546             Surface surface) throws Exception {
547         MediaFormat format = MediaUtils.getTrackFormatForResource(mContext, res, "video");
548         MediaFormat mimeFormat = new MediaFormat();
549         mimeFormat.setString(MediaFormat.KEY_MIME, format.getString(MediaFormat.KEY_MIME));
550 
551         for (String decoderName: MediaUtils.getDecoderNames(mimeFormat)) {
552             if (!MediaUtils.supports(decoderName, format)) {
553                 MediaUtils.skipTest(decoderName + " cannot play resource " + res);
554             } else {
555                 testColorAspects(decoderName, res, testId,
556                         expectRange, expectStandard, expectTransfer, surface);
557             }
558         }
559     }
560 
testColorAspects( String decoderName, int res, int testId, int expectRange, int expectStandard, int expectTransfer, Surface surface)561     private void testColorAspects(
562             String decoderName, int res, int testId, int expectRange,
563             int expectStandard, int expectTransfer, Surface surface) throws Exception {
564         AssetFileDescriptor fd = mResources.openRawResourceFd(res);
565         MediaExtractor ex = new MediaExtractor();
566         ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
567         MediaFormat format = ex.getTrackFormat(0);
568         MediaCodec dec = MediaCodec.createByCodecName(decoderName);
569         dec.configure(format, surface, null, 0);
570         dec.start();
571         ByteBuffer[] buf = dec.getInputBuffers();
572         ex.selectTrack(0);
573         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
574         boolean sawInputEOS = false;
575         boolean getOutputFormat = false;
576         boolean rangeMatch = false;
577         boolean colorMatch = false;
578         boolean transferMatch = false;
579         int colorRange = 0;
580         int colorStandard = 0;
581         int colorTransfer = 0;
582 
583         while (true) {
584             if (!sawInputEOS) {
585                 int flags = ex.getSampleFlags();
586                 long time = ex.getSampleTime();
587                 int bufidx = dec.dequeueInputBuffer(200 * 1000);
588                 if (bufidx >= 0) {
589                     int n = ex.readSampleData(buf[bufidx], 0);
590                     if (n < 0) {
591                         flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
592                         sawInputEOS = true;
593                         n = 0;
594                     }
595                     dec.queueInputBuffer(bufidx, 0, n, time, flags);
596                     ex.advance();
597                 } else {
598                     assertEquals(
599                             "codec.dequeueInputBuffer() unrecognized return value: " + bufidx,
600                             MediaCodec.INFO_TRY_AGAIN_LATER, bufidx);
601                 }
602             }
603 
604             int status = dec.dequeueOutputBuffer(info, sawInputEOS ? 3000 * 1000 : 100 * 1000);
605             if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
606                 MediaFormat fmt = dec.getOutputFormat();
607                 colorRange = fmt.containsKey("color-range") ? fmt.getInteger("color-range") : 0;
608                 colorStandard = fmt.containsKey("color-standard") ? fmt.getInteger("color-standard") : 0;
609                 colorTransfer = fmt.containsKey("color-transfer") ? fmt.getInteger("color-transfer") : 0;
610                 rangeMatch = colorRange == expectRange;
611                 colorMatch = colorStandard == expectStandard;
612                 transferMatch = colorTransfer == expectTransfer;
613                 getOutputFormat = true;
614                 // Test only needs to check the color format in the first format changed event.
615                 break;
616             } else if (status >= 0) {
617                 // Test should get at least one format changed event before getting first frame.
618                 assertTrue(getOutputFormat == true);
619                 break;
620             } else {
621                 assertFalse(
622                         "codec.dequeueOutputBuffer() timeout after seeing input EOS",
623                         status == MediaCodec.INFO_TRY_AGAIN_LATER && sawInputEOS);
624             }
625         }
626 
627         String reportName = decoderName + "_colorAspectsTest Test " + testId +
628                 " (Get R: " + colorRange + " S: " + colorStandard + " T: " + colorTransfer + ")" +
629                 " (Expect R: " + expectRange + " S: " + expectStandard + " T: " + expectTransfer + ")";
630         Log.d(TAG, reportName);
631 
632         DeviceReportLog log = new DeviceReportLog("CtsMediaTestCases", "color_aspects_test");
633         log.addValue("decoder_name", decoderName, ResultType.NEUTRAL, ResultUnit.NONE);
634         log.addValue("test_id", testId, ResultType.NEUTRAL, ResultUnit.NONE);
635         log.addValues(
636                 "rst_actual", new int[] { colorRange, colorStandard, colorTransfer },
637                 ResultType.NEUTRAL, ResultUnit.NONE);
638         log.addValues(
639                 "rst_expected", new int[] { expectRange, expectStandard, expectTransfer },
640                 ResultType.NEUTRAL, ResultUnit.NONE);
641 
642         if (rangeMatch && colorMatch && transferMatch) {
643             log.setSummary("result", 1, ResultType.HIGHER_BETTER, ResultUnit.COUNT);
644         } else {
645             log.setSummary("result", 0, ResultType.HIGHER_BETTER, ResultUnit.COUNT);
646         }
647         log.submit(getInstrumentation());
648 
649         assertTrue(rangeMatch && colorMatch && transferMatch);
650 
651         dec.release();
652         ex.release();
653         fd.close();
654     }
655 
testTrackSelection(int resid)656     private void testTrackSelection(int resid) throws Exception {
657         AssetFileDescriptor fd1 = null;
658         MediaExtractor ex1 = new MediaExtractor();
659         try {
660             fd1 = mResources.openRawResourceFd(resid);
661             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
662 
663             ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
664             ArrayList<Integer> vid = new ArrayList<Integer>();
665             ArrayList<Integer> aud = new ArrayList<Integer>();
666 
667             // scan the file once and build lists of audio and video samples
668             ex1.selectTrack(0);
669             ex1.selectTrack(1);
670             while(true) {
671                 int n1 = ex1.readSampleData(buf1, 0);
672                 if (n1 < 0) {
673                     break;
674                 }
675                 int idx = ex1.getSampleTrackIndex();
676                 if (idx == 0) {
677                     vid.add(n1);
678                 } else if (idx == 1) {
679                     aud.add(n1);
680                 } else {
681                     fail("unexpected track index: " + idx);
682                 }
683                 ex1.advance();
684             }
685 
686             // read the video track once, then rewind and do it again, and
687             // verify we get the right samples
688             ex1.release();
689             ex1 = new MediaExtractor();
690             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
691             ex1.selectTrack(0);
692             for (int i = 0; i < 2; i++) {
693                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
694                 int idx = 0;
695                 while(true) {
696                     int n1 = ex1.readSampleData(buf1, 0);
697                     if (n1 < 0) {
698                         assertEquals(vid.size(), idx);
699                         break;
700                     }
701                     assertEquals(vid.get(idx++).intValue(), n1);
702                     ex1.advance();
703                 }
704             }
705 
706             // read the audio track once, then rewind and do it again, and
707             // verify we get the right samples
708             ex1.release();
709             ex1 = new MediaExtractor();
710             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
711             ex1.selectTrack(1);
712             for (int i = 0; i < 2; i++) {
713                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
714                 int idx = 0;
715                 while(true) {
716                     int n1 = ex1.readSampleData(buf1, 0);
717                     if (n1 < 0) {
718                         assertEquals(aud.size(), idx);
719                         break;
720                     }
721                     assertEquals(aud.get(idx++).intValue(), n1);
722                     ex1.advance();
723                 }
724             }
725 
726             // read the video track first, then rewind and get the audio track instead, and
727             // verify we get the right samples
728             ex1.release();
729             ex1 = new MediaExtractor();
730             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
731             for (int i = 0; i < 2; i++) {
732                 ex1.selectTrack(i);
733                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
734                 int idx = 0;
735                 while(true) {
736                     int n1 = ex1.readSampleData(buf1, 0);
737                     if (i == 0) {
738                         if (n1 < 0) {
739                             assertEquals(vid.size(), idx);
740                             break;
741                         }
742                         assertEquals(vid.get(idx++).intValue(), n1);
743                     } else if (i == 1) {
744                         if (n1 < 0) {
745                             assertEquals(aud.size(), idx);
746                             break;
747                         }
748                         assertEquals(aud.get(idx++).intValue(), n1);
749                     } else {
750                         fail("unexpected track index: " + idx);
751                     }
752                     ex1.advance();
753                 }
754                 ex1.unselectTrack(i);
755             }
756 
757             // read the video track first, then rewind, enable the audio track in addition
758             // to the video track, and verify we get the right samples
759             ex1.release();
760             ex1 = new MediaExtractor();
761             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
762             for (int i = 0; i < 2; i++) {
763                 ex1.selectTrack(i);
764                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
765                 int vididx = 0;
766                 int audidx = 0;
767                 while(true) {
768                     int n1 = ex1.readSampleData(buf1, 0);
769                     if (n1 < 0) {
770                         // we should have read all audio and all video samples at this point
771                         assertEquals(vid.size(), vididx);
772                         if (i == 1) {
773                             assertEquals(aud.size(), audidx);
774                         }
775                         break;
776                     }
777                     int trackidx = ex1.getSampleTrackIndex();
778                     if (trackidx == 0) {
779                         assertEquals(vid.get(vididx++).intValue(), n1);
780                     } else if (trackidx == 1) {
781                         assertEquals(aud.get(audidx++).intValue(), n1);
782                     } else {
783                         fail("unexpected track index: " + trackidx);
784                     }
785                     ex1.advance();
786                 }
787             }
788 
789             // read both tracks from the start, then rewind and verify we get the right
790             // samples both times
791             ex1.release();
792             ex1 = new MediaExtractor();
793             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
794             for (int i = 0; i < 2; i++) {
795                 ex1.selectTrack(0);
796                 ex1.selectTrack(1);
797                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
798                 int vididx = 0;
799                 int audidx = 0;
800                 while(true) {
801                     int n1 = ex1.readSampleData(buf1, 0);
802                     if (n1 < 0) {
803                         // we should have read all audio and all video samples at this point
804                         assertEquals(vid.size(), vididx);
805                         assertEquals(aud.size(), audidx);
806                         break;
807                     }
808                     int trackidx = ex1.getSampleTrackIndex();
809                     if (trackidx == 0) {
810                         assertEquals(vid.get(vididx++).intValue(), n1);
811                     } else if (trackidx == 1) {
812                         assertEquals(aud.get(audidx++).intValue(), n1);
813                     } else {
814                         fail("unexpected track index: " + trackidx);
815                     }
816                     ex1.advance();
817                 }
818             }
819 
820         } finally {
821             if (ex1 != null) {
822                 ex1.release();
823             }
824             if (fd1 != null) {
825                 fd1.close();
826             }
827         }
828     }
829 
testVp9HdrStaticMetadata()830     public void testVp9HdrStaticMetadata() throws Exception {
831         final String staticInfo =
832                 "00 d0 84 80 3e c2 33 c4  86 4c 1d b8 0b 13 3d 42" +
833                 "40 e8 03 64 00 e8 03 2c  01                     " ;
834         testHdrStaticMetadata(R.raw.video_1280x720_vp9_hdr_static_3mbps,
835                 staticInfo, true /*metadataInContainer*/);
836     }
837 
testAV1HdrStaticMetadata()838     public void testAV1HdrStaticMetadata() throws Exception {
839         final String staticInfo =
840                 "00 d0 84 80 3e c2 33 c4  86 4c 1d b8 0b 13 3d 42" +
841                 "40 e8 03 64 00 e8 03 2c  01                     " ;
842         testHdrStaticMetadata(R.raw.video_1280x720_av1_hdr_static_3mbps,
843                 staticInfo, false /*metadataInContainer*/);
844     }
845 
testH265HDR10StaticMetadata()846     public void testH265HDR10StaticMetadata() throws Exception {
847         // Expected value of MediaFormat.KEY_HDR_STATIC_INFO key.
848         // The associated value is a ByteBuffer. This buffer contains the raw contents of the
849         // Static Metadata Descriptor (including the descriptor ID) of an HDMI Dynamic Range and
850         // Mastering InfoFrame as defined by CTA-861.3.
851         // Media frameworks puts the display primaries in RGB order, here we verify the three
852         // primaries are indeed in this order and fail otherwise.
853         final String staticInfo =
854                 "00 d0 84 80 3e c2 33 c4  86 4c 1d b8 0b 13 3d 42" +
855                 "40 e8 03 00 00 e8 03 90  01                     " ;
856         testHdrStaticMetadata(R.raw.video_1280x720_hevc_hdr10_static_3mbps,
857                 staticInfo, false /*metadataInContainer*/);
858     }
859 
testHdrStaticMetadata(int res, String pattern, boolean metadataInContainer)860     private void testHdrStaticMetadata(int res, String pattern, boolean metadataInContainer)
861             throws Exception {
862         AssetFileDescriptor infd = null;
863         MediaExtractor extractor = null;
864 
865         try {
866             infd = mResources.openRawResourceFd(res);
867             extractor = new MediaExtractor();
868             extractor.setDataSource(infd.getFileDescriptor(),
869                     infd.getStartOffset(), infd.getLength());
870 
871             MediaFormat format = null;
872             int trackIndex = -1;
873             for (int i = 0; i < extractor.getTrackCount(); i++) {
874                 format = extractor.getTrackFormat(i);
875                 if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
876                     trackIndex = i;
877                     break;
878                 }
879             }
880 
881             assertTrue("Extractor failed to extract video track",
882                     format != null && trackIndex >= 0);
883             if (metadataInContainer) {
884                 verifyHdrStaticInfo("Extractor failed to extract static info", format, pattern);
885             }
886 
887             extractor.selectTrack(trackIndex);
888             Log.v(TAG, "format " + format);
889 
890             String mime = format.getString(MediaFormat.KEY_MIME);
891             // setting profile and level
892             if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
893                 assertEquals("Extractor set wrong profile",
894                         MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10,
895                         format.getInteger(MediaFormat.KEY_PROFILE));
896             } else if (MediaFormat.MIMETYPE_VIDEO_VP9.equals(mime)) {
897                 // The muxer might not have put VP9 CSD in the mkv, we manually patch
898                 // it here so that we only test HDR when decoder supports it.
899                 format.setInteger(MediaFormat.KEY_PROFILE,
900                         MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR);
901             } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mime)) {
902                 // The muxer might not have put AV1 CSD in the webm, we manually patch
903                 // it here so that we only test HDR when decoder supports it.
904                 format.setInteger(MediaFormat.KEY_PROFILE,
905                         MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10);
906             } else {
907                 fail("Codec " + mime + " shouldn't be tested with this test!");
908             }
909             String[] decoderNames = MediaUtils.getDecoderNames(format);
910 
911             if (decoderNames == null || decoderNames.length == 0) {
912                 MediaUtils.skipTest("No video codecs supports HDR");
913                 return;
914             }
915 
916             final Surface surface = getActivity().getSurfaceHolder().getSurface();
917             final MediaExtractor finalExtractor = extractor;
918 
919             for (String name : decoderNames) {
920                 Log.d(TAG, "Testing candicate decoder " + name);
921                 CountDownLatch latch = new CountDownLatch(1);
922                 extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
923 
924                 MediaCodec decoder = MediaCodec.createByCodecName(name);
925                 decoder.setCallback(new MediaCodec.Callback() {
926                     boolean mInputEOS;
927                     boolean mOutputReceived;
928 
929                     @Override
930                     public void onOutputBufferAvailable(
931                             MediaCodec codec, int index, BufferInfo info) {
932                         if (mOutputReceived) {
933                             return;
934                         }
935 
936                         MediaFormat bufferFormat = codec.getOutputFormat(index);
937                         Log.i(TAG, "got output buffer: format " + bufferFormat);
938 
939                         codec.releaseOutputBuffer(index,  false);
940                         verifyHdrStaticInfo("Output buffer has wrong static info",
941                                 bufferFormat, pattern);
942                         mOutputReceived = true;
943                         latch.countDown();
944                     }
945 
946                     @Override
947                     public void onInputBufferAvailable(MediaCodec codec, int index) {
948                         // keep queuing until intput EOS, or first output buffer received.
949                         if (mInputEOS || mOutputReceived) {
950                             return;
951                         }
952 
953                         ByteBuffer inputBuffer = codec.getInputBuffer(index);
954 
955                         if (finalExtractor.getSampleTrackIndex() == -1) {
956                             codec.queueInputBuffer(
957                                     index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
958                             mInputEOS = true;
959                         } else {
960                             int size = finalExtractor.readSampleData(inputBuffer, 0);
961                             long timestamp = finalExtractor.getSampleTime();
962                             finalExtractor.advance();
963                             codec.queueInputBuffer(index, 0, size, timestamp, 0);
964                         }
965                     }
966 
967                     @Override
968                     public void onError(MediaCodec codec, MediaCodec.CodecException e) {
969                         Log.i(TAG, "got codec exception", e);
970                         fail("received codec error during decode" + e);
971                     }
972 
973                     @Override
974                     public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
975                         Log.i(TAG, "got output format: " + format);
976                         verifyHdrStaticInfo("Output format has wrong static info",
977                                 format, pattern);
978                     }
979                 });
980                 decoder.configure(format, surface, null/*crypto*/, 0/*flags*/);
981                 decoder.start();
982                 try {
983                     assertTrue(latch.await(2000, TimeUnit.MILLISECONDS));
984                 } catch (InterruptedException e) {
985                     fail("playback interrupted");
986                 }
987                 decoder.stop();
988                 decoder.release();
989             }
990         } finally {
991             if (extractor != null) {
992                 extractor.release();
993             }
994             if (infd != null) {
995                 infd.close();
996             }
997         }
998     }
999 
verifyHdrStaticInfo(String reason, MediaFormat format, String pattern)1000     private void verifyHdrStaticInfo(String reason, MediaFormat format, String pattern) {
1001         ByteBuffer staticMetadataBuffer = format.containsKey("hdr-static-info") ?
1002                 format.getByteBuffer("hdr-static-info") : null;
1003         assertTrue(reason + ": empty",
1004                 staticMetadataBuffer != null && staticMetadataBuffer.remaining() > 0);
1005         assertTrue(reason + ": mismatch",
1006                 Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array()));
1007     }
1008 
1009     // helper to load byte[] from a String
loadByteArrayFromString(final String str)1010     private byte[] loadByteArrayFromString(final String str) {
1011         Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}");
1012         Matcher matcher = pattern.matcher(str);
1013         // allocate a large enough byte array first
1014         byte[] tempArray = new byte[str.length() / 2];
1015         int i = 0;
1016         while (matcher.find()) {
1017           tempArray[i++] = (byte)Integer.parseInt(matcher.group(), 16);
1018         }
1019         return Arrays.copyOfRange(tempArray, 0, i);
1020     }
1021 
testDecodeFragmented()1022     public void testDecodeFragmented() throws Exception {
1023         testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
1024                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
1025         testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
1026                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
1027     }
1028 
testDecodeFragmented(int reference, int teststream)1029     private void testDecodeFragmented(int reference, int teststream) throws Exception {
1030         AssetFileDescriptor fd1 = null;
1031         AssetFileDescriptor fd2 = null;
1032         try {
1033             fd1 = mResources.openRawResourceFd(reference);
1034             MediaExtractor ex1 = new MediaExtractor();
1035             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
1036 
1037             fd2 = mResources.openRawResourceFd(teststream);
1038             MediaExtractor ex2 = new MediaExtractor();
1039             ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength());
1040 
1041             assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount());
1042 
1043             ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
1044             ByteBuffer buf2 = ByteBuffer.allocate(1024*1024);
1045 
1046             for (int i = 0; i < ex1.getTrackCount(); i++) {
1047                 // note: this assumes the tracks are reported in the order in which they appear
1048                 // in the file.
1049                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
1050                 ex1.selectTrack(i);
1051                 ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
1052                 ex2.selectTrack(i);
1053 
1054                 while(true) {
1055                     int n1 = ex1.readSampleData(buf1, 0);
1056                     int n2 = ex2.readSampleData(buf2, 0);
1057                     assertEquals("different buffer size on track " + i, n1, n2);
1058 
1059                     if (n1 < 0) {
1060                         break;
1061                     }
1062                     // see bug 13008204
1063                     buf1.limit(n1);
1064                     buf2.limit(n2);
1065                     buf1.rewind();
1066                     buf2.rewind();
1067 
1068                     assertEquals("limit does not match return value on track " + i,
1069                             n1, buf1.limit());
1070                     assertEquals("limit does not match return value on track " + i,
1071                             n2, buf2.limit());
1072 
1073                     assertEquals("buffer data did not match on track " + i, buf1, buf2);
1074 
1075                     ex1.advance();
1076                     ex2.advance();
1077                 }
1078                 ex1.unselectTrack(i);
1079                 ex2.unselectTrack(i);
1080             }
1081         } finally {
1082             if (fd1 != null) {
1083                 fd1.close();
1084             }
1085             if (fd2 != null) {
1086                 fd2.close();
1087             }
1088         }
1089     }
1090 
1091     /**
1092      * Verify correct decoding of MPEG-4 AAC-LC mono and stereo streams
1093      */
testDecodeAacLcM4a()1094     public void testDecodeAacLcM4a() throws Exception {
1095         // mono
1096         decodeNtest(R.raw.sinesweep1_1ch_8khz_aot2_mp4, 40.f);
1097         decodeNtest(R.raw.sinesweep1_1ch_11khz_aot2_mp4, 40.f);
1098         decodeNtest(R.raw.sinesweep1_1ch_12khz_aot2_mp4, 40.f);
1099         decodeNtest(R.raw.sinesweep1_1ch_16khz_aot2_mp4, 40.f);
1100         decodeNtest(R.raw.sinesweep1_1ch_22khz_aot2_mp4, 40.f);
1101         decodeNtest(R.raw.sinesweep1_1ch_24khz_aot2_mp4, 40.f);
1102         decodeNtest(R.raw.sinesweep1_1ch_32khz_aot2_mp4, 40.f);
1103         decodeNtest(R.raw.sinesweep1_1ch_44khz_aot2_mp4, 40.f);
1104         decodeNtest(R.raw.sinesweep1_1ch_48khz_aot2_mp4, 40.f);
1105         // stereo
1106         decodeNtest(R.raw.sinesweep_2ch_8khz_aot2_mp4, 40.f);
1107         decodeNtest(R.raw.sinesweep_2ch_11khz_aot2_mp4, 40.f);
1108         decodeNtest(R.raw.sinesweep_2ch_12khz_aot2_mp4, 40.f);
1109         decodeNtest(R.raw.sinesweep_2ch_16khz_aot2_mp4, 40.f);
1110         decodeNtest(R.raw.sinesweep_2ch_22khz_aot2_mp4, 40.f);
1111         decodeNtest(R.raw.sinesweep_2ch_24khz_aot2_mp4, 40.f);
1112         decodeNtest(R.raw.sinesweep_2ch_32khz_aot2_mp4, 40.f);
1113         decodeNtest(R.raw.sinesweep_2ch_44khz_aot2_mp4, 40.f);
1114         decodeNtest(R.raw.sinesweep_2ch_48khz_aot2_mp4, 40.f);
1115     }
1116 
1117     /**
1118      * Verify correct decoding of MPEG-4 AAC-LC 5.0 and 5.1 channel streams
1119      */
testDecodeAacLcMcM4a()1120     public void testDecodeAacLcMcM4a() throws Exception {
1121         AudioParameter decParams = new AudioParameter();
1122         short[] decSamples = decodeToMemory(decParams, R.raw.noise_6ch_48khz_aot2_mp4,
1123                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1124         checkEnergy(decSamples, decParams, 6);
1125         decParams.reset();
1126 
1127         decSamples = decodeToMemory(decParams, R.raw.noise_5ch_44khz_aot2_mp4,
1128                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1129         checkEnergy(decSamples, decParams, 5);
1130         decParams.reset();
1131     }
1132 
1133     /**
1134      * Verify correct decoding of MPEG-4 HE-AAC mono and stereo streams
1135      */
testDecodeHeAacM4a()1136     public void testDecodeHeAacM4a() throws Exception {
1137         AudioParameter decParams = new AudioParameter();
1138         // mono
1139         short[] decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot5_dr_sbr_sig1_mp4,
1140                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1141         checkEnergy(decSamples, decParams, 1);
1142         decParams.reset();
1143 
1144         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot5_ds_sbr_sig1_mp4,
1145                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1146         checkEnergy(decSamples, decParams, 1);
1147         decParams.reset();
1148 
1149         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_32khz_aot5_dr_sbr_sig2_mp4,
1150                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1151         checkEnergy(decSamples, decParams, 1);
1152         decParams.reset();
1153 
1154         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot5_dr_sbr_sig0_mp4,
1155                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1156         checkEnergy(decSamples, decParams, 1);
1157         decParams.reset();
1158 
1159         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot5_ds_sbr_sig2_mp4,
1160                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1161         checkEnergy(decSamples, decParams, 1);
1162         decParams.reset();
1163 
1164         // stereo
1165         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_24khz_aot5_dr_sbr_sig2_mp4,
1166                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1167         checkEnergy(decSamples, decParams, 2);
1168         decParams.reset();
1169 
1170         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_32khz_aot5_ds_sbr_sig2_mp4,
1171                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1172         checkEnergy(decSamples, decParams, 2);
1173         decParams.reset();
1174 
1175         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot5_dr_sbr_sig1_mp4,
1176                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1177         checkEnergy(decSamples, decParams, 2);
1178         decParams.reset();
1179 
1180         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot5_ds_sbr_sig1_mp4,
1181                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1182         checkEnergy(decSamples, decParams, 2);
1183         decParams.reset();
1184     }
1185 
1186     /**
1187      * Verify correct decoding of MPEG-4 HE-AAC 5.0 and 5.1 channel streams
1188      */
testDecodeHeAacMcM4a()1189     public void testDecodeHeAacMcM4a() throws Exception {
1190         AudioParameter decParams = new AudioParameter();
1191         short[] decSamples = decodeToMemory(decParams, R.raw.noise_5ch_48khz_aot5_dr_sbr_sig1_mp4,
1192                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1193         checkEnergy(decSamples, decParams, 5);
1194         decParams.reset();
1195 
1196         decSamples = decodeToMemory(decParams, R.raw.noise_6ch_44khz_aot5_dr_sbr_sig2_mp4,
1197                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1198         checkEnergy(decSamples, decParams, 6);
1199         decParams.reset();
1200     }
1201 
1202     /**
1203      * Verify correct decoding of MPEG-4 HE-AAC v2 stereo streams
1204      */
testDecodeHeAacV2M4a()1205     public void testDecodeHeAacV2M4a() throws Exception {
1206         AudioParameter decParams = new AudioParameter();
1207         short[] decSamples = decodeToMemory(decParams, R.raw.noise_2ch_24khz_aot29_dr_sbr_sig0_mp4,
1208                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1209         checkEnergy(decSamples, decParams, 2);
1210 
1211         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_44khz_aot29_dr_sbr_sig1_mp4,
1212                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1213         checkEnergy(decSamples, decParams, 2);
1214 
1215         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot29_dr_sbr_sig2_mp4,
1216                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1217         checkEnergy(decSamples, decParams, 2);
1218     }
1219 
1220     /**
1221      * Verify correct decoding of MPEG-4 AAC-ELD mono and stereo streams
1222      */
testDecodeAacEldM4a()1223     public void testDecodeAacEldM4a() throws Exception {
1224         // mono
1225         decodeNtest(R.raw.sinesweep1_1ch_16khz_aot39_fl480_mp4, 40.f);
1226         decodeNtest(R.raw.sinesweep1_1ch_22khz_aot39_fl512_mp4, 40.f);
1227         decodeNtest(R.raw.sinesweep1_1ch_24khz_aot39_fl480_mp4, 40.f);
1228         decodeNtest(R.raw.sinesweep1_1ch_32khz_aot39_fl512_mp4, 40.f);
1229         decodeNtest(R.raw.sinesweep1_1ch_44khz_aot39_fl480_mp4, 40.f);
1230         decodeNtest(R.raw.sinesweep1_1ch_48khz_aot39_fl512_mp4, 40.f);
1231 
1232         // stereo
1233         decodeNtest(R.raw.sinesweep_2ch_16khz_aot39_fl512_mp4, 40.f);
1234         decodeNtest(R.raw.sinesweep_2ch_22khz_aot39_fl480_mp4, 40.f);
1235         decodeNtest(R.raw.sinesweep_2ch_24khz_aot39_fl512_mp4, 40.f);
1236         decodeNtest(R.raw.sinesweep_2ch_32khz_aot39_fl480_mp4, 40.f);
1237         decodeNtest(R.raw.sinesweep_2ch_44khz_aot39_fl512_mp4, 40.f);
1238         decodeNtest(R.raw.sinesweep_2ch_48khz_aot39_fl480_mp4, 40.f);
1239 
1240         AudioParameter decParams = new AudioParameter();
1241         // mono
1242         short[] decSamples = decodeToMemory(decParams, R.raw.noise_1ch_16khz_aot39_ds_sbr_fl512_mp4,
1243                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1244         checkEnergy(decSamples, decParams, 1);
1245         decParams.reset();
1246 
1247         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot39_ds_sbr_fl512_mp4,
1248                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1249         checkEnergy(decSamples, decParams, 1);
1250         decParams.reset();
1251 
1252         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_32khz_aot39_dr_sbr_fl480_mp4,
1253                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1254         checkEnergy(decSamples, decParams, 1);
1255         decParams.reset();
1256 
1257         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot39_ds_sbr_fl512_mp4,
1258                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1259         checkEnergy(decSamples, decParams, 1);
1260         decParams.reset();
1261 
1262         decSamples = decodeToMemory(decParams, R.raw.noise_1ch_48khz_aot39_dr_sbr_fl480_mp4,
1263                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1264         checkEnergy(decSamples, decParams, 1);
1265         decParams.reset();
1266 
1267         // stereo
1268         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_22khz_aot39_ds_sbr_fl512_mp4,
1269                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1270         checkEnergy(decSamples, decParams, 2);
1271         decParams.reset();
1272 
1273         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_32khz_aot39_ds_sbr_fl512_mp4,
1274                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1275         checkEnergy(decSamples, decParams, 2);
1276         decParams.reset();
1277 
1278         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_44khz_aot39_dr_sbr_fl480_mp4,
1279                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1280         checkEnergy(decSamples, decParams, 2);
1281         decParams.reset();
1282 
1283         decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot39_ds_sbr_fl512_mp4,
1284                 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1285         checkEnergy(decSamples, decParams, 2);
1286         decParams.reset();
1287     }
1288 
1289     /**
1290      * Perform a segmented energy analysis on given audio signal samples and run several tests on
1291      * the energy values.
1292      *
1293      * The main purpose is to verify whether an AAC decoder implementation applies Spectral Band
1294      * Replication (SBR) and Parametric Stereo (PS) correctly. Both tools are inherent parts to the
1295      * MPEG-4 HE-AAC and HE-AAC v2 audio codecs.
1296      *
1297      * In addition, this test can verify the correct decoding of multi-channel (e.g. 5.1 channel)
1298      * streams or the creation of a mixdown signal.
1299      *
1300      * Note: This test procedure is not an MPEG Conformance Test and can not serve as a replacement.
1301      *
1302      * @param decSamples the decoded audio samples to be tested
1303      * @param decParams the audio parameters of the given audio samples (decSamples)
1304      * @param encNch the encoded number of audio channels (number of channels of the original
1305      *               input)
1306      * @param nrgRatioThresh threshold to classify the energy ratios ]0.0, 1.0[
1307      * @throws RuntimeException
1308      */
checkEnergy(short[] decSamples, AudioParameter decParams, int encNch, float nrgRatioThresh)1309     protected void checkEnergy(short[] decSamples, AudioParameter decParams, int encNch,
1310                              float nrgRatioThresh) throws RuntimeException
1311     {
1312         final int nSegPerBlk = 4;                          // the number of segments per block
1313         final int nCh = decParams.getNumChannels();        // the number of input channels
1314         final int nBlkSmp = decParams.getSamplingRate();   // length of one (LB/HB) block [samples]
1315         final int nSegSmp = nBlkSmp / nSegPerBlk;          // length of one segment [samples]
1316         final int smplPerChan = decSamples.length / nCh;   // actual # samples per channel (total)
1317 
1318         final int nSegSmpTot = nSegSmp * nCh;              // actual # samples per segment (all ch)
1319         final int nSegChOffst = 2 * nSegPerBlk;            // signal offset between chans [segments]
1320         final int procNch = Math.min(nCh, encNch);         // the number of channels to be analyzed
1321         if (encNch > 4) {
1322             assertTrue(String.format("multichannel content (%dch) was downmixed (%dch)",
1323                     encNch, nCh), procNch > 4);
1324         }
1325         assertTrue(String.format("got less channels(%d) than encoded (%d)", nCh, encNch),
1326                 nCh >= encNch);
1327 
1328         final int encEffNch = (encNch > 5) ? encNch-1 : encNch;  // all original configs with more
1329                                                            // ... than five channel have an LFE */
1330         final int expSmplPerChan = Math.max(encEffNch, 2) * nSegChOffst * nSegSmp;
1331         final boolean isDmx = nCh < encNch;                // flag telling that input is dmx signal
1332         int effProcNch = procNch;                          // the num analyzed channels with signal
1333 
1334         assertTrue("got less input samples than expected", smplPerChan >= expSmplPerChan);
1335 
1336         // get the signal offset by counting zero samples at the very beginning (over all channels)
1337         final int zeroSigThresh = 1;                     // sample value threshold for signal search
1338         int signalStart = smplPerChan;                   // receives the number of samples that
1339                                                          // ... are in front of the actual signal
1340         int noiseStart = signalStart;                    // receives the number of null samples
1341                                                          // ... (per chan) at the very beginning
1342         for (int smpl = 0; smpl < decSamples.length; smpl++) {
1343             int value = Math.abs(decSamples[smpl]);
1344             if (value > 0 && noiseStart == signalStart) {
1345                 noiseStart = smpl / nCh;                   // store start of prepended noise
1346             }                                              // ... (can be same as signalStart)
1347             if (value > zeroSigThresh) {
1348                 signalStart = smpl / nCh;                  // store signal start offset [samples]
1349                 break;
1350             }
1351         }
1352         signalStart = (signalStart > noiseStart+1) ? signalStart : noiseStart;
1353         assertTrue ("no signal found in any channel!", signalStart < smplPerChan);
1354         final int totSeg = (smplPerChan-signalStart) / nSegSmp; // max num seg that fit into signal
1355         final int totSmp = nSegSmp * totSeg;               // max num relevant samples (per channel)
1356         assertTrue("no segments left to test after signal search", totSeg > 0);
1357 
1358         // get the energies and the channel offsets by searching for the first segment above the
1359         //  energy threshold
1360         final double zeroMaxNrgRatio = 0.001f;             // ratio of zeroNrgThresh to the max nrg
1361         double zeroNrgThresh = nSegSmp * nSegSmp;          // threshold to classify segment energies
1362         double totMaxNrg = 0.0f;                           // will store the max seg nrg over all ch
1363         double[][] nrg = new double[procNch][totSeg];      // array receiving the segment energies
1364         int[] offset = new int[procNch];                   // array for channel offsets
1365         boolean[] sigSeg = new boolean[totSeg];            // array receiving the segment ...
1366                                                            // ... energy status over all channels
1367         for (int ch = 0; ch < procNch; ch++) {
1368             offset[ch] = -1;
1369             for (int seg = 0; seg < totSeg; seg++) {
1370                 final int smpStart = (signalStart * nCh) + (seg * nSegSmpTot) + ch;
1371                 final int smpStop = smpStart + nSegSmpTot;
1372                 for (int smpl = smpStart; smpl < smpStop; smpl += nCh) {
1373                     nrg[ch][seg] += decSamples[smpl] * decSamples[smpl];  // accumulate segment nrg
1374                 }
1375                 if (nrg[ch][seg] > zeroNrgThresh && offset[ch] < 0) { // store 1st segment (index)
1376                     offset[ch] = seg / nSegChOffst;        // ... per ch which has energy above the
1377                 }                                          // ... threshold to get the ch offsets
1378                 if (nrg[ch][seg] > totMaxNrg) {
1379                     totMaxNrg = nrg[ch][seg];              // store the max segment nrg over all ch
1380                 }
1381                 sigSeg[seg] |= nrg[ch][seg] > zeroNrgThresh;  // store whether the channel has
1382                                                            // ... energy in this segment
1383             }
1384             if (offset[ch] < 0) {                          // if one channel has no signal it is
1385                 effProcNch -= 1;                           // ... most probably the LFE
1386                 offset[ch] = effProcNch;                   // the LFE is no effective channel
1387             }
1388             if (ch == 0) {                                 // recalculate the zero signal threshold
1389                 zeroNrgThresh = zeroMaxNrgRatio * totMaxNrg; // ... based on the 1st channels max
1390             }                                              // ... energy for all subsequent checks
1391         }
1392         // check the channel mapping
1393         assertTrue("more than one LFE detected", effProcNch >= procNch - 1);
1394         assertTrue(String.format("less samples decoded than expected: %d < %d",
1395                 decSamples.length-(signalStart * nCh), totSmp * effProcNch),
1396                 decSamples.length-(signalStart * nCh) >= totSmp * effProcNch);
1397         if (procNch >= 5) {                                // for multi-channel signals the only
1398             final int[] frontChMap1 = {2, 0, 1};           // valid front channel orders are L, R, C
1399             final int[] frontChMap2 = {0, 1, 2};           // or C, L, R (L=left, R=right, C=center)
1400             if ( !(Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap1)
1401                     || Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap2)) ) {
1402                 fail("wrong front channel mapping");
1403             }
1404         }
1405         // check whether every channel occurs exactly once
1406         int[] chMap = new int[nCh];                        // mapping array to sort channels
1407         for (int ch = 0; ch < effProcNch; ch++) {
1408             int occurred = 0;
1409             for (int idx = 0; idx < procNch; idx++) {
1410                 if (offset[idx] == ch) {
1411                     occurred += 1;
1412                     chMap[ch] = idx;                       // create mapping table to address chans
1413                 }                                          // ... from front to back
1414             }                                              // the LFE must be last
1415             assertTrue(String.format("channel %d occurs %d times in the mapping", ch, occurred),
1416                     occurred == 1);
1417         }
1418 
1419         // go over all segment energies in all channels and check them
1420         double refMinNrg = zeroNrgThresh;                  // reference min energy for the 1st ch;
1421                                                            // others will be compared against 1st
1422         for (int ch = 0; ch < procNch; ch++) {
1423             int idx = chMap[ch];                           // resolve channel mapping
1424             final int ofst = offset[idx] * nSegChOffst;    // signal offset [segments]
1425             if (ch < effProcNch && ofst < totSeg) {
1426                 int nrgSegEnd;                             // the last segment that has energy
1427                 int nrgSeg;                                // the number of segments with energy
1428                 if ((encNch <= 2) && (ch == 0)) {          // the first channel of a mono or ...
1429                     nrgSeg = totSeg;                       // stereo signal has full signal ...
1430                 } else {                                   // all others have one LB + one HB block
1431                     nrgSeg = Math.min(totSeg, (2 * nSegPerBlk) + ofst) - ofst;
1432                 }
1433                 nrgSegEnd = ofst + nrgSeg;
1434                 // find min and max energy of all segments that should have signal
1435                 double minNrg = nrg[idx][ofst];            // channels minimum segment energy
1436                 double maxNrg = nrg[idx][ofst];            // channels maximum segment energy
1437                 for (int seg = ofst+1; seg < nrgSegEnd; seg++) {          // values of 1st segment
1438                     if (nrg[idx][seg] < minNrg) minNrg = nrg[idx][seg];   // ... already assigned
1439                     if (nrg[idx][seg] > maxNrg) maxNrg = nrg[idx][seg];
1440                 }
1441                 assertTrue(String.format("max energy of channel %d is zero", ch),
1442                         maxNrg > 0.0f);
1443                 assertTrue(String.format("channel %d has not enough energy", ch),
1444                         minNrg >= refMinNrg);              // check the channels minimum energy
1445                 if (ch == 0) {                             // use 85% of 1st channels min energy as
1446                     refMinNrg = minNrg * 0.85f;            // ... reference the other chs must meet
1447                 } else if (isDmx && (ch == 1)) {           // in case of mixdown signal the energy
1448                     refMinNrg *= 0.50f;                    // ... can be lower depending on the
1449                 }                                          // ... downmix equation
1450                 // calculate and check the energy ratio
1451                 final double nrgRatio = minNrg / maxNrg;
1452                 assertTrue(String.format("energy ratio of channel %d below threshold", ch),
1453                         nrgRatio >= nrgRatioThresh);
1454                 if (!isDmx) {
1455                     if (nrgSegEnd < totSeg) {
1456                         // consider that some noise can extend into the subsequent segment
1457                         // allow this to be at max 20% of the channels minimum energy
1458                         assertTrue(String.format("min energy after noise above threshold (%.2f)",
1459                                 nrg[idx][nrgSegEnd]),
1460                                 nrg[idx][nrgSegEnd] < minNrg * 0.20f);
1461                         nrgSegEnd += 1;
1462                     }
1463                 } else {                                   // ignore all subsequent segments
1464                     nrgSegEnd = totSeg;                    // ... in case of a mixdown signal
1465                 }
1466                 // zero-out the verified energies to simplify the subsequent check
1467                 for (int seg = ofst; seg < nrgSegEnd; seg++) nrg[idx][seg] = 0.0f;
1468             }
1469             // check zero signal parts
1470             for (int seg = 0; seg < totSeg; seg++) {
1471                 assertTrue(String.format("segment %d in channel %d has signal where should " +
1472                         "be none (%.2f)", seg, ch, nrg[idx][seg]), nrg[idx][seg] < zeroNrgThresh);
1473             }
1474         }
1475         // test whether each segment has energy in at least one channel
1476         for (int seg = 0; seg < totSeg; seg++) {
1477             assertTrue(String.format("no channel has energy in segment %d", seg), sigSeg[seg]);
1478         }
1479     }
1480 
1481     private void checkEnergy(short[] decSamples, AudioParameter decParams, int encNch)
1482             throws RuntimeException {
1483         checkEnergy(decSamples, decParams, encNch, 0.50f);  // default energy ratio threshold: 0.50
1484     }
1485 
1486     /**
1487      * Calculate the RMS of the difference signal between a given signal and the reference samples
1488      * located in mMasterBuffer.
1489      * @param signal the decoded samples to test
1490      * @return RMS of error signal
1491      * @throws RuntimeException
1492      */
1493     private double getRmsError(short[] signal) throws RuntimeException {
1494         long totalErrorSquared = 0;
1495         int stride = mMasterBuffer.length / signal.length;
1496         assertEquals("wrong data size", mMasterBuffer.length, signal.length * stride);
1497 
1498         for (int i = 0; i < signal.length; i++) {
1499             short sample = signal[i];
1500             short mastersample = mMasterBuffer[i * stride];
1501             int d = sample - mastersample;
1502             totalErrorSquared += d * d;
1503         }
1504         long avgErrorSquared = (totalErrorSquared / signal.length);
1505         return Math.sqrt(avgErrorSquared);
1506     }
1507 
1508     /**
1509      * Decode a given input stream and compare the output against the reference signal. The RMS of
1510      * the error signal must be below the given threshold (maxerror).
1511      * Important note about the test signals: this method expects test signals to have been
1512      *   "stretched" relative to the reference signal. The reference, sinesweepraw, is 3s long at
1513      *   44100Hz. For instance for comparing this reference to a test signal at 8000Hz, the test
1514      *   signal needs to be 44100/8000 = 5.5125 times longer, containing frequencies 5.5125
1515      *   times lower than the reference.
1516      * @param testinput the file to decode
1517      * @param maxerror  the maximum allowed root mean squared error
1518      * @throws Exception
1519      */
1520     private void decodeNtest(int testinput, float maxerror) throws Exception {
1521         String localTag = TAG + "#decodeNtest";
1522 
1523         AudioParameter decParams = new AudioParameter();
1524         short[] decoded = decodeToMemory(decParams, testinput, RESET_MODE_NONE, CONFIG_MODE_NONE,
1525                 -1, null);
1526         double rmse = getRmsError(decoded);
1527 
1528         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
1529         Log.v(localTag, String.format("rms = %f (max = %f)", rmse, maxerror));
1530     }
1531 
1532     private void monoTest(int res, int expectedLength) throws Exception {
1533         short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1534         if (mono.length == expectedLength) {
1535             // expected
1536         } else if (mono.length == expectedLength * 2) {
1537             // the decoder output 2 channels instead of 1, check that the left and right channel
1538             // are identical
1539             for (int i = 0; i < mono.length; i += 2) {
1540                 assertEquals("mismatched samples at " + i, mono[i], mono[i+1]);
1541             }
1542         } else {
1543             fail("wrong number of samples: " + mono.length);
1544         }
1545 
1546         short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null);
1547 
1548         assertEquals("count different after reconfigure: ", mono.length, mono2.length);
1549         for (int i = 0; i < mono.length; i++) {
1550             assertEquals("samples at " + i + " don't match", mono[i], mono2[i]);
1551         }
1552 
1553         short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null);
1554 
1555         assertEquals("count different after flush: ", mono.length, mono3.length);
1556         for (int i = 0; i < mono.length; i++) {
1557             assertEquals("samples at " + i + " don't match", mono[i], mono3[i]);
1558         }
1559     }
1560 
1561     /**
1562      * @param testinput the file to decode
1563      * @param maxerror the maximum allowed root mean squared error
1564      * @throws IOException
1565      */
1566     private void decode(int testinput, float maxerror) throws IOException {
1567 
1568         short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
1569 
1570         assertEquals("wrong data size", mMasterBuffer.length, decoded.length);
1571 
1572         double rmse = getRmsError(decoded);
1573 
1574         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
1575 
1576         int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE,
1577                 RESET_MODE_FLUSH, RESET_MODE_EOS_FLUSH };
1578         int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE };
1579 
1580         for (int conf : configModes) {
1581             for (int reset : resetModes) {
1582                 if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) {
1583                     // default case done outside of loop
1584                     continue;
1585                 }
1586                 if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) {
1587                     continue;
1588                 }
1589 
1590                 String params = String.format("(using reset: %d, config: %s)", reset, conf);
1591                 short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null);
1592                 assertEquals("count different with reconfigure" + params,
1593                         decoded.length, decoded2.length);
1594                 for (int i = 0; i < decoded.length; i++) {
1595                     assertEquals("samples don't match" + params, decoded[i], decoded2[i]);
1596                 }
1597             }
1598         }
1599     }
1600 
1601     private boolean hasAudioCsd(int testinput) throws IOException {
1602         AssetFileDescriptor fd = null;
1603         try {
1604 
1605             fd = mResources.openRawResourceFd(testinput);
1606             MediaExtractor extractor = new MediaExtractor();
1607             extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
1608             MediaFormat format = extractor.getTrackFormat(0);
1609 
1610             return format.containsKey(CSD_KEYS[0]);
1611 
1612         } finally {
1613             if (fd != null) {
1614                 fd.close();
1615             }
1616         }
1617     }
1618 
1619     // Class handling all audio parameters relevant for testing
1620     protected static class AudioParameter {
1621 
1622         public AudioParameter() {
1623             this.reset();
1624         }
1625 
1626         public void reset() {
1627             this.numChannels = 0;
1628             this.samplingRate = 0;
1629         }
1630 
1631         public int getNumChannels() {
1632             return this.numChannels;
1633         }
1634 
1635         public int getSamplingRate() {
1636             return this.samplingRate;
1637         }
1638 
1639         public void setNumChannels(int numChannels) {
1640             this.numChannels = numChannels;
1641         }
1642 
1643         public void setSamplingRate(int samplingRate) {
1644             this.samplingRate = samplingRate;
1645         }
1646 
1647         private int numChannels;
1648         private int samplingRate;
1649     }
1650 
1651     private short[] decodeToMemory(int testinput, int resetMode, int configMode,
1652             int eossample, List<Long> timestamps) throws IOException {
1653 
1654         AudioParameter audioParams = new AudioParameter();
1655         return decodeToMemory(audioParams, testinput, resetMode, configMode, eossample, timestamps);
1656     }
1657 
1658     private short[] decodeToMemory(AudioParameter audioParams, int testinput, int resetMode,
1659             int configMode, int eossample, List<Long> timestamps)
1660             throws IOException
1661     {
1662         String localTag = TAG + "#decodeToMemory";
1663         Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode));
1664         short [] decoded = new short[0];
1665         int decodedIdx = 0;
1666 
1667         AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput);
1668 
1669         MediaExtractor extractor;
1670         MediaCodec codec;
1671         ByteBuffer[] codecInputBuffers;
1672         ByteBuffer[] codecOutputBuffers;
1673 
1674         extractor = new MediaExtractor();
1675         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
1676                 testFd.getLength());
1677         testFd.close();
1678 
1679         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
1680         MediaFormat format = extractor.getTrackFormat(0);
1681         String mime = format.getString(MediaFormat.KEY_MIME);
1682         assertTrue("not an audio file", mime.startsWith("audio/"));
1683 
1684         MediaFormat configFormat = format;
1685         codec = MediaCodec.createDecoderByType(mime);
1686         if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) {
1687             configFormat = MediaFormat.createAudioFormat(mime,
1688                     format.getInteger(MediaFormat.KEY_SAMPLE_RATE),
1689                     format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
1690 
1691             configFormat.setLong(MediaFormat.KEY_DURATION,
1692                     format.getLong(MediaFormat.KEY_DURATION));
1693             String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" };
1694             for (String k : keys) {
1695                 if (format.containsKey(k)) {
1696                     configFormat.setInteger(k, format.getInteger(k));
1697                 }
1698             }
1699         }
1700         Log.v(localTag, "configuring with " + configFormat);
1701         codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
1702 
1703         codec.start();
1704         codecInputBuffers = codec.getInputBuffers();
1705         codecOutputBuffers = codec.getOutputBuffers();
1706 
1707         if (resetMode == RESET_MODE_RECONFIGURE) {
1708             codec.stop();
1709             codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
1710             codec.start();
1711             codecInputBuffers = codec.getInputBuffers();
1712             codecOutputBuffers = codec.getOutputBuffers();
1713         } else if (resetMode == RESET_MODE_FLUSH) {
1714             codec.flush();
1715         }
1716 
1717         extractor.selectTrack(0);
1718 
1719         if (configMode == CONFIG_MODE_QUEUE) {
1720             queueConfig(codec, format);
1721         }
1722 
1723         // start decoding
1724         final long kTimeOutUs = 5000;
1725         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
1726         boolean sawInputEOS = false;
1727         boolean sawOutputEOS = false;
1728         int noOutputCounter = 0;
1729         int samplecounter = 0;
1730         while (!sawOutputEOS && noOutputCounter < 50) {
1731             noOutputCounter++;
1732             if (!sawInputEOS) {
1733                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
1734 
1735                 if (inputBufIndex >= 0) {
1736                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
1737 
1738                     int sampleSize =
1739                         extractor.readSampleData(dstBuf, 0 /* offset */);
1740 
1741                     long presentationTimeUs = 0;
1742 
1743                     if (sampleSize < 0 && eossample > 0) {
1744                         fail("test is broken: never reached eos sample");
1745                     }
1746                     if (sampleSize < 0) {
1747                         Log.d(TAG, "saw input EOS.");
1748                         sawInputEOS = true;
1749                         sampleSize = 0;
1750                     } else {
1751                         if (samplecounter == eossample) {
1752                             sawInputEOS = true;
1753                         }
1754                         samplecounter++;
1755                         presentationTimeUs = extractor.getSampleTime();
1756                     }
1757                     codec.queueInputBuffer(
1758                             inputBufIndex,
1759                             0 /* offset */,
1760                             sampleSize,
1761                             presentationTimeUs,
1762                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
1763 
1764                     if (!sawInputEOS) {
1765                         extractor.advance();
1766                     }
1767                 }
1768             }
1769 
1770             int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
1771 
1772             if (res >= 0) {
1773                 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
1774 
1775                 if (info.size > 0) {
1776                     noOutputCounter = 0;
1777                     if (timestamps != null) {
1778                         timestamps.add(info.presentationTimeUs);
1779                     }
1780                 }
1781                 if (info.size > 0 &&
1782                         resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) {
1783                     // once we've gotten some data out of the decoder, reset and start again
1784                     if (resetMode == RESET_MODE_RECONFIGURE) {
1785                         codec.stop();
1786                         codec.configure(configFormat, null /* surface */, null /* crypto */,
1787                                 0 /* flags */);
1788                         codec.start();
1789                         codecInputBuffers = codec.getInputBuffers();
1790                         codecOutputBuffers = codec.getOutputBuffers();
1791                         if (configMode == CONFIG_MODE_QUEUE) {
1792                             queueConfig(codec, format);
1793                         }
1794                     } else /* resetMode == RESET_MODE_FLUSH */ {
1795                         codec.flush();
1796                     }
1797                     resetMode = RESET_MODE_NONE;
1798                     extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
1799                     sawInputEOS = false;
1800                     samplecounter = 0;
1801                     if (timestamps != null) {
1802                         timestamps.clear();
1803                     }
1804                     continue;
1805                 }
1806 
1807                 int outputBufIndex = res;
1808                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
1809 
1810                 if (decodedIdx + (info.size / 2) >= decoded.length) {
1811                     decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
1812                 }
1813 
1814                 buf.position(info.offset);
1815                 for (int i = 0; i < info.size; i += 2) {
1816                     decoded[decodedIdx++] = buf.getShort();
1817                 }
1818 
1819                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
1820 
1821                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
1822                     Log.d(TAG, "saw output EOS.");
1823                     if (resetMode == RESET_MODE_EOS_FLUSH) {
1824                         resetMode = RESET_MODE_NONE;
1825                         codec.flush();
1826                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
1827                         sawInputEOS = false;
1828                         samplecounter = 0;
1829                         decoded = new short[0];
1830                         decodedIdx = 0;
1831                         if (timestamps != null) {
1832                             timestamps.clear();
1833                         }
1834                     } else {
1835                         sawOutputEOS = true;
1836                     }
1837                 }
1838             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
1839                 codecOutputBuffers = codec.getOutputBuffers();
1840 
1841                 Log.d(TAG, "output buffers have changed.");
1842             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
1843                 MediaFormat oformat = codec.getOutputFormat();
1844                 audioParams.setNumChannels(oformat.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
1845                 audioParams.setSamplingRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
1846                 Log.d(TAG, "output format has changed to " + oformat);
1847             } else {
1848                 Log.d(TAG, "dequeueOutputBuffer returned " + res);
1849             }
1850         }
1851         if (noOutputCounter >= 50) {
1852             fail("decoder stopped outputing data");
1853         }
1854 
1855         codec.stop();
1856         codec.release();
1857         return decoded;
1858     }
1859 
1860     private static void queueConfig(MediaCodec codec, MediaFormat format) {
1861         for (String csdKey : CSD_KEYS) {
1862             if (!format.containsKey(csdKey)) {
1863                 continue;
1864             }
1865             ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
1866             int inputBufIndex = codec.dequeueInputBuffer(-1);
1867             if (inputBufIndex < 0) {
1868                 fail("failed to queue configuration buffer " + csdKey);
1869             } else {
1870                 ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind();
1871                 Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd));
1872                 codecInputBuffers[inputBufIndex].put(csd);
1873                 codec.queueInputBuffer(
1874                         inputBufIndex,
1875                         0 /* offset */,
1876                         csd.limit(),
1877                         0 /* presentation time (us) */,
1878                         MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
1879             }
1880         }
1881     }
1882 
1883     public void testDecodeWithEOSOnLastBuffer() throws Exception {
1884         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
1885         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
1886         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
1887         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopus);
1888         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopusmp4);
1889         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
1890         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmkv);
1891         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
1892         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmp4);
1893         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
1894         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmkv);
1895         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmp4);
1896     }
1897 
1898     /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
1899      * input buffer after all the full ones. */
1900     private void testDecodeWithEOSOnLastBuffer(int res) throws Exception {
1901         int numsamples = countSamples(res);
1902         assertTrue(numsamples != 0);
1903 
1904         List<Long> timestamps1 = new ArrayList<Long>();
1905         short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1);
1906 
1907         List<Long> timestamps2 = new ArrayList<Long>();
1908         short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1,
1909                 timestamps2);
1910 
1911         // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last
1912         assertEquals(decode1.length, decode2.length);
1913         assertTrue(Arrays.equals(decode1, decode2));
1914         assertEquals(timestamps1.size(), timestamps2.size());
1915         assertTrue(timestamps1.equals(timestamps2));
1916 
1917         // ... and that this is also true when reconfiguring the codec
1918         timestamps2.clear();
1919         decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2);
1920         assertTrue(Arrays.equals(decode1, decode2));
1921         assertTrue(timestamps1.equals(timestamps2));
1922         timestamps2.clear();
1923         decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1,
1924                 timestamps2);
1925         assertEquals(decode1.length, decode2.length);
1926         assertTrue(Arrays.equals(decode1, decode2));
1927         assertTrue(timestamps1.equals(timestamps2));
1928 
1929         // ... and that this is also true when flushing the codec
1930         timestamps2.clear();
1931         decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2);
1932         assertTrue(Arrays.equals(decode1, decode2));
1933         assertTrue(timestamps1.equals(timestamps2));
1934         timestamps2.clear();
1935         decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1,
1936                 timestamps2);
1937         assertEquals(decode1.length, decode2.length);
1938         assertTrue(Arrays.equals(decode1, decode2));
1939         assertTrue(timestamps1.equals(timestamps2));
1940     }
1941 
1942     private int countSamples(int res) throws IOException {
1943         AssetFileDescriptor testFd = mResources.openRawResourceFd(res);
1944 
1945         MediaExtractor extractor = new MediaExtractor();
1946         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
1947                 testFd.getLength());
1948         testFd.close();
1949         extractor.selectTrack(0);
1950         int numsamples = extractor.getSampleTime() < 0 ? 0 : 1;
1951         while (extractor.advance()) {
1952             numsamples++;
1953         }
1954         return numsamples;
1955     }
1956 
1957     private void testDecode(int testVideo, int frameNum) throws Exception {
1958         if (!MediaUtils.checkCodecForResource(mContext, testVideo, 0 /* track */)) {
1959             return; // skip
1960         }
1961 
1962         // Decode to Surface.
1963         Surface s = getActivity().getSurfaceHolder().getSurface();
1964         int frames1 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, s);
1965         assertEquals("wrong number of frames decoded", frameNum, frames1);
1966 
1967         // Decode to buffer.
1968         int frames2 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, null);
1969         assertEquals("different number of frames when using Surface", frames1, frames2);
1970     }
1971 
1972     public void testCodecBasicH264() throws Exception {
1973         testDecode(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 240);
1974     }
1975 
1976     public void testCodecBasicHEVC() throws Exception {
1977         testDecode(
1978                 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, 300);
1979     }
1980 
1981     public void testCodecBasicH263() throws Exception {
1982         testDecode(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 122);
1983     }
1984 
1985     public void testCodecBasicMpeg2() throws Exception {
1986         testDecode(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 300);
1987     }
1988 
1989     public void testCodecBasicMpeg4() throws Exception {
1990         testDecode(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 249);
1991     }
1992 
1993     public void testCodecBasicVP8() throws Exception {
1994         testDecode(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240);
1995     }
1996 
1997     public void testCodecBasicVP9() throws Exception {
1998         testDecode(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240);
1999     }
2000 
2001     public void testCodecBasicAV1() throws Exception {
2002         testDecode(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
2003     }
2004 
2005     public void testH264Decode320x240() throws Exception {
2006         testDecode(R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz, 300);
2007     }
2008 
2009     public void testH264Decode720x480() throws Exception {
2010         testDecode(R.raw.bbb_s1_720x480_mp4_h264_mp3_2mbps_30fps_aac_lc_5ch_320kbps_48000hz, 300);
2011     }
2012 
2013     public void testH264Decode30fps1280x720Tv() throws Exception {
2014         if (checkTv()) {
2015             assertTrue(MediaUtils.canDecodeVideo(
2016                     MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30,
2017                     AVCProfileHigh, AVCLevel31, 8000000));
2018         }
2019     }
2020 
2021     public void testH264SecureDecode30fps1280x720Tv() throws Exception {
2022         if (checkTv()) {
2023             verifySecureVideoDecodeSupport(
2024                     MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30,
2025                     AVCProfileHigh, AVCLevel31, 8000000);
2026         }
2027     }
2028 
2029     public void testH264Decode30fps1280x720() throws Exception {
2030         testDecode(R.raw.bbb_s4_1280x720_mp4_h264_mp31_8mbps_30fps_aac_he_mono_40kbps_44100hz, 300);
2031     }
2032 
2033     public void testH264Decode60fps1280x720Tv() throws Exception {
2034         if (checkTv()) {
2035             assertTrue(MediaUtils.canDecodeVideo(
2036                     MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60,
2037                     AVCProfileHigh, AVCLevel32, 8000000));
2038             testDecode(
2039                     R.raw.bbb_s3_1280x720_mp4_h264_hp32_8mbps_60fps_aac_he_v2_stereo_48kbps_48000hz,
2040                     600);
2041         }
2042     }
2043 
2044     public void testH264SecureDecode60fps1280x720Tv() throws Exception {
2045         if (checkTv()) {
2046             verifySecureVideoDecodeSupport(
2047                     MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60,
2048                     AVCProfileHigh, AVCLevel32, 8000000);
2049         }
2050     }
2051 
2052     public void testH264Decode60fps1280x720() throws Exception {
2053         testDecode(
2054             R.raw.bbb_s3_1280x720_mp4_h264_mp32_8mbps_60fps_aac_he_v2_6ch_144kbps_44100hz, 600);
2055     }
2056 
2057     public void testH264Decode30fps1920x1080Tv() throws Exception {
2058         if (checkTv()) {
2059             assertTrue(MediaUtils.canDecodeVideo(
2060                     MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30,
2061                     AVCProfileHigh, AVCLevel4, 20000000));
2062             testDecode(
2063                     R.raw.bbb_s4_1920x1080_wide_mp4_h264_hp4_20mbps_30fps_aac_lc_6ch_384kbps_44100hz,
2064                     150);
2065         }
2066     }
2067 
2068     public void testH264SecureDecode30fps1920x1080Tv() throws Exception {
2069         if (checkTv()) {
2070             verifySecureVideoDecodeSupport(
2071                     MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30,
2072                     AVCProfileHigh, AVCLevel4, 20000000);
2073         }
2074     }
2075 
2076     public void testH264Decode30fps1920x1080() throws Exception {
2077         testDecode(
2078                 R.raw.bbb_s4_1920x1080_wide_mp4_h264_mp4_20mbps_30fps_aac_he_5ch_200kbps_44100hz,
2079                 150);
2080     }
2081 
2082     public void testH264Decode60fps1920x1080Tv() throws Exception {
2083         if (checkTv()) {
2084             assertTrue(MediaUtils.canDecodeVideo(
2085                     MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60,
2086                     AVCProfileHigh, AVCLevel42, 20000000));
2087             testDecode(
2088                     R.raw.bbb_s2_1920x1080_mp4_h264_hp42_20mbps_60fps_aac_lc_6ch_384kbps_48000hz,
2089                     300);
2090         }
2091     }
2092 
2093     public void testH264SecureDecode60fps1920x1080Tv() throws Exception {
2094         if (checkTv()) {
2095             verifySecureVideoDecodeSupport(
2096                     MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60,
2097                     AVCProfileHigh, AVCLevel42, 20000000);
2098         }
2099     }
2100 
2101     public void testH264Decode60fps1920x1080() throws Exception {
2102         testDecode(
2103                 R.raw.bbb_s2_1920x1080_mp4_h264_mp42_20mbps_60fps_aac_he_v2_5ch_160kbps_48000hz,
2104                 300);
2105         testDecode(
2106                 R.raw.bbb_s2_1920x1080_mkv_h264_mp42_20mbps_60fps_aac_he_v2_5ch_160kbps_48000hz,
2107                 300);
2108     }
2109 
2110     public void testH265Decode25fps1280x720() throws Exception {
2111         testDecode(
2112                 R.raw.video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz, 240);
2113     }
2114 
2115     public void testVP8Decode320x180() throws Exception {
2116         testDecode(R.raw.bbb_s1_320x180_webm_vp8_800kbps_30fps_opus_5ch_320kbps_48000hz, 300);
2117     }
2118 
2119     public void testVP8Decode640x360() throws Exception {
2120         testDecode(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz, 300);
2121     }
2122 
2123     public void testVP8Decode30fps1280x720Tv() throws Exception {
2124         if (checkTv()) {
2125             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30));
2126         }
2127     }
2128 
2129     public void testVP8Decode30fps1280x720() throws Exception {
2130         testDecode(R.raw.bbb_s4_1280x720_webm_vp8_8mbps_30fps_opus_mono_64kbps_48000hz, 300);
2131     }
2132 
2133     public void testVP8Decode60fps1280x720Tv() throws Exception {
2134         if (checkTv()) {
2135             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 60));
2136         }
2137     }
2138 
2139     public void testVP8Decode60fps1280x720() throws Exception {
2140         testDecode(R.raw.bbb_s3_1280x720_webm_vp8_8mbps_60fps_opus_6ch_384kbps_48000hz, 600);
2141     }
2142 
2143     public void testVP8Decode30fps1920x1080Tv() throws Exception {
2144         if (checkTv()) {
2145             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30));
2146         }
2147     }
2148 
2149     public void testVP8Decode30fps1920x1080() throws Exception {
2150         testDecode(
2151                 R.raw.bbb_s4_1920x1080_wide_webm_vp8_20mbps_30fps_vorbis_6ch_384kbps_44100hz, 150);
2152     }
2153 
2154     public void testVP8Decode60fps1920x1080Tv() throws Exception {
2155         if (checkTv()) {
2156             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 60));
2157         }
2158     }
2159 
2160     public void testVP8Decode60fps1920x1080() throws Exception {
2161         testDecode(R.raw.bbb_s2_1920x1080_webm_vp8_20mbps_60fps_vorbis_6ch_384kbps_48000hz, 300);
2162     }
2163 
2164     public void testVP9Decode320x180() throws Exception {
2165         testDecode(R.raw.bbb_s1_320x180_webm_vp9_0p11_600kbps_30fps_vorbis_mono_64kbps_48000hz, 300);
2166     }
2167 
2168     public void testVP9Decode640x360() throws Exception {
2169         testDecode(
2170                 R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz,
2171                 300);
2172     }
2173 
2174     public void testVP9Decode30fps1280x720Tv() throws Exception {
2175         if (checkTv()) {
2176             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30));
2177         }
2178     }
2179 
2180     public void testVP9Decode30fps1280x720() throws Exception {
2181         testDecode(
2182                 R.raw.bbb_s4_1280x720_webm_vp9_0p31_4mbps_30fps_opus_stereo_128kbps_48000hz, 300);
2183     }
2184 
2185     public void testVP9Decode60fps1920x1080() throws Exception {
2186         testDecode(
2187                 R.raw.bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz, 300);
2188     }
2189 
2190     public void testVP9Decode30fps3840x2160() throws Exception {
2191         testDecode(
2192                 R.raw.bbb_s4_3840x2160_webm_vp9_0p5_20mbps_30fps_vorbis_6ch_384kbps_24000hz, 150);
2193     }
2194 
2195     public void testVP9Decode60fps3840x2160() throws Exception {
2196         testDecode(
2197                 R.raw.bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz, 300);
2198     }
2199 
2200     public void testAV1Decode320x180() throws Exception {
2201         testDecode(R.raw.video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
2202     }
2203 
2204     public void testAV1Decode640x360() throws Exception {
2205         testDecode(
2206                 R.raw.video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz,
2207                 300);
2208     }
2209 
2210     public void testAV1Decode30fps1280x720() throws Exception {
2211         testDecode(
2212                 R.raw.video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz, 300);
2213     }
2214 
2215     public void testAV1Decode60fps1920x1080() throws Exception {
2216         testDecode(
2217                 R.raw.video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300);
2218     }
2219 
2220     public void testAV1Decode30fps3840x2160() throws Exception {
2221         testDecode(
2222                 R.raw.video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz, 150);
2223     }
2224 
2225     public void testAV1Decode60fps3840x2160() throws Exception {
2226         testDecode(
2227                 R.raw.video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300);
2228     }
2229 
2230     public void testHEVCDecode352x288() throws Exception {
2231         testDecode(
2232                 R.raw.bbb_s1_352x288_mp4_hevc_mp2_600kbps_30fps_aac_he_stereo_96kbps_48000hz, 300);
2233     }
2234 
2235     public void testHEVCDecode720x480() throws Exception {
2236         testDecode(
2237                 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, 300);
2238     }
2239 
2240     public void testHEVCDecode30fps1280x720Tv() throws Exception {
2241         if (checkTv()) {
2242             assertTrue(MediaUtils.canDecodeVideo(
2243                     MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30,
2244                     HEVCProfileMain, HEVCMainTierLevel31, 4000000));
2245         }
2246     }
2247 
2248     public void testHEVCDecode30fps1280x720() throws Exception {
2249         testDecode(
2250                 R.raw.bbb_s4_1280x720_mp4_hevc_mp31_4mbps_30fps_aac_he_stereo_80kbps_32000hz, 300);
2251     }
2252 
2253     public void testHEVCDecode30fps1920x1080Tv() throws Exception {
2254         if (checkTv()) {
2255             assertTrue(MediaUtils.canDecodeVideo(
2256                     MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30,
2257                     HEVCProfileMain, HEVCMainTierLevel41, 5000000));
2258         }
2259     }
2260 
2261     public void testHEVCDecode60fps1920x1080() throws Exception {
2262         testDecode(
2263                 R.raw.bbb_s2_1920x1080_mp4_hevc_mp41_10mbps_60fps_aac_lc_6ch_384kbps_22050hz, 300);
2264     }
2265 
2266     public void testHEVCDecode30fps3840x2160() throws Exception {
2267         testDecode(
2268                 R.raw.bbb_s4_3840x2160_mp4_hevc_mp5_20mbps_30fps_aac_lc_6ch_384kbps_24000hz, 150);
2269     }
2270 
2271     public void testHEVCDecode60fps3840x2160() throws Exception {
2272         testDecode(
2273                 R.raw.bbb_s2_3840x2160_mp4_hevc_mp51_20mbps_60fps_aac_lc_6ch_384kbps_32000hz, 300);
2274     }
2275 
2276     public void testMpeg2Decode352x288() throws Exception {
2277         testDecode(R.raw.video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz, 300);
2278     }
2279 
2280     public void testMpeg2Decode720x480() throws Exception {
2281         testDecode(R.raw.video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz, 300);
2282     }
2283 
2284     public void testMpeg2Decode30fps1280x720Tv() throws Exception {
2285         if (checkTv()) {
2286             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1280, 720, 30));
2287         }
2288     }
2289 
2290     public void testMpeg2Decode30fps1280x720() throws Exception {
2291         testDecode(R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
2292     }
2293 
2294     public void testMpeg2Decode30fps1920x1080Tv() throws Exception {
2295         if (checkTv()) {
2296             assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1920, 1080, 30));
2297         }
2298     }
2299 
2300     public void testMpeg2Decode30fps1920x1080() throws Exception {
2301         testDecode(R.raw.video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
2302     }
2303 
2304     public void testMpeg2Decode30fps3840x2160() throws Exception {
2305         testDecode(R.raw.video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz, 150);
2306     }
2307 
2308     private void testCodecEarlyEOS(int resid, int eosFrame) throws Exception {
2309         if (!MediaUtils.checkCodecForResource(mContext, resid, 0 /* track */)) {
2310             return; // skip
2311         }
2312         Surface s = getActivity().getSurfaceHolder().getSurface();
2313         int frames1 = countFrames(resid, RESET_MODE_NONE, eosFrame, s);
2314         assertEquals("wrong number of frames decoded", eosFrame, frames1);
2315     }
2316 
2317     public void testCodecEarlyEOSH263() throws Exception {
2318         testCodecEarlyEOS(
2319                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
2320                 64 /* eosframe */);
2321     }
2322 
2323     public void testCodecEarlyEOSH264() throws Exception {
2324         testCodecEarlyEOS(
2325                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
2326                 120 /* eosframe */);
2327     }
2328 
2329     public void testCodecEarlyEOSHEVC() throws Exception {
2330         testCodecEarlyEOS(
2331                 R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz,
2332                 120 /* eosframe */);
2333     }
2334 
2335     public void testCodecEarlyEOSMpeg2() throws Exception {
2336         testCodecEarlyEOS(
2337                 R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz,
2338                 120 /* eosframe */);
2339     }
2340 
2341     public void testCodecEarlyEOSMpeg4() throws Exception {
2342         testCodecEarlyEOS(
2343                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
2344                 120 /* eosframe */);
2345     }
2346 
2347     public void testCodecEarlyEOSVP8() throws Exception {
2348         testCodecEarlyEOS(
2349                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
2350                 120 /* eosframe */);
2351     }
2352 
2353     public void testCodecEarlyEOSVP9() throws Exception {
2354         testCodecEarlyEOS(
2355                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
2356                 120 /* eosframe */);
2357     }
2358 
2359     public void testCodecEarlyEOSAV1() throws Exception {
2360         testCodecEarlyEOS(
2361                 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz,
2362                 120 /* eosframe */);
2363     }
2364 
2365     public void testCodecResetsH264WithoutSurface() throws Exception {
2366         testCodecResets(
2367                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
2368     }
2369 
2370     public void testCodecResetsH264WithSurface() throws Exception {
2371         Surface s = getActivity().getSurfaceHolder().getSurface();
2372         testCodecResets(
2373                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
2374     }
2375 
2376     public void testCodecResetsHEVCWithoutSurface() throws Exception {
2377         testCodecResets(
2378                 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, null);
2379     }
2380 
2381     public void testCodecResetsHEVCWithSurface() throws Exception {
2382         Surface s = getActivity().getSurfaceHolder().getSurface();
2383         testCodecResets(
2384                 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, s);
2385     }
2386 
2387     public void testCodecResetsMpeg2WithoutSurface() throws Exception {
2388         testCodecResets(
2389                 R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, null);
2390     }
2391 
2392     public void testCodecResetsMpeg2WithSurface() throws Exception {
2393         Surface s = getActivity().getSurfaceHolder().getSurface();
2394         testCodecResets(
2395                 R.raw.video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz, s);
2396     }
2397 
2398     public void testCodecResetsH263WithoutSurface() throws Exception {
2399         testCodecResets(
2400                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
2401     }
2402 
2403     public void testCodecResetsH263WithSurface() throws Exception {
2404         Surface s = getActivity().getSurfaceHolder().getSurface();
2405         testCodecResets(
2406                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s);
2407     }
2408 
2409     public void testCodecResetsMpeg4WithoutSurface() throws Exception {
2410         testCodecResets(
2411                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null);
2412     }
2413 
2414     public void testCodecResetsMpeg4WithSurface() throws Exception {
2415         Surface s = getActivity().getSurfaceHolder().getSurface();
2416         testCodecResets(
2417                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s);
2418     }
2419 
2420     public void testCodecResetsVP8WithoutSurface() throws Exception {
2421         testCodecResets(
2422                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null);
2423     }
2424 
2425     public void testCodecResetsVP8WithSurface() throws Exception {
2426         Surface s = getActivity().getSurfaceHolder().getSurface();
2427         testCodecResets(
2428                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s);
2429     }
2430 
2431     public void testCodecResetsVP9WithoutSurface() throws Exception {
2432         testCodecResets(
2433                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null);
2434     }
2435 
2436     public void testCodecResetsAV1WithoutSurface() throws Exception {
2437         testCodecResets(
2438                 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, null);
2439     }
2440 
2441     public void testCodecResetsVP9WithSurface() throws Exception {
2442         Surface s = getActivity().getSurfaceHolder().getSurface();
2443         testCodecResets(
2444                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s);
2445     }
2446 
2447     public void testCodecResetsAV1WithSurface() throws Exception {
2448         Surface s = getActivity().getSurfaceHolder().getSurface();
2449         testCodecResets(
2450                 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, s);
2451     }
2452 
2453 //    public void testCodecResetsOgg() throws Exception {
2454 //        testCodecResets(R.raw.sinesweepogg, null);
2455 //    }
2456 
2457     public void testCodecResetsMp3() throws Exception {
2458         testCodecReconfig(R.raw.sinesweepmp3lame);
2459         // NOTE: replacing testCodecReconfig call soon
2460 //        testCodecResets(R.raw.sinesweepmp3lame, null);
2461     }
2462 
2463     public void testCodecResetsM4a() throws Exception {
2464         testCodecReconfig(R.raw.sinesweepm4a);
2465         // NOTE: replacing testCodecReconfig call soon
2466 //        testCodecResets(R.raw.sinesweepm4a, null);
2467     }
2468 
2469     private void testCodecReconfig(int audio) throws Exception {
2470         int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */);
2471         int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */);
2472         assertEquals("different output size when using reconfigured codec", size1, size2);
2473     }
2474 
2475     private void testCodecResets(int video, Surface s) throws Exception {
2476         if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) {
2477             return; // skip
2478         }
2479 
2480         int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s);
2481         int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s);
2482         int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s);
2483         assertEquals("different number of frames when using reconfigured codec", frames1, frames2);
2484         assertEquals("different number of frames when using flushed codec", frames1, frames3);
2485     }
2486 
2487     private static void verifySecureVideoDecodeSupport(
2488             String mime, int width, int height, float rate, int profile, int level, int bitrate) {
2489         MediaFormat baseFormat = new MediaFormat();
2490         baseFormat.setString(MediaFormat.KEY_MIME, mime);
2491         baseFormat.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
2492 
2493         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
2494         format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
2495         format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
2496         format.setInteger(MediaFormat.KEY_PROFILE, profile);
2497         format.setInteger(MediaFormat.KEY_LEVEL, level);
2498         format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
2499 
2500         MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
2501         if (mcl.findDecoderForFormat(baseFormat) == null) {
2502             MediaUtils.skipTest("no secure decoder for " + mime);
2503             return;
2504         }
2505         assertNotNull("no decoder for " + format, mcl.findDecoderForFormat(format));
2506     }
2507 
2508     private static MediaCodec createDecoder(String mime) {
2509         try {
2510             if (false) {
2511                 // change to force testing software codecs
2512                 MediaFormat format = new MediaFormat();
2513                 format.setString(MediaFormat.KEY_MIME, mime);
2514                 String[] codecs =
2515                     MediaUtils.getDecoderNames(Boolean.TRUE /* isGoog */, format);
2516                 if (codecs.length > 0) {
2517                     return MediaCodec.createByCodecName(codecs[0]);
2518                 } else {
2519                     return null;
2520                 }
2521             }
2522             return MediaCodec.createDecoderByType(mime);
2523         } catch (Exception e) {
2524             return null;
2525         }
2526     }
2527 
2528     private static MediaCodec createDecoder(MediaFormat format) {
2529         return MediaUtils.getDecoder(format);
2530     }
2531 
2532     // for video
2533     private int countFrames(int video, int resetMode, int eosframe, Surface s)
2534             throws Exception {
2535         AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
2536         MediaExtractor extractor = new MediaExtractor();
2537         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
2538                 testFd.getLength());
2539         extractor.selectTrack(0);
2540 
2541         int numframes = decodeWithChecks(null /* decoderName */, extractor,
2542                 CHECKFLAG_RETURN_OUTPUTFRAMES | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
2543                 resetMode, s, eosframe, null, null);
2544 
2545         extractor.release();
2546         testFd.close();
2547         return numframes;
2548     }
2549 
2550     // for audio
2551     private int countSize(int audio, int resetMode, int eosframe)
2552             throws Exception {
2553         AssetFileDescriptor testFd = mResources.openRawResourceFd(audio);
2554         MediaExtractor extractor = new MediaExtractor();
2555         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
2556                 testFd.getLength());
2557         extractor.selectTrack(0);
2558 
2559         // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH
2560         int outputSize = decodeWithChecks(null /* decoderName */, extractor,
2561                 CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null,
2562                 eosframe, null, null);
2563 
2564         extractor.release();
2565         testFd.close();
2566         return outputSize;
2567     }
2568 
2569     /*
2570     * Test all decoders' EOS behavior.
2571     */
2572     private void testEOSBehavior(int movie, int stopatsample) throws Exception {
2573         testEOSBehavior(movie, new int[] {stopatsample});
2574     }
2575 
2576     /*
2577     * Test all decoders' EOS behavior.
2578     */
2579     private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception {
2580         Surface s = null;
2581         AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
2582         MediaExtractor extractor = new MediaExtractor();
2583         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
2584                 testFd.getLength());
2585         extractor.selectTrack(0); // consider variable looping on track
2586         MediaFormat format = extractor.getTrackFormat(0);
2587 
2588         String[] decoderNames = MediaUtils.getDecoderNames(format);
2589         for (String decoderName: decoderNames) {
2590             List<Long> outputChecksums = new ArrayList<Long>();
2591             List<Long> outputTimestamps = new ArrayList<Long>();
2592             Arrays.sort(stopAtSample);
2593             int last = stopAtSample.length - 1;
2594 
2595             // decode reference (longest sequence to stop at + 100) and
2596             // store checksums/pts in outputChecksums and outputTimestamps
2597             // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH)
2598             decodeWithChecks(decoderName, extractor,
2599                     CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
2600                     RESET_MODE_NONE, s,
2601                     stopAtSample[last] + 100, outputChecksums, outputTimestamps);
2602 
2603             // decode stopAtSample requests in reverse order (longest to
2604             // shortest) and compare to reference checksums/pts in
2605             // outputChecksums and outputTimestamps
2606             for (int i = last; i >= 0; --i) {
2607                 if (true) { // reposition extractor
2608                     extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
2609                 } else { // create new extractor
2610                     extractor.release();
2611                     extractor = new MediaExtractor();
2612                     extractor.setDataSource(testFd.getFileDescriptor(),
2613                             testFd.getStartOffset(), testFd.getLength());
2614                     extractor.selectTrack(0); // consider variable looping on track
2615                 }
2616                 decodeWithChecks(decoderName, extractor,
2617                         CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS
2618                         | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH
2619                         | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
2620                         RESET_MODE_NONE, s,
2621                         stopAtSample[i], outputChecksums, outputTimestamps);
2622             }
2623             extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
2624         }
2625 
2626         extractor.release();
2627         testFd.close();
2628     }
2629 
2630     private static final int CHECKFLAG_SETCHECKSUM = 1 << 0;
2631     private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1;
2632     private static final int CHECKFLAG_SETPTS = 1 << 2;
2633     private static final int CHECKFLAG_COMPAREPTS = 1 << 3;
2634     private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4;
2635     private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5;
2636     private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6;
2637     private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7;
2638 
2639     /**
2640      * Decodes frames with parameterized checks and return values.
2641      * If decoderName is provided, mediacodec will create that decoder. Otherwise,
2642      * mediacodec will use the default decoder provided by platform.
2643      * The integer return can be selected through the checkFlags variable.
2644      */
2645     private static int decodeWithChecks(
2646             String decoderName, MediaExtractor extractor,
2647             int checkFlags, int resetMode, Surface surface, int stopAtSample,
2648             List<Long> outputChecksums, List<Long> outputTimestamps)
2649             throws Exception {
2650         int trackIndex = extractor.getSampleTrackIndex();
2651         MediaFormat format = extractor.getTrackFormat(trackIndex);
2652         String mime = format.getString(MediaFormat.KEY_MIME);
2653         boolean isAudio = mime.startsWith("audio/");
2654         ByteBuffer[] codecInputBuffers;
2655         ByteBuffer[] codecOutputBuffers;
2656 
2657         MediaCodec codec =
2658                 decoderName == null ? createDecoder(format) : MediaCodec.createByCodecName(decoderName);
2659         Log.i("@@@@", "using codec: " + codec.getName());
2660         codec.configure(format, surface, null /* crypto */, 0 /* flags */);
2661         codec.start();
2662         codecInputBuffers = codec.getInputBuffers();
2663         codecOutputBuffers = codec.getOutputBuffers();
2664 
2665         if (resetMode == RESET_MODE_RECONFIGURE) {
2666             codec.stop();
2667             codec.configure(format, surface, null /* crypto */, 0 /* flags */);
2668             codec.start();
2669             codecInputBuffers = codec.getInputBuffers();
2670             codecOutputBuffers = codec.getOutputBuffers();
2671         } else if (resetMode == RESET_MODE_FLUSH) {
2672             codec.flush();
2673 
2674             // We must always queue CSD after a flush that is potentially
2675             // before we receive output format has changed.
2676             queueConfig(codec, format);
2677         }
2678 
2679         // start decode loop
2680         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
2681 
2682         MediaFormat outFormat = codec.getOutputFormat();
2683         long kTimeOutUs = 5000; // 5ms timeout
2684         String outMime = format.getString(MediaFormat.KEY_MIME);
2685         if ((surface == null) && (outMime != null) && outMime.startsWith("video/")) {
2686             int outWidth = outFormat.getInteger(MediaFormat.KEY_WIDTH);
2687             int outHeight = outFormat.getInteger(MediaFormat.KEY_HEIGHT);
2688             // in the 4K decoding case in byte buffer mode, set kTimeOutUs to 10ms as decode may
2689             // involve a memcpy
2690             if (outWidth * outHeight >= 8000000) {
2691                 kTimeOutUs = 10000;
2692             }
2693         }
2694 
2695         boolean sawInputEOS = false;
2696         boolean sawOutputEOS = false;
2697         int deadDecoderCounter = 0;
2698         int samplenum = 0;
2699         int numframes = 0;
2700         int outputSize = 0;
2701         int width = 0;
2702         int height = 0;
2703         boolean dochecksum = false;
2704         ArrayList<Long> timestamps = new ArrayList<Long>();
2705         if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
2706             outputTimestamps.clear();
2707         }
2708         if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
2709             outputChecksums.clear();
2710         }
2711         boolean advanceDone = true;
2712         while (!sawOutputEOS && deadDecoderCounter < 100) {
2713             // handle input
2714             if (!sawInputEOS) {
2715                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
2716 
2717                 if (inputBufIndex >= 0) {
2718                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
2719 
2720                     int sampleSize =
2721                             extractor.readSampleData(dstBuf, 0 /* offset */);
2722                     assertEquals("end of stream should match extractor.advance()", sampleSize >= 0,
2723                             advanceDone);
2724                     long presentationTimeUs = extractor.getSampleTime();
2725                     advanceDone = extractor.advance();
2726                     // int flags = extractor.getSampleFlags();
2727                     // Log.i("@@@@", "read sample " + samplenum + ":" +
2728                     // extractor.getSampleFlags()
2729                     // + " @ " + extractor.getSampleTime() + " size " +
2730                     // sampleSize);
2731 
2732                     if (sampleSize < 0) {
2733                         assertFalse("advance succeeded after failed read", advanceDone);
2734                         Log.d(TAG, "saw input EOS.");
2735                         sawInputEOS = true;
2736                         assertEquals("extractor.readSampleData() must return -1 at end of stream",
2737                                 -1, sampleSize);
2738                         assertEquals("extractor.getSampleTime() must return -1 at end of stream",
2739                                 -1, presentationTimeUs);
2740                         sampleSize = 0; // required otherwise queueInputBuffer
2741                                         // returns invalid.
2742                     } else {
2743                         timestamps.add(presentationTimeUs);
2744                         samplenum++; // increment before comparing with stopAtSample
2745                         if (samplenum == stopAtSample) {
2746                             Log.d(TAG, "saw input EOS (stop at sample).");
2747                             sawInputEOS = true; // tag this sample as EOS
2748                         }
2749                     }
2750                     codec.queueInputBuffer(
2751                             inputBufIndex,
2752                             0 /* offset */,
2753                             sampleSize,
2754                             presentationTimeUs,
2755                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
2756                 } else {
2757                     assertEquals(
2758                             "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex,
2759                             MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex);
2760                 }
2761             }
2762 
2763             // handle output
2764             int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
2765 
2766             deadDecoderCounter++;
2767             if (outputBufIndex >= 0) {
2768                 if (info.size > 0) { // Disregard 0-sized buffers at the end.
2769                     deadDecoderCounter = 0;
2770                     if (resetMode != RESET_MODE_NONE) {
2771                         // once we've gotten some data out of the decoder, reset
2772                         // and start again
2773                         if (resetMode == RESET_MODE_RECONFIGURE) {
2774                             codec.stop();
2775                             codec.configure(format, surface /* surface */, null /* crypto */,
2776                                     0 /* flags */);
2777                             codec.start();
2778                             codecInputBuffers = codec.getInputBuffers();
2779                             codecOutputBuffers = codec.getOutputBuffers();
2780                         } else if (resetMode == RESET_MODE_FLUSH) {
2781                             codec.flush();
2782                         } else {
2783                             fail("unknown resetMode: " + resetMode);
2784                         }
2785                         // restart at beginning, clear resetMode
2786                         resetMode = RESET_MODE_NONE;
2787                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
2788                         sawInputEOS = false;
2789                         numframes = 0;
2790                         timestamps.clear();
2791                         if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
2792                             outputTimestamps.clear();
2793                         }
2794                         if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
2795                             outputChecksums.clear();
2796                         }
2797                         continue;
2798                     }
2799                     if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) {
2800                         assertTrue("number of frames (" + numframes
2801                                 + ") exceeds number of reference timestamps",
2802                                 numframes < outputTimestamps.size());
2803                         assertEquals("frame ts mismatch at frame " + numframes,
2804                                 (long) outputTimestamps.get(numframes), info.presentationTimeUs);
2805                     } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
2806                         outputTimestamps.add(info.presentationTimeUs);
2807                     }
2808                     if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) {
2809                         long sum = 0;   // note: checksum is 0 if buffer format unrecognized
2810                         if (dochecksum) {
2811                             Image image = codec.getOutputImage(outputBufIndex);
2812                             // use image to do crc if it's available
2813                             // fall back to buffer if image is not available
2814                             if (image != null) {
2815                                 sum = checksum(image);
2816                             } else {
2817                                 // TODO: add stride - right now just use info.size (as before)
2818                                 //sum = checksum(codecOutputBuffers[outputBufIndex], width, height,
2819                                 //        stride);
2820                                 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufIndex);
2821                                 outputBuffer.position(info.offset);
2822                                 sum = checksum(outputBuffer, info.size);
2823                             }
2824                         }
2825                         if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) {
2826                             assertTrue("number of frames (" + numframes
2827                                     + ") exceeds number of reference checksums",
2828                                     numframes < outputChecksums.size());
2829                             Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes)
2830                                     + " new checksum: " + sum);
2831                             assertEquals("frame data mismatch at frame " + numframes,
2832                                     (long) outputChecksums.get(numframes), sum);
2833                         } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
2834                             outputChecksums.add(sum);
2835                         }
2836                     }
2837                     if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) {
2838                         assertTrue("output timestamp " + info.presentationTimeUs
2839                                 + " without corresponding input timestamp"
2840                                 , timestamps.remove(info.presentationTimeUs));
2841                     }
2842                     outputSize += info.size;
2843                     numframes++;
2844                 }
2845                 // Log.d(TAG, "got frame, size " + info.size + "/" +
2846                 // info.presentationTimeUs +
2847                 // "/" + numframes + "/" + info.flags);
2848                 codec.releaseOutputBuffer(outputBufIndex, true /* render */);
2849                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
2850                     Log.d(TAG, "saw output EOS.");
2851                     sawOutputEOS = true;
2852                 }
2853             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
2854                 codecOutputBuffers = codec.getOutputBuffers();
2855                 Log.d(TAG, "output buffers have changed.");
2856             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
2857                 MediaFormat oformat = codec.getOutputFormat();
2858                 if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) &&
2859                         oformat.containsKey(MediaFormat.KEY_WIDTH) &&
2860                         oformat.containsKey(MediaFormat.KEY_HEIGHT)) {
2861                     int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
2862                     width = oformat.getInteger(MediaFormat.KEY_WIDTH);
2863                     height = oformat.getInteger(MediaFormat.KEY_HEIGHT);
2864                     dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw
2865                                                                   // buf formats
2866                     Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height);
2867                 } else {
2868                     dochecksum = false; // check with audio later
2869                     width = height = 0;
2870                     Log.d(TAG, "output format has changed to (unknown video) " + oformat);
2871                 }
2872             } else {
2873                 assertEquals(
2874                         "codec.dequeueOutputBuffer() unrecognized return index: "
2875                                 + outputBufIndex,
2876                         MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex);
2877             }
2878         }
2879         codec.stop();
2880         codec.release();
2881 
2882         assertTrue("last frame didn't have EOS", sawOutputEOS);
2883         if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) {
2884             assertEquals("I!=O", samplenum, numframes);
2885             if (stopAtSample != 0) {
2886                 assertEquals("did not stop with right number of frames", stopAtSample, numframes);
2887             }
2888         }
2889         return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize :
2890                 (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes :
2891                         0;
2892     }
2893 
2894     public void testEOSBehaviorH264() throws Exception {
2895         // this video has an I frame at 44
2896         testEOSBehavior(
2897                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
2898                 new int[] {1, 44, 45, 55});
2899     }
2900     public void testEOSBehaviorHEVC() throws Exception {
2901         testEOSBehavior(
2902             R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz,
2903             new int[] {1, 17, 23, 49});
2904     }
2905 
2906     public void testEOSBehaviorMpeg2() throws Exception {
2907         testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 17);
2908         testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 23);
2909         testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 49);
2910     }
2911 
2912     public void testEOSBehaviorH263() throws Exception {
2913         // this video has an I frame every 12 frames.
2914         testEOSBehavior(
2915                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
2916                 new int[] {1, 24, 25, 48, 50});
2917     }
2918 
2919     public void testEOSBehaviorMpeg4() throws Exception {
2920         // this video has an I frame every 12 frames
2921         testEOSBehavior(
2922                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
2923                 new int[] {1, 24, 25, 48, 50, 2});
2924     }
2925 
2926     public void testEOSBehaviorVP8() throws Exception {
2927         // this video has an I frame at 46
2928         testEOSBehavior(
2929                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
2930                 new int[] {1, 46, 47, 57, 45});
2931     }
2932 
2933     public void testEOSBehaviorVP9() throws Exception {
2934         // this video has an I frame at 44
2935         testEOSBehavior(
2936                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
2937                 new int[] {1, 44, 45, 55, 43});
2938     }
2939 
2940     public void testEOSBehaviorAV1() throws Exception {
2941         // this video has an I frame at 44
2942         testEOSBehavior(
2943                 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz,
2944                 new int[] {1, 44, 45, 55, 43});
2945     }
2946 
2947     /* from EncodeDecodeTest */
2948     private static boolean isRecognizedFormat(int colorFormat) {
2949         // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
2950         switch (colorFormat) {
2951         // these are the formats we know how to handle for this test
2952             case CodecCapabilities.COLOR_FormatYUV420Planar:
2953             case CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
2954             case CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
2955             case CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
2956             case CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
2957             case CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
2958                 /*
2959                  * TODO: Check newer formats or ignore.
2960                  * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
2961                  * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2
2962                  * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5
2963                  */
2964                 return true;
2965             default:
2966                 return false;
2967         }
2968     }
2969 
2970     private static long checksum(ByteBuffer buf, int size) {
2971         int cap = buf.capacity();
2972         assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap,
2973                 size > 0 && size <= cap);
2974         CRC32 crc = new CRC32();
2975         if (buf.hasArray()) {
2976             crc.update(buf.array(), buf.position() + buf.arrayOffset(), size);
2977         } else {
2978             int pos = buf.position();
2979             final int rdsize = Math.min(4096, size);
2980             byte bb[] = new byte[rdsize];
2981             int chk;
2982             for (int i = 0; i < size; i += chk) {
2983                 chk = Math.min(rdsize, size - i);
2984                 buf.get(bb, 0, chk);
2985                 crc.update(bb, 0, chk);
2986             }
2987             buf.position(pos);
2988         }
2989         return crc.getValue();
2990     }
2991 
2992     private static long checksum(ByteBuffer buf, int width, int height, int stride) {
2993         int cap = buf.capacity();
2994         assertTrue("checksum() params are invalid: w x h , s = "
2995                 + width + " x " + height + " , " + stride + " cap = " + cap,
2996                 width > 0 && width <= stride && height > 0 && height * stride <= cap);
2997         // YUV 4:2:0 should generally have a data storage height 1.5x greater
2998         // than the declared image height, representing the UV planes.
2999         //
3000         // We only check Y frame for now. Somewhat unknown with tiling effects.
3001         //
3002         //long tm = System.nanoTime();
3003         final int lineinterval = 1; // line sampling frequency
3004         CRC32 crc = new CRC32();
3005         if (buf.hasArray()) {
3006             byte b[] = buf.array();
3007             int offs = buf.arrayOffset();
3008             for (int i = 0; i < height; i += lineinterval) {
3009                 crc.update(b, i * stride + offs, width);
3010             }
3011         } else { // almost always ends up here due to direct buffers
3012             int pos = buf.position();
3013             if (true) { // this {} is 80x times faster than else {} below.
3014                 byte[] bb = new byte[width]; // local line buffer
3015                 for (int i = 0; i < height; i += lineinterval) {
3016                     buf.position(pos + i * stride);
3017                     buf.get(bb, 0, width);
3018                     crc.update(bb, 0, width);
3019                 }
3020             } else {
3021                 for (int i = 0; i < height; i += lineinterval) {
3022                     buf.position(pos + i * stride);
3023                     for (int j = 0; j < width; ++j) {
3024                         crc.update(buf.get());
3025                     }
3026                 }
3027             }
3028             buf.position(pos);
3029         }
3030         //tm = System.nanoTime() - tm;
3031         //Log.d(TAG, "checksum time " + tm);
3032         return crc.getValue();
3033     }
3034 
3035     private static long checksum(Image image) {
3036         int format = image.getFormat();
3037         assertEquals("unsupported image format", ImageFormat.YUV_420_888, format);
3038 
3039         CRC32 crc = new CRC32();
3040 
3041         int imageWidth = image.getWidth();
3042         int imageHeight = image.getHeight();
3043 
3044         Image.Plane[] planes = image.getPlanes();
3045         for (int i = 0; i < planes.length; ++i) {
3046             ByteBuffer buf = planes[i].getBuffer();
3047 
3048             int width, height, rowStride, pixelStride, x, y;
3049             rowStride = planes[i].getRowStride();
3050             pixelStride = planes[i].getPixelStride();
3051             if (i == 0) {
3052                 width = imageWidth;
3053                 height = imageHeight;
3054             } else {
3055                 width = imageWidth / 2;
3056                 height = imageHeight /2;
3057             }
3058             // local contiguous pixel buffer
3059             byte[] bb = new byte[width * height];
3060             if (buf.hasArray()) {
3061                 byte b[] = buf.array();
3062                 int offs = buf.arrayOffset();
3063                 if (pixelStride == 1) {
3064                     for (y = 0; y < height; ++y) {
3065                         System.arraycopy(bb, y * width, b, y * rowStride + offs, width);
3066                     }
3067                 } else {
3068                     // do it pixel-by-pixel
3069                     for (y = 0; y < height; ++y) {
3070                         int lineOffset = offs + y * rowStride;
3071                         for (x = 0; x < width; ++x) {
3072                             bb[y * width + x] = b[lineOffset + x * pixelStride];
3073                         }
3074                     }
3075                 }
3076             } else { // almost always ends up here due to direct buffers
3077                 int pos = buf.position();
3078                 if (pixelStride == 1) {
3079                     for (y = 0; y < height; ++y) {
3080                         buf.position(pos + y * rowStride);
3081                         buf.get(bb, y * width, width);
3082                     }
3083                 } else {
3084                     // local line buffer
3085                     byte[] lb = new byte[rowStride];
3086                     // do it pixel-by-pixel
3087                     for (y = 0; y < height; ++y) {
3088                         buf.position(pos + y * rowStride);
3089                         // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
3090                         buf.get(lb, 0, pixelStride * (width - 1) + 1);
3091                         for (x = 0; x < width; ++x) {
3092                             bb[y * width + x] = lb[x * pixelStride];
3093                         }
3094                     }
3095                 }
3096                 buf.position(pos);
3097             }
3098             crc.update(bb, 0, width * height);
3099         }
3100 
3101         return crc.getValue();
3102     }
3103 
3104     public void testFlush() throws Exception {
3105         testFlush(R.raw.loudsoftwav);
3106         testFlush(R.raw.loudsoftogg);
3107         testFlush(R.raw.loudsoftoggmkv);
3108         testFlush(R.raw.loudsoftoggmp4);
3109         testFlush(R.raw.loudsoftmp3);
3110         testFlush(R.raw.loudsoftaac);
3111         testFlush(R.raw.loudsoftfaac);
3112         testFlush(R.raw.loudsoftitunes);
3113     }
3114 
3115     private void testFlush(int resource) throws Exception {
3116 
3117         AssetFileDescriptor testFd = mResources.openRawResourceFd(resource);
3118 
3119         MediaExtractor extractor;
3120         MediaCodec codec;
3121         ByteBuffer[] codecInputBuffers;
3122         ByteBuffer[] codecOutputBuffers;
3123 
3124         extractor = new MediaExtractor();
3125         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
3126                 testFd.getLength());
3127         testFd.close();
3128 
3129         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
3130         MediaFormat format = extractor.getTrackFormat(0);
3131         String mime = format.getString(MediaFormat.KEY_MIME);
3132         assertTrue("not an audio file", mime.startsWith("audio/"));
3133 
3134         codec = MediaCodec.createDecoderByType(mime);
3135         assertNotNull("couldn't find codec " + mime, codec);
3136 
3137         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
3138         codec.start();
3139         codecInputBuffers = codec.getInputBuffers();
3140         codecOutputBuffers = codec.getOutputBuffers();
3141 
3142         extractor.selectTrack(0);
3143 
3144         // decode a bit of the first part of the file, and verify the amplitude
3145         short maxvalue1 = getAmplitude(extractor, codec);
3146 
3147         // flush the codec and seek the extractor a different position, then decode a bit more
3148         // and check the amplitude
3149         extractor.seekTo(8000000, 0);
3150         codec.flush();
3151         short maxvalue2 = getAmplitude(extractor, codec);
3152 
3153         assertTrue("first section amplitude too low", maxvalue1 > 20000);
3154         assertTrue("second section amplitude too high", maxvalue2 < 5000);
3155         codec.stop();
3156         codec.release();
3157 
3158     }
3159 
3160     private short getAmplitude(MediaExtractor extractor, MediaCodec codec) {
3161         short maxvalue = 0;
3162         int numBytesDecoded = 0;
3163         final long kTimeOutUs = 5000;
3164         ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
3165         ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
3166         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
3167 
3168         while(numBytesDecoded < 44100 * 2) {
3169             int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
3170 
3171             if (inputBufIndex >= 0) {
3172                 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
3173 
3174                 int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */);
3175                 long presentationTimeUs = extractor.getSampleTime();
3176 
3177                 codec.queueInputBuffer(
3178                         inputBufIndex,
3179                         0 /* offset */,
3180                         sampleSize,
3181                         presentationTimeUs,
3182                         0 /* flags */);
3183 
3184                 extractor.advance();
3185             }
3186             int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
3187 
3188             if (res >= 0) {
3189 
3190                 int outputBufIndex = res;
3191                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
3192 
3193                 buf.position(info.offset);
3194                 for (int i = 0; i < info.size; i += 2) {
3195                     short sample = buf.getShort();
3196                     if (maxvalue < sample) {
3197                         maxvalue = sample;
3198                     }
3199                     int idx = (numBytesDecoded + i) / 2;
3200                 }
3201 
3202                 numBytesDecoded += info.size;
3203 
3204                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
3205             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
3206                 codecOutputBuffers = codec.getOutputBuffers();
3207             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
3208                 MediaFormat oformat = codec.getOutputFormat();
3209             }
3210         }
3211         return maxvalue;
3212     }
3213 
3214     /* return true if a particular video feature is supported for the given mimetype */
3215     private boolean isVideoFeatureSupported(String mimeType, String feature) {
3216         MediaFormat format = MediaFormat.createVideoFormat( mimeType, 1920, 1080);
3217         format.setFeatureEnabled(feature, true);
3218         MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
3219         String codecName = mcl.findDecoderForFormat(format);
3220         return (codecName == null) ? false : true;
3221     }
3222 
3223 
3224     /**
3225      * Test tunneled video playback mode if supported
3226      */
3227     public void testTunneledVideoPlayback() throws Exception {
3228         if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
3229                 CodecCapabilities.FEATURE_TunneledPlayback)) {
3230             MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
3231             return;
3232         }
3233 
3234         AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
3235         mMediaCodecPlayer = new MediaCodecTunneledPlayer(
3236                 getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
3237 
3238         Uri audioUri = Uri.parse(dynamicConfig.getValue(AUDIO_URL_KEY));
3239         Uri videoUri = Uri.parse(dynamicConfig.getValue(VIDEO_URL_KEY));
3240         mMediaCodecPlayer.setAudioDataSource(audioUri, null);
3241         mMediaCodecPlayer.setVideoDataSource(videoUri, null);
3242         assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
3243         assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
3244 
3245         // starts video playback
3246         mMediaCodecPlayer.startThread();
3247 
3248         final long durationMs = mMediaCodecPlayer.getDuration();
3249         final long timeOutMs = System.currentTimeMillis() + durationMs + 5 * 1000; // add 5 sec
3250         while (!mMediaCodecPlayer.isEnded()) {
3251             // Log.d(TAG, "currentPosition: " + mMediaCodecPlayer.getCurrentPosition()
3252             //         + "  duration: " + mMediaCodecPlayer.getDuration());
3253             assertTrue("Tunneled video playback timeout exceeded",
3254                     timeOutMs > System.currentTimeMillis());
3255             Thread.sleep(SLEEP_TIME_MS);
3256             if (mMediaCodecPlayer.getCurrentPosition() >= mMediaCodecPlayer.getDuration()) {
3257                 Log.d(TAG, "testTunneledVideoPlayback -- current pos = " +
3258                         mMediaCodecPlayer.getCurrentPosition() +
3259                         ">= duration = " + mMediaCodecPlayer.getDuration());
3260                 break;
3261             }
3262         }
3263         // mMediaCodecPlayer.reset() handled in TearDown();
3264     }
3265 
3266     /**
3267      * Test tunneled video playback flush if supported
3268      */
3269     public void testTunneledVideoFlush() throws Exception {
3270         if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
3271                 CodecCapabilities.FEATURE_TunneledPlayback)) {
3272             MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
3273             return;
3274         }
3275 
3276         AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
3277         mMediaCodecPlayer = new MediaCodecTunneledPlayer(
3278                 getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
3279 
3280         Uri audioUri = Uri.parse(dynamicConfig.getValue(AUDIO_URL_KEY));
3281         Uri videoUri = Uri.parse(dynamicConfig.getValue(VIDEO_URL_KEY));
3282         mMediaCodecPlayer.setAudioDataSource(audioUri, null);
3283         mMediaCodecPlayer.setVideoDataSource(videoUri, null);
3284         assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
3285         assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
3286 
3287         // starts video playback
3288         mMediaCodecPlayer.startThread();
3289         Thread.sleep(SLEEP_TIME_MS);
3290         mMediaCodecPlayer.pause();
3291         mMediaCodecPlayer.flush();
3292         // mMediaCodecPlayer.reset() handled in TearDown();
3293     }
3294 
3295     /**
3296      * Returns list of CodecCapabilities advertising support for the given MIME type.
3297      */
3298     private static List<CodecCapabilities> getCodecCapabilitiesForMimeType(String mimeType) {
3299         int numCodecs = MediaCodecList.getCodecCount();
3300         List<CodecCapabilities> caps = new ArrayList<CodecCapabilities>();
3301         for (int i = 0; i < numCodecs; i++) {
3302             MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
3303             if (codecInfo.isAlias()) {
3304                 continue;
3305             }
3306             if (codecInfo.isEncoder()) {
3307                 continue;
3308             }
3309 
3310             String[] types = codecInfo.getSupportedTypes();
3311             for (int j = 0; j < types.length; j++) {
3312                 if (types[j].equalsIgnoreCase(mimeType)) {
3313                     caps.add(codecInfo.getCapabilitiesForType(mimeType));
3314                 }
3315             }
3316         }
3317         return caps;
3318     }
3319 
3320     /**
3321      * Returns true if there exists a codec supporting the given MIME type that meets the
3322      * minimum specification for VR high performance requirements.
3323      *
3324      * The requirements are as follows:
3325      *   - At least 243000 blocks per second (where blocks are defined as 16x16 -- note this
3326      *   is equivalent to 1920x1080@30fps)
3327      *   - Feature adaptive-playback present
3328      */
3329     private static boolean doesMimeTypeHaveMinimumSpecVrReadyCodec(String mimeType) {
3330         List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType);
3331         for (CodecCapabilities c : caps) {
3332             if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
3333                 continue;
3334             }
3335 
3336             if (!c.getVideoCapabilities().areSizeAndRateSupported(1920, 1080, 30.0)) {
3337                 continue;
3338             }
3339 
3340             return true;
3341         }
3342 
3343         return false;
3344     }
3345 
3346     /**
3347      * Returns true if there exists a codec supporting the given MIME type that meets VR high
3348      * performance requirements.
3349      *
3350      * The requirements are as follows:
3351      *   - At least 972000 blocks per second (where blocks are defined as 16x16 -- note this
3352      *   is equivalent to 3840x2160@30fps)
3353      *   - At least 4 concurrent instances
3354      *   - Feature adaptive-playback present
3355      */
3356     private static boolean doesMimeTypeHaveVrReadyCodec(String mimeType) {
3357         List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType);
3358         for (CodecCapabilities c : caps) {
3359             if (c.getMaxSupportedInstances() < 4) {
3360                 continue;
3361             }
3362 
3363             if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
3364                 continue;
3365             }
3366 
3367             if (!c.getVideoCapabilities().areSizeAndRateSupported(3840, 2160, 30.0)) {
3368                 continue;
3369             }
3370 
3371             return true;
3372         }
3373 
3374         return false;
3375     }
3376 
3377     public void testVrHighPerformanceH264() throws Exception {
3378         if (!supportsVrHighPerformance()) {
3379             MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
3380             return;
3381         }
3382 
3383         boolean h264IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_AVC);
3384         assertTrue("Did not find a VR ready H.264 decoder", h264IsReady);
3385     }
3386 
3387     public void testVrHighPerformanceHEVC() throws Exception {
3388         if (!supportsVrHighPerformance()) {
3389             MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
3390             return;
3391         }
3392 
3393         // Test minimum mandatory requirements.
3394         assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC));
3395 
3396         boolean hevcIsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC);
3397         if (!hevcIsReady) {
3398             Log.d(TAG, "HEVC isn't required to be VR ready");
3399             return;
3400         }
3401     }
3402 
3403     public void testVrHighPerformanceVP9() throws Exception {
3404         if (!supportsVrHighPerformance()) {
3405             MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present");
3406             return;
3407         }
3408 
3409         // Test minimum mandatory requirements.
3410         assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9));
3411 
3412         boolean vp9IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9);
3413         if (!vp9IsReady) {
3414             Log.d(TAG, "VP9 isn't required to be VR ready");
3415             return;
3416         }
3417     }
3418 
3419     private boolean supportsVrHighPerformance() {
3420         PackageManager pm = mContext.getPackageManager();
3421         return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
3422     }
3423 }
3424