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