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