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 com.android.cts.media.R;
     20 
     21 import android.content.res.AssetFileDescriptor;
     22 import android.content.res.Resources;
     23 import android.graphics.ImageFormat;
     24 import android.media.Image;
     25 import android.media.MediaCodec;
     26 import android.media.MediaCodecInfo;
     27 import android.media.MediaExtractor;
     28 import android.media.MediaFormat;
     29 import android.util.Log;
     30 import android.view.Surface;
     31 
     32 import java.io.BufferedInputStream;
     33 import java.io.IOException;
     34 import java.io.InputStream;
     35 import java.nio.ByteBuffer;
     36 import java.util.ArrayList;
     37 import java.util.Arrays;
     38 import java.util.List;
     39 import java.util.zip.CRC32;
     40 
     41 public class DecoderTest extends MediaPlayerTestBase {
     42     private static final String TAG = "DecoderTest";
     43 
     44     private static final int RESET_MODE_NONE = 0;
     45     private static final int RESET_MODE_RECONFIGURE = 1;
     46     private static final int RESET_MODE_FLUSH = 2;
     47     private static final int RESET_MODE_EOS_FLUSH = 3;
     48 
     49     private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
     50 
     51     private static final int CONFIG_MODE_NONE = 0;
     52     private static final int CONFIG_MODE_QUEUE = 1;
     53 
     54     private Resources mResources;
     55     short[] mMasterBuffer;
     56 
     57     @Override
     58     protected void setUp() throws Exception {
     59         super.setUp();
     60         mResources = mContext.getResources();
     61 
     62         // read master file into memory
     63         AssetFileDescriptor masterFd = mResources.openRawResourceFd(R.raw.sinesweepraw);
     64         long masterLength = masterFd.getLength();
     65         mMasterBuffer = new short[(int) (masterLength / 2)];
     66         InputStream is = masterFd.createInputStream();
     67         BufferedInputStream bis = new BufferedInputStream(is);
     68         for (int i = 0; i < mMasterBuffer.length; i++) {
     69             int lo = bis.read();
     70             int hi = bis.read();
     71             if (hi >= 128) {
     72                 hi -= 256;
     73             }
     74             int sample = hi * 256 + lo;
     75             mMasterBuffer[i] = (short) sample;
     76         }
     77         bis.close();
     78         masterFd.close();
     79     }
     80 
     81     // TODO: add similar tests for other audio and video formats
     82     public void testBug11696552() throws Exception {
     83         MediaCodec mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm");
     84         MediaFormat mFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 48000, 2);
     85         mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} ));
     86         mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
     87         mMediaCodec.configure(mFormat, null, null, 0);
     88         mMediaCodec.start();
     89         int index = mMediaCodec.dequeueInputBuffer(250000);
     90         mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM );
     91         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
     92         mMediaCodec.dequeueOutputBuffer(info, 250000);
     93     }
     94 
     95     // The allowed errors in the following tests are the actual maximum measured
     96     // errors with the standard decoders, plus 10%.
     97     // This should allow for some variation in decoders, while still detecting
     98     // phase and delay errors, channel swap, etc.
     99     public void testDecodeMp3Lame() throws Exception {
    100         decode(R.raw.sinesweepmp3lame, 804.f);
    101         testTimeStampOrdering(R.raw.sinesweepmp3lame);
    102     }
    103     public void testDecodeMp3Smpb() throws Exception {
    104         decode(R.raw.sinesweepmp3smpb, 413.f);
    105         testTimeStampOrdering(R.raw.sinesweepmp3smpb);
    106     }
    107     public void testDecodeM4a() throws Exception {
    108         decode(R.raw.sinesweepm4a, 124.f);
    109         testTimeStampOrdering(R.raw.sinesweepm4a);
    110     }
    111     public void testDecodeOgg() throws Exception {
    112         decode(R.raw.sinesweepogg, 168.f);
    113         testTimeStampOrdering(R.raw.sinesweepogg);
    114     }
    115     public void testDecodeWav() throws Exception {
    116         decode(R.raw.sinesweepwav, 0.0f);
    117         testTimeStampOrdering(R.raw.sinesweepwav);
    118     }
    119     public void testDecodeFlac() throws Exception {
    120         decode(R.raw.sinesweepflac, 0.0f);
    121         testTimeStampOrdering(R.raw.sinesweepflac);
    122     }
    123 
    124     public void testDecodeMonoMp3() throws Exception {
    125         monoTest(R.raw.monotestmp3);
    126         testTimeStampOrdering(R.raw.monotestmp3);
    127     }
    128 
    129     public void testDecodeMonoM4a() throws Exception {
    130         monoTest(R.raw.monotestm4a);
    131         testTimeStampOrdering(R.raw.monotestm4a);
    132     }
    133 
    134     public void testDecodeMonoOgg() throws Exception {
    135         monoTest(R.raw.monotestogg);
    136         testTimeStampOrdering(R.raw.monotestogg);
    137     }
    138 
    139     public void testDecodeAacTs() throws Exception {
    140         testTimeStampOrdering(R.raw.sinesweeptsaac);
    141     }
    142 
    143     public void testDecode51M4a() throws Exception {
    144         decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
    145     }
    146 
    147     private void testTimeStampOrdering(int res) throws Exception {
    148         List<Long> timestamps = new ArrayList<Long>();
    149         decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps);
    150         Long lastTime = Long.MIN_VALUE;
    151         for (int i = 0; i < timestamps.size(); i++) {
    152             Long thisTime = timestamps.get(i);
    153             assertTrue("timetravel occurred: " + lastTime + " > " + thisTime, thisTime >= lastTime);
    154             lastTime = thisTime;
    155         }
    156     }
    157 
    158     public void testTrackSelection() throws Exception {
    159         testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz);
    160         testTrackSelection(
    161                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
    162         testTrackSelection(
    163                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
    164     }
    165 
    166     public void testBFrames() throws Exception {
    167         testBFrames(R.raw.video_h264_main_b_frames);
    168         testBFrames(R.raw.video_h264_main_b_frames_frag);
    169     }
    170 
    171     public void testBFrames(int res) throws Exception {
    172         AssetFileDescriptor fd = mResources.openRawResourceFd(res);
    173         MediaExtractor ex = new MediaExtractor();
    174         ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
    175         MediaFormat format = ex.getTrackFormat(0);
    176         String mime = format.getString(MediaFormat.KEY_MIME);
    177         assertTrue("not a video track. Wrong test file?", mime.startsWith("video/"));
    178         MediaCodec dec = MediaCodec.createDecoderByType(mime);
    179         Surface s = getActivity().getSurfaceHolder().getSurface();
    180         dec.configure(format, s, null, 0);
    181         dec.start();
    182         ByteBuffer[] buf = dec.getInputBuffers();
    183         ex.selectTrack(0);
    184         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    185         long lastPresentationTimeUsFromExtractor = -1;
    186         long lastPresentationTimeUsFromDecoder = -1;
    187         boolean inputoutoforder = false;
    188         while(true) {
    189             int flags = ex.getSampleFlags();
    190             long time = ex.getSampleTime();
    191             if (time >= 0 && time < lastPresentationTimeUsFromExtractor) {
    192                 inputoutoforder = true;
    193             }
    194             lastPresentationTimeUsFromExtractor = time;
    195             int bufidx = dec.dequeueInputBuffer(5000);
    196             if (bufidx >= 0) {
    197                 int n = ex.readSampleData(buf[bufidx], 0);
    198                 if (n < 0) {
    199                     flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
    200                     time = 0;
    201                     n = 0;
    202                 }
    203                 dec.queueInputBuffer(bufidx, 0, n, time, flags);
    204                 ex.advance();
    205             }
    206             int status = dec.dequeueOutputBuffer(info, 5000);
    207             if (status >= 0) {
    208                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    209                     break;
    210                 }
    211                 assertTrue("out of order timestamp from decoder",
    212                         info.presentationTimeUs > lastPresentationTimeUsFromDecoder);
    213                 dec.releaseOutputBuffer(status, true);
    214                 lastPresentationTimeUsFromDecoder = info.presentationTimeUs;
    215             }
    216         }
    217         assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder);
    218         dec.release();
    219         ex.release();
    220       }
    221 
    222     private void testTrackSelection(int resid) throws Exception {
    223         AssetFileDescriptor fd1 = null;
    224         try {
    225             fd1 = mResources.openRawResourceFd(resid);
    226             MediaExtractor ex1 = new MediaExtractor();
    227             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    228 
    229             ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
    230             ArrayList<Integer> vid = new ArrayList<Integer>();
    231             ArrayList<Integer> aud = new ArrayList<Integer>();
    232 
    233             // scan the file once and build lists of audio and video samples
    234             ex1.selectTrack(0);
    235             ex1.selectTrack(1);
    236             while(true) {
    237                 int n1 = ex1.readSampleData(buf1, 0);
    238                 if (n1 < 0) {
    239                     break;
    240                 }
    241                 int idx = ex1.getSampleTrackIndex();
    242                 if (idx == 0) {
    243                     vid.add(n1);
    244                 } else if (idx == 1) {
    245                     aud.add(n1);
    246                 } else {
    247                     fail("unexpected track index: " + idx);
    248                 }
    249                 ex1.advance();
    250             }
    251 
    252             // read the video track once, then rewind and do it again, and
    253             // verify we get the right samples
    254             ex1.release();
    255             ex1 = new MediaExtractor();
    256             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    257             ex1.selectTrack(0);
    258             for (int i = 0; i < 2; i++) {
    259                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    260                 int idx = 0;
    261                 while(true) {
    262                     int n1 = ex1.readSampleData(buf1, 0);
    263                     if (n1 < 0) {
    264                         assertEquals(vid.size(), idx);
    265                         break;
    266                     }
    267                     assertEquals(vid.get(idx++).intValue(), n1);
    268                     ex1.advance();
    269                 }
    270             }
    271 
    272             // read the audio track once, then rewind and do it again, and
    273             // verify we get the right samples
    274             ex1.release();
    275             ex1 = new MediaExtractor();
    276             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    277             ex1.selectTrack(1);
    278             for (int i = 0; i < 2; i++) {
    279                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    280                 int idx = 0;
    281                 while(true) {
    282                     int n1 = ex1.readSampleData(buf1, 0);
    283                     if (n1 < 0) {
    284                         assertEquals(aud.size(), idx);
    285                         break;
    286                     }
    287                     assertEquals(aud.get(idx++).intValue(), n1);
    288                     ex1.advance();
    289                 }
    290             }
    291 
    292             // read the video track first, then rewind and get the audio track instead, and
    293             // verify we get the right samples
    294             ex1.release();
    295             ex1 = new MediaExtractor();
    296             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    297             for (int i = 0; i < 2; i++) {
    298                 ex1.selectTrack(i);
    299                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    300                 int idx = 0;
    301                 while(true) {
    302                     int n1 = ex1.readSampleData(buf1, 0);
    303                     if (i == 0) {
    304                         if (n1 < 0) {
    305                             assertEquals(vid.size(), idx);
    306                             break;
    307                         }
    308                         assertEquals(vid.get(idx++).intValue(), n1);
    309                     } else if (i == 1) {
    310                         if (n1 < 0) {
    311                             assertEquals(aud.size(), idx);
    312                             break;
    313                         }
    314                         assertEquals(aud.get(idx++).intValue(), n1);
    315                     } else {
    316                         fail("unexpected track index: " + idx);
    317                     }
    318                     ex1.advance();
    319                 }
    320                 ex1.unselectTrack(i);
    321             }
    322 
    323             // read the video track first, then rewind, enable the audio track in addition
    324             // to the video track, and verify we get the right samples
    325             ex1.release();
    326             ex1 = new MediaExtractor();
    327             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    328             for (int i = 0; i < 2; i++) {
    329                 ex1.selectTrack(i);
    330                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    331                 int vididx = 0;
    332                 int audidx = 0;
    333                 while(true) {
    334                     int n1 = ex1.readSampleData(buf1, 0);
    335                     if (n1 < 0) {
    336                         // we should have read all audio and all video samples at this point
    337                         assertEquals(vid.size(), vididx);
    338                         if (i == 1) {
    339                             assertEquals(aud.size(), audidx);
    340                         }
    341                         break;
    342                     }
    343                     int trackidx = ex1.getSampleTrackIndex();
    344                     if (trackidx == 0) {
    345                         assertEquals(vid.get(vididx++).intValue(), n1);
    346                     } else if (trackidx == 1) {
    347                         assertEquals(aud.get(audidx++).intValue(), n1);
    348                     } else {
    349                         fail("unexpected track index: " + trackidx);
    350                     }
    351                     ex1.advance();
    352                 }
    353             }
    354 
    355             // read both tracks from the start, then rewind and verify we get the right
    356             // samples both times
    357             ex1.release();
    358             ex1 = new MediaExtractor();
    359             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    360             for (int i = 0; i < 2; i++) {
    361                 ex1.selectTrack(0);
    362                 ex1.selectTrack(1);
    363                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    364                 int vididx = 0;
    365                 int audidx = 0;
    366                 while(true) {
    367                     int n1 = ex1.readSampleData(buf1, 0);
    368                     if (n1 < 0) {
    369                         // we should have read all audio and all video samples at this point
    370                         assertEquals(vid.size(), vididx);
    371                         assertEquals(aud.size(), audidx);
    372                         break;
    373                     }
    374                     int trackidx = ex1.getSampleTrackIndex();
    375                     if (trackidx == 0) {
    376                         assertEquals(vid.get(vididx++).intValue(), n1);
    377                     } else if (trackidx == 1) {
    378                         assertEquals(aud.get(audidx++).intValue(), n1);
    379                     } else {
    380                         fail("unexpected track index: " + trackidx);
    381                     }
    382                     ex1.advance();
    383                 }
    384             }
    385 
    386         } finally {
    387             if (fd1 != null) {
    388                 fd1.close();
    389             }
    390         }
    391     }
    392 
    393     public void testDecodeFragmented() throws Exception {
    394         testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
    395                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
    396         testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
    397                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
    398     }
    399 
    400     private void testDecodeFragmented(int reference, int teststream) throws Exception {
    401         AssetFileDescriptor fd1 = null;
    402         AssetFileDescriptor fd2 = null;
    403         try {
    404             fd1 = mResources.openRawResourceFd(reference);
    405             MediaExtractor ex1 = new MediaExtractor();
    406             ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
    407 
    408             fd2 = mResources.openRawResourceFd(teststream);
    409             MediaExtractor ex2 = new MediaExtractor();
    410             ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength());
    411 
    412             assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount());
    413 
    414             ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
    415             ByteBuffer buf2 = ByteBuffer.allocate(1024*1024);
    416 
    417             for (int i = 0; i < ex1.getTrackCount(); i++) {
    418                 // note: this assumes the tracks are reported in the order in which they appear
    419                 // in the file.
    420                 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    421                 ex1.selectTrack(i);
    422                 ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    423                 ex2.selectTrack(i);
    424 
    425                 while(true) {
    426                     int n1 = ex1.readSampleData(buf1, 0);
    427                     int n2 = ex2.readSampleData(buf2, 0);
    428                     assertEquals("different buffer size on track " + i, n1, n2);
    429 
    430                     if (n1 < 0) {
    431                         break;
    432                     }
    433                     // see bug 13008204
    434                     buf1.limit(n1);
    435                     buf2.limit(n2);
    436                     buf1.rewind();
    437                     buf2.rewind();
    438 
    439                     assertEquals("limit does not match return value on track " + i,
    440                             n1, buf1.limit());
    441                     assertEquals("limit does not match return value on track " + i,
    442                             n2, buf2.limit());
    443 
    444                     assertEquals("buffer data did not match on track " + i, buf1, buf2);
    445 
    446                     ex1.advance();
    447                     ex2.advance();
    448                 }
    449                 ex1.unselectTrack(i);
    450                 ex2.unselectTrack(i);
    451             }
    452         } finally {
    453             if (fd1 != null) {
    454                 fd1.close();
    455             }
    456             if (fd2 != null) {
    457                 fd2.close();
    458             }
    459         }
    460     }
    461 
    462 
    463     private void monoTest(int res) throws Exception {
    464         short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
    465         if (mono.length == 44100) {
    466             // expected
    467         } else if (mono.length == 88200) {
    468             // the decoder output 2 channels instead of 1, check that the left and right channel
    469             // are identical
    470             for (int i = 0; i < mono.length; i += 2) {
    471                 assertEquals("mismatched samples at " + i, mono[i], mono[i+1]);
    472             }
    473         } else {
    474             fail("wrong number of samples: " + mono.length);
    475         }
    476 
    477         short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null);
    478         assertTrue(Arrays.equals(mono, mono2));
    479 
    480         short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null);
    481         assertTrue(Arrays.equals(mono, mono3));
    482     }
    483 
    484     /**
    485      * @param testinput the file to decode
    486      * @param maxerror the maximum allowed root mean squared error
    487      * @throws IOException
    488      */
    489     private void decode(int testinput, float maxerror) throws IOException {
    490 
    491         short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
    492 
    493         assertEquals("wrong data size", mMasterBuffer.length, decoded.length);
    494 
    495         long totalErrorSquared = 0;
    496 
    497         for (int i = 0; i < decoded.length; i++) {
    498             short sample = decoded[i];
    499             short mastersample = mMasterBuffer[i];
    500             int d = sample - mastersample;
    501             totalErrorSquared += d * d;
    502         }
    503 
    504         long avgErrorSquared = (totalErrorSquared / decoded.length);
    505         double rmse = Math.sqrt(avgErrorSquared);
    506         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
    507 
    508         int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE,
    509                 RESET_MODE_FLUSH, RESET_MODE_EOS_FLUSH };
    510         int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE };
    511 
    512         for (int conf : configModes) {
    513             for (int reset : resetModes) {
    514                 if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) {
    515                     // default case done outside of loop
    516                     continue;
    517                 }
    518                 if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) {
    519                     continue;
    520                 }
    521 
    522                 String params = String.format("(using reset: %d, config: %s)", reset, conf);
    523                 short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null);
    524                 assertEquals("count different with reconfigure" + params,
    525                         decoded.length, decoded2.length);
    526                 for (int i = 0; i < decoded.length; i++) {
    527                     assertEquals("samples don't match" + params, decoded[i], decoded2[i]);
    528                 }
    529             }
    530         }
    531     }
    532 
    533     private boolean hasAudioCsd(int testinput) throws IOException {
    534         AssetFileDescriptor fd = null;
    535         try {
    536 
    537             fd = mResources.openRawResourceFd(testinput);
    538             MediaExtractor extractor = new MediaExtractor();
    539             extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
    540             MediaFormat format = extractor.getTrackFormat(0);
    541 
    542             return format.containsKey(CSD_KEYS[0]);
    543 
    544         } finally {
    545             if (fd != null) {
    546                 fd.close();
    547             }
    548         }
    549     }
    550 
    551     private short[] decodeToMemory(int testinput, int resetMode, int configMode,
    552             int eossample, List<Long> timestamps) throws IOException {
    553 
    554         String localTag = TAG + "#decodeToMemory";
    555         Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode));
    556         short [] decoded = new short[0];
    557         int decodedIdx = 0;
    558 
    559         AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput);
    560 
    561         MediaExtractor extractor;
    562         MediaCodec codec;
    563         ByteBuffer[] codecInputBuffers;
    564         ByteBuffer[] codecOutputBuffers;
    565 
    566         extractor = new MediaExtractor();
    567         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
    568                 testFd.getLength());
    569         testFd.close();
    570 
    571         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
    572         MediaFormat format = extractor.getTrackFormat(0);
    573         String mime = format.getString(MediaFormat.KEY_MIME);
    574         assertTrue("not an audio file", mime.startsWith("audio/"));
    575 
    576         MediaFormat configFormat = format;
    577         codec = MediaCodec.createDecoderByType(mime);
    578         if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) {
    579             configFormat = MediaFormat.createAudioFormat(mime,
    580                     format.getInteger(MediaFormat.KEY_SAMPLE_RATE),
    581                     format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
    582 
    583             configFormat.setLong(MediaFormat.KEY_DURATION,
    584                     format.getLong(MediaFormat.KEY_DURATION));
    585             String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" };
    586             for (String k : keys) {
    587                 if (format.containsKey(k)) {
    588                     configFormat.setInteger(k, format.getInteger(k));
    589                 }
    590             }
    591         }
    592         Log.v(localTag, "configuring with " + configFormat);
    593         codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
    594 
    595         codec.start();
    596         codecInputBuffers = codec.getInputBuffers();
    597         codecOutputBuffers = codec.getOutputBuffers();
    598 
    599         if (resetMode == RESET_MODE_RECONFIGURE) {
    600             codec.stop();
    601             codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
    602             codec.start();
    603             codecInputBuffers = codec.getInputBuffers();
    604             codecOutputBuffers = codec.getOutputBuffers();
    605         } else if (resetMode == RESET_MODE_FLUSH) {
    606             codec.flush();
    607         }
    608 
    609         extractor.selectTrack(0);
    610 
    611         if (configMode == CONFIG_MODE_QUEUE) {
    612             queueConfig(codec, format);
    613         }
    614 
    615         // start decoding
    616         final long kTimeOutUs = 5000;
    617         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    618         boolean sawInputEOS = false;
    619         boolean sawOutputEOS = false;
    620         int noOutputCounter = 0;
    621         int samplecounter = 0;
    622         while (!sawOutputEOS && noOutputCounter < 50) {
    623             noOutputCounter++;
    624             if (!sawInputEOS) {
    625                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
    626 
    627                 if (inputBufIndex >= 0) {
    628                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
    629 
    630                     int sampleSize =
    631                         extractor.readSampleData(dstBuf, 0 /* offset */);
    632 
    633                     long presentationTimeUs = 0;
    634 
    635                     if (sampleSize < 0 && eossample > 0) {
    636                         fail("test is broken: never reached eos sample");
    637                     }
    638                     if (sampleSize < 0) {
    639                         Log.d(TAG, "saw input EOS.");
    640                         sawInputEOS = true;
    641                         sampleSize = 0;
    642                     } else {
    643                         if (samplecounter == eossample) {
    644                             sawInputEOS = true;
    645                         }
    646                         samplecounter++;
    647                         presentationTimeUs = extractor.getSampleTime();
    648                     }
    649                     codec.queueInputBuffer(
    650                             inputBufIndex,
    651                             0 /* offset */,
    652                             sampleSize,
    653                             presentationTimeUs,
    654                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
    655 
    656                     if (!sawInputEOS) {
    657                         extractor.advance();
    658                     }
    659                 }
    660             }
    661 
    662             int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
    663 
    664             if (res >= 0) {
    665                 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
    666 
    667                 if (info.size > 0) {
    668                     noOutputCounter = 0;
    669                     if (timestamps != null) {
    670                         timestamps.add(info.presentationTimeUs);
    671                     }
    672                 }
    673                 if (info.size > 0 &&
    674                         resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) {
    675                     // once we've gotten some data out of the decoder, reset and start again
    676                     if (resetMode == RESET_MODE_RECONFIGURE) {
    677                         codec.stop();
    678                         codec.configure(configFormat, null /* surface */, null /* crypto */,
    679                                 0 /* flags */);
    680                         codec.start();
    681                         codecInputBuffers = codec.getInputBuffers();
    682                         codecOutputBuffers = codec.getOutputBuffers();
    683                         if (configMode == CONFIG_MODE_QUEUE) {
    684                             queueConfig(codec, format);
    685                         }
    686                     } else /* resetMode == RESET_MODE_FLUSH */ {
    687                         codec.flush();
    688                     }
    689                     resetMode = RESET_MODE_NONE;
    690                     extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    691                     sawInputEOS = false;
    692                     samplecounter = 0;
    693                     if (timestamps != null) {
    694                         timestamps.clear();
    695                     }
    696                     continue;
    697                 }
    698 
    699                 int outputBufIndex = res;
    700                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
    701 
    702                 if (decodedIdx + (info.size / 2) >= decoded.length) {
    703                     decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
    704                 }
    705 
    706                 buf.position(info.offset);
    707                 for (int i = 0; i < info.size; i += 2) {
    708                     decoded[decodedIdx++] = buf.getShort();
    709                 }
    710 
    711                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
    712 
    713                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    714                     Log.d(TAG, "saw output EOS.");
    715                     if (resetMode == RESET_MODE_EOS_FLUSH) {
    716                         resetMode = RESET_MODE_NONE;
    717                         codec.flush();
    718                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
    719                         sawInputEOS = false;
    720                         samplecounter = 0;
    721                         decoded = new short[0];
    722                         decodedIdx = 0;
    723                         if (timestamps != null) {
    724                             timestamps.clear();
    725                         }
    726                     } else {
    727                         sawOutputEOS = true;
    728                     }
    729                 }
    730             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
    731                 codecOutputBuffers = codec.getOutputBuffers();
    732 
    733                 Log.d(TAG, "output buffers have changed.");
    734             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    735                 MediaFormat oformat = codec.getOutputFormat();
    736 
    737                 Log.d(TAG, "output format has changed to " + oformat);
    738             } else {
    739                 Log.d(TAG, "dequeueOutputBuffer returned " + res);
    740             }
    741         }
    742         if (noOutputCounter >= 50) {
    743             fail("decoder stopped outputing data");
    744         }
    745 
    746         codec.stop();
    747         codec.release();
    748         return decoded;
    749     }
    750 
    751     private void queueConfig(MediaCodec codec, MediaFormat format) {
    752         for (String csdKey : CSD_KEYS) {
    753             if (!format.containsKey(csdKey)) {
    754                 continue;
    755             }
    756             ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
    757             int inputBufIndex = codec.dequeueInputBuffer(-1);
    758             if (inputBufIndex < 0) {
    759                 fail("failed to queue configuration buffer " + csdKey);
    760             } else {
    761                 ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind();
    762                 Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd));
    763                 codecInputBuffers[inputBufIndex].put(csd);
    764                 codec.queueInputBuffer(
    765                         inputBufIndex,
    766                         0 /* offset */,
    767                         csd.limit(),
    768                         0 /* presentation time (us) */,
    769                         MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
    770             }
    771         }
    772     }
    773 
    774     public void testDecodeWithEOSOnLastBuffer() throws Exception {
    775         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
    776         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
    777         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
    778         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
    779         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
    780         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
    781     }
    782 
    783     /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
    784      * input buffer after all the full ones. */
    785     private void testDecodeWithEOSOnLastBuffer(int res) throws Exception {
    786         int numsamples = countSamples(res);
    787         assertTrue(numsamples != 0);
    788 
    789         List<Long> timestamps1 = new ArrayList<Long>();
    790         short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1);
    791 
    792         List<Long> timestamps2 = new ArrayList<Long>();
    793         short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1,
    794                 timestamps2);
    795 
    796         // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last
    797         assertEquals(decode1.length, decode2.length);
    798         assertTrue(Arrays.equals(decode1, decode2));
    799         assertEquals(timestamps1.size(), timestamps2.size());
    800         assertTrue(timestamps1.equals(timestamps2));
    801 
    802         // ... and that this is also true when reconfiguring the codec
    803         timestamps2.clear();
    804         decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2);
    805         assertTrue(Arrays.equals(decode1, decode2));
    806         assertTrue(timestamps1.equals(timestamps2));
    807         timestamps2.clear();
    808         decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1,
    809                 timestamps2);
    810         assertEquals(decode1.length, decode2.length);
    811         assertTrue(Arrays.equals(decode1, decode2));
    812         assertTrue(timestamps1.equals(timestamps2));
    813 
    814         // ... and that this is also true when flushing the codec
    815         timestamps2.clear();
    816         decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2);
    817         assertTrue(Arrays.equals(decode1, decode2));
    818         assertTrue(timestamps1.equals(timestamps2));
    819         timestamps2.clear();
    820         decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1,
    821                 timestamps2);
    822         assertEquals(decode1.length, decode2.length);
    823         assertTrue(Arrays.equals(decode1, decode2));
    824         assertTrue(timestamps1.equals(timestamps2));
    825     }
    826 
    827     private int countSamples(int res) throws IOException {
    828         AssetFileDescriptor testFd = mResources.openRawResourceFd(res);
    829 
    830         MediaExtractor extractor = new MediaExtractor();
    831         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
    832                 testFd.getLength());
    833         testFd.close();
    834         extractor.selectTrack(0);
    835         int numsamples = 0;
    836         while (extractor.advance()) {
    837             numsamples++;
    838         }
    839         return numsamples;
    840     }
    841 
    842     public void testCodecBasicH264() throws Exception {
    843         Surface s = getActivity().getSurfaceHolder().getSurface();
    844         int frames1 = countFrames(
    845                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
    846                 RESET_MODE_NONE, -1 /* eosframe */, s);
    847         assertEquals("wrong number of frames decoded", 240, frames1);
    848 
    849         int frames2 = countFrames(
    850                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
    851                 RESET_MODE_NONE, -1 /* eosframe */, null);
    852         assertEquals("different number of frames when using Surface", frames1, frames2);
    853     }
    854 
    855     public void testCodecBasicHEVC() throws Exception {
    856         Surface s = getActivity().getSurfaceHolder().getSurface();
    857         int frames1 = countFrames(
    858                 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
    859                 RESET_MODE_NONE, -1 /* eosframe */, s);
    860         assertEquals("wrong number of frames decoded", 300, frames1);
    861 
    862         int frames2 = countFrames(
    863                 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
    864                 RESET_MODE_NONE, -1 /* eosframe */, null);
    865         assertEquals("different number of frames when using Surface", frames1, frames2);
    866     }
    867 
    868     public void testCodecBasicH263() throws Exception {
    869         Surface s = getActivity().getSurfaceHolder().getSurface();
    870         int frames1 = countFrames(
    871                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
    872                 RESET_MODE_NONE, -1 /* eosframe */, s);
    873         assertEquals("wrong number of frames decoded", 122, frames1);
    874 
    875         int frames2 = countFrames(
    876                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
    877                 RESET_MODE_NONE, -1 /* eosframe */, null);
    878         assertEquals("different number of frames when using Surface", frames1, frames2);
    879     }
    880 
    881     public void testCodecBasicMpeg4() throws Exception {
    882         Surface s = getActivity().getSurfaceHolder().getSurface();
    883         int frames1 = countFrames(
    884                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
    885                 RESET_MODE_NONE, -1 /* eosframe */, s);
    886         assertEquals("wrong number of frames decoded", 249, frames1);
    887 
    888         int frames2 = countFrames(
    889                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
    890                 RESET_MODE_NONE, -1 /* eosframe */, null);
    891         assertEquals("different number of frames when using Surface", frames1, frames2);
    892     }
    893 
    894     public void testCodecBasicVP8() throws Exception {
    895         Surface s = getActivity().getSurfaceHolder().getSurface();
    896         int frames1 = countFrames(
    897                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    898                 RESET_MODE_NONE, -1 /* eosframe */, s);
    899         assertEquals("wrong number of frames decoded", 240, frames1);
    900 
    901         int frames2 = countFrames(
    902                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    903                 RESET_MODE_NONE, -1 /* eosframe */, null);
    904         assertEquals("different number of frames when using Surface", frames1, frames2);
    905     }
    906 
    907     public void testCodecBasicVP9() throws Exception {
    908         Surface s = getActivity().getSurfaceHolder().getSurface();
    909         int frames1 = countFrames(
    910                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    911                 RESET_MODE_NONE, -1 /* eosframe */, s);
    912         assertEquals("wrong number of frames decoded", 240, frames1);
    913 
    914         int frames2 = countFrames(
    915                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    916                 RESET_MODE_NONE, -1 /* eosframe */, null);
    917         assertEquals("different number of frames when using Surface", frames1, frames2);
    918     }
    919 
    920     public void testCodecEarlyEOSH263() throws Exception {
    921         Surface s = getActivity().getSurfaceHolder().getSurface();
    922         int frames1 = countFrames(
    923                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
    924                 RESET_MODE_NONE, 64 /* eosframe */, s);
    925         assertEquals("wrong number of frames decoded", 64, frames1);
    926     }
    927 
    928     public void testCodecEarlyEOSH264() throws Exception {
    929         Surface s = getActivity().getSurfaceHolder().getSurface();
    930         int frames1 = countFrames(
    931                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
    932                 RESET_MODE_NONE, 120 /* eosframe */, s);
    933         assertEquals("wrong number of frames decoded", 120, frames1);
    934     }
    935 
    936     public void testCodecEarlyEOSHEVC() throws Exception {
    937         Surface s = getActivity().getSurfaceHolder().getSurface();
    938         int frames1 = countFrames(
    939                 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
    940                 RESET_MODE_NONE, 120 /* eosframe */, s);
    941         assertEquals("wrong number of frames decoded", 120, frames1);
    942     }
    943 
    944     public void testCodecEarlyEOSMpeg4() throws Exception {
    945         Surface s = getActivity().getSurfaceHolder().getSurface();
    946         int frames1 = countFrames(
    947                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
    948                 RESET_MODE_NONE, 120 /* eosframe */, s);
    949         assertEquals("wrong number of frames decoded", 120, frames1);
    950     }
    951 
    952     public void testCodecEarlyEOSVP8() throws Exception {
    953         Surface s = getActivity().getSurfaceHolder().getSurface();
    954         int frames1 = countFrames(
    955                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    956                 RESET_MODE_NONE, 120 /* eosframe */, s);
    957         assertEquals("wrong number of frames decoded", 120, frames1);
    958     }
    959 
    960     public void testCodecEarlyEOSVP9() throws Exception {
    961         Surface s = getActivity().getSurfaceHolder().getSurface();
    962         int frames1 = countFrames(
    963                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
    964                 RESET_MODE_NONE, 120 /* eosframe */, s);
    965         assertEquals("wrong number of frames decoded", 120, frames1);
    966     }
    967 
    968     public void testCodecResetsH264WithoutSurface() throws Exception {
    969         testCodecResets(
    970                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
    971     }
    972 
    973     public void testCodecResetsH264WithSurface() throws Exception {
    974         Surface s = getActivity().getSurfaceHolder().getSurface();
    975         testCodecResets(
    976                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
    977     }
    978 
    979     public void testCodecResetsHEVCWithoutSurface() throws Exception {
    980         testCodecResets(
    981                 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, null);
    982     }
    983 
    984     public void testCodecResetsHEVCWithSurface() throws Exception {
    985         Surface s = getActivity().getSurfaceHolder().getSurface();
    986         testCodecResets(
    987                 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, s);
    988     }
    989 
    990     public void testCodecResetsH263WithoutSurface() throws Exception {
    991         testCodecResets(
    992                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
    993     }
    994 
    995     public void testCodecResetsH263WithSurface() throws Exception {
    996         Surface s = getActivity().getSurfaceHolder().getSurface();
    997         testCodecResets(
    998                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s);
    999     }
   1000 
   1001     public void testCodecResetsMpeg4WithoutSurface() throws Exception {
   1002         testCodecResets(
   1003                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null);
   1004     }
   1005 
   1006     public void testCodecResetsMpeg4WithSurface() throws Exception {
   1007         Surface s = getActivity().getSurfaceHolder().getSurface();
   1008         testCodecResets(
   1009                 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s);
   1010     }
   1011 
   1012     public void testCodecResetsVP8WithoutSurface() throws Exception {
   1013         testCodecResets(
   1014                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null);
   1015     }
   1016 
   1017     public void testCodecResetsVP8WithSurface() throws Exception {
   1018         Surface s = getActivity().getSurfaceHolder().getSurface();
   1019         testCodecResets(
   1020                 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s);
   1021     }
   1022 
   1023     public void testCodecResetsVP9WithoutSurface() throws Exception {
   1024         testCodecResets(
   1025                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null);
   1026     }
   1027 
   1028     public void testCodecResetsVP9WithSurface() throws Exception {
   1029         Surface s = getActivity().getSurfaceHolder().getSurface();
   1030         testCodecResets(
   1031                 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s);
   1032     }
   1033 
   1034 //    public void testCodecResetsOgg() throws Exception {
   1035 //        testCodecResets(R.raw.sinesweepogg, null);
   1036 //    }
   1037 
   1038     public void testCodecResetsMp3() throws Exception {
   1039         testCodecReconfig(R.raw.sinesweepmp3lame);
   1040         // NOTE: replacing testCodecReconfig call soon
   1041 //        testCodecResets(R.raw.sinesweepmp3lame, null);
   1042     }
   1043 
   1044     public void testCodecResetsM4a() throws Exception {
   1045         testCodecReconfig(R.raw.sinesweepm4a);
   1046         // NOTE: replacing testCodecReconfig call soon
   1047 //        testCodecResets(R.raw.sinesweepm4a, null);
   1048     }
   1049 
   1050     private void testCodecReconfig(int audio) throws Exception {
   1051         int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */);
   1052         int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */);
   1053         assertEquals("different output size when using reconfigured codec", size1, size2);
   1054     }
   1055 
   1056     private void testCodecResets(int video, Surface s) throws Exception {
   1057         int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s);
   1058         int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s);
   1059         int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s);
   1060         assertEquals("different number of frames when using reconfigured codec", frames1, frames2);
   1061         assertEquals("different number of frames when using flushed codec", frames1, frames3);
   1062     }
   1063 
   1064     private static MediaCodec createDecoder(String mime) {
   1065         try {
   1066             if (false) {
   1067                 // change to force testing software codecs
   1068                 if (mime.contains("avc")) {
   1069                     return MediaCodec.createByCodecName("OMX.google.h264.decoder");
   1070                 } else if (mime.contains("hevc")) {
   1071                     return MediaCodec.createByCodecName("OMX.google.hevc.decoder");
   1072                 } else if (mime.contains("3gpp")) {
   1073                     return MediaCodec.createByCodecName("OMX.google.h263.decoder");
   1074                 } else if (mime.contains("mp4v")) {
   1075                     return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
   1076                 } else if (mime.contains("vp8")) {
   1077                     return MediaCodec.createByCodecName("OMX.google.vp8.decoder");
   1078                 } else if (mime.contains("vp9")) {
   1079                     return MediaCodec.createByCodecName("OMX.google.vp9.decoder");
   1080                 }
   1081             }
   1082             return MediaCodec.createDecoderByType(mime);
   1083         } catch (Exception e) {
   1084             return null;
   1085         }
   1086     }
   1087 
   1088     // for video
   1089     private int countFrames(int video, int resetMode, int eosframe, Surface s)
   1090             throws Exception {
   1091         AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
   1092         MediaExtractor extractor = new MediaExtractor();
   1093         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
   1094                 testFd.getLength());
   1095         extractor.selectTrack(0);
   1096 
   1097         int numframes = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTFRAMES
   1098                 | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, resetMode, s,
   1099                 eosframe, null, null);
   1100 
   1101         extractor.release();
   1102         testFd.close();
   1103         return numframes;
   1104     }
   1105 
   1106     // for audio
   1107     private int countSize(int audio, int resetMode, int eosframe)
   1108             throws Exception {
   1109         AssetFileDescriptor testFd = mResources.openRawResourceFd(audio);
   1110         MediaExtractor extractor = new MediaExtractor();
   1111         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
   1112                 testFd.getLength());
   1113         extractor.selectTrack(0);
   1114 
   1115         // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH
   1116         int outputSize = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null,
   1117                 eosframe, null, null);
   1118 
   1119         extractor.release();
   1120         testFd.close();
   1121         return outputSize;
   1122     }
   1123 
   1124     private void testEOSBehavior(int movie, int stopatsample) throws Exception {
   1125         testEOSBehavior(movie, new int[] {stopatsample});
   1126     }
   1127 
   1128     private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception {
   1129         Surface s = null;
   1130         AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
   1131         MediaExtractor extractor = new MediaExtractor();
   1132         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
   1133                 testFd.getLength());
   1134         extractor.selectTrack(0); // consider variable looping on track
   1135         List<Long> outputChecksums = new ArrayList<Long>();
   1136         List<Long> outputTimestamps = new ArrayList<Long>();
   1137         Arrays.sort(stopAtSample);
   1138         int last = stopAtSample.length - 1;
   1139 
   1140         // decode reference (longest sequence to stop at + 100) and
   1141         // store checksums/pts in outputChecksums and outputTimestamps
   1142         // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH)
   1143         decodeWithChecks(extractor,
   1144                 CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
   1145                 RESET_MODE_NONE, s,
   1146                 stopAtSample[last] + 100, outputChecksums, outputTimestamps);
   1147 
   1148         // decode stopAtSample requests in reverse order (longest to
   1149         // shortest) and compare to reference checksums/pts in
   1150         // outputChecksums and outputTimestamps
   1151         for (int i = last; i >= 0; --i) {
   1152             if (true) { // reposition extractor
   1153                 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
   1154             } else { // create new extractor
   1155                 extractor.release();
   1156                 extractor = new MediaExtractor();
   1157                 extractor.setDataSource(testFd.getFileDescriptor(),
   1158                         testFd.getStartOffset(), testFd.getLength());
   1159                 extractor.selectTrack(0); // consider variable looping on track
   1160             }
   1161             decodeWithChecks(extractor,
   1162                     CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS
   1163                     | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH
   1164                     | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
   1165                     RESET_MODE_NONE, s,
   1166                     stopAtSample[i], outputChecksums, outputTimestamps);
   1167         }
   1168         extractor.release();
   1169         testFd.close();
   1170     }
   1171 
   1172     private static final int CHECKFLAG_SETCHECKSUM = 1 << 0;
   1173     private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1;
   1174     private static final int CHECKFLAG_SETPTS = 1 << 2;
   1175     private static final int CHECKFLAG_COMPAREPTS = 1 << 3;
   1176     private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4;
   1177     private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5;
   1178     private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6;
   1179     private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7;
   1180 
   1181     /**
   1182      * Decodes frames with parameterized checks and return values.
   1183      * The integer return can be selected through the checkFlags variable.
   1184      */
   1185     private static int decodeWithChecks(MediaExtractor extractor, int checkFlags, int resetMode,
   1186             Surface surface, int stopAtSample,
   1187             List<Long> outputChecksums, List<Long> outputTimestamps)
   1188             throws Exception {
   1189         int trackIndex = extractor.getSampleTrackIndex();
   1190         MediaFormat format = extractor.getTrackFormat(trackIndex);
   1191         String mime = format.getString(MediaFormat.KEY_MIME);
   1192         boolean isAudio = mime.startsWith("audio/");
   1193         ByteBuffer[] codecInputBuffers;
   1194         ByteBuffer[] codecOutputBuffers;
   1195 
   1196         MediaCodec codec = createDecoder(mime);
   1197         Log.i("@@@@", "using codec: " + codec.getName());
   1198         codec.configure(format, surface, null /* crypto */, 0 /* flags */);
   1199         codec.start();
   1200         codecInputBuffers = codec.getInputBuffers();
   1201         codecOutputBuffers = codec.getOutputBuffers();
   1202 
   1203         if (resetMode == RESET_MODE_RECONFIGURE) {
   1204             codec.stop();
   1205             codec.configure(format, surface, null /* crypto */, 0 /* flags */);
   1206             codec.start();
   1207             codecInputBuffers = codec.getInputBuffers();
   1208             codecOutputBuffers = codec.getOutputBuffers();
   1209         } else if (resetMode == RESET_MODE_FLUSH) {
   1210             codec.flush();
   1211         }
   1212 
   1213         // start decode loop
   1214         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
   1215 
   1216         final long kTimeOutUs = 5000; // 5ms timeout
   1217         boolean sawInputEOS = false;
   1218         boolean sawOutputEOS = false;
   1219         int deadDecoderCounter = 0;
   1220         int samplenum = 0;
   1221         int numframes = 0;
   1222         int outputSize = 0;
   1223         int width = 0;
   1224         int height = 0;
   1225         boolean dochecksum = false;
   1226         ArrayList<Long> timestamps = new ArrayList<Long>();
   1227         if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
   1228             outputTimestamps.clear();
   1229         }
   1230         if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
   1231             outputChecksums.clear();
   1232         }
   1233         while (!sawOutputEOS && deadDecoderCounter < 100) {
   1234             // handle input
   1235             if (!sawInputEOS) {
   1236                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
   1237 
   1238                 if (inputBufIndex >= 0) {
   1239                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
   1240 
   1241                     int sampleSize =
   1242                             extractor.readSampleData(dstBuf, 0 /* offset */);
   1243                     long presentationTimeUs = extractor.getSampleTime();
   1244                     boolean advanceDone = extractor.advance();
   1245                     // int flags = extractor.getSampleFlags();
   1246                     // Log.i("@@@@", "read sample " + samplenum + ":" +
   1247                     // extractor.getSampleFlags()
   1248                     // + " @ " + extractor.getSampleTime() + " size " +
   1249                     // sampleSize);
   1250                     assertEquals("extractor.advance() should match end of stream", sampleSize >= 0,
   1251                             advanceDone);
   1252 
   1253                     if (sampleSize < 0) {
   1254                         Log.d(TAG, "saw input EOS.");
   1255                         sawInputEOS = true;
   1256                         assertEquals("extractor.readSampleData() must return -1 at end of stream",
   1257                                 -1, sampleSize);
   1258                         assertEquals("extractor.getSampleTime() must return -1 at end of stream",
   1259                                 -1, presentationTimeUs);
   1260                         sampleSize = 0; // required otherwise queueInputBuffer
   1261                                         // returns invalid.
   1262                     } else {
   1263                         timestamps.add(presentationTimeUs);
   1264                         samplenum++; // increment before comparing with stopAtSample
   1265                         if (samplenum == stopAtSample) {
   1266                             Log.d(TAG, "saw input EOS (stop at sample).");
   1267                             sawInputEOS = true; // tag this sample as EOS
   1268                         }
   1269                     }
   1270                     codec.queueInputBuffer(
   1271                             inputBufIndex,
   1272                             0 /* offset */,
   1273                             sampleSize,
   1274                             presentationTimeUs,
   1275                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
   1276                 } else {
   1277                     assertEquals(
   1278                             "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex,
   1279                             MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex);
   1280                 }
   1281             }
   1282 
   1283             // handle output
   1284             int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
   1285 
   1286             deadDecoderCounter++;
   1287             if (outputBufIndex >= 0) {
   1288                 if (info.size > 0) { // Disregard 0-sized buffers at the end.
   1289                     deadDecoderCounter = 0;
   1290                     if (resetMode != RESET_MODE_NONE) {
   1291                         // once we've gotten some data out of the decoder, reset
   1292                         // and start again
   1293                         if (resetMode == RESET_MODE_RECONFIGURE) {
   1294                             codec.stop();
   1295                             codec.configure(format, surface /* surface */, null /* crypto */,
   1296                                     0 /* flags */);
   1297                             codec.start();
   1298                             codecInputBuffers = codec.getInputBuffers();
   1299                             codecOutputBuffers = codec.getOutputBuffers();
   1300                         } else if (resetMode == RESET_MODE_FLUSH) {
   1301                             codec.flush();
   1302                         } else {
   1303                             fail("unknown resetMode: " + resetMode);
   1304                         }
   1305                         // restart at beginning, clear resetMode
   1306                         resetMode = RESET_MODE_NONE;
   1307                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
   1308                         sawInputEOS = false;
   1309                         numframes = 0;
   1310                         timestamps.clear();
   1311                         if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
   1312                             outputTimestamps.clear();
   1313                         }
   1314                         if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
   1315                             outputChecksums.clear();
   1316                         }
   1317                         continue;
   1318                     }
   1319                     if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) {
   1320                         assertTrue("number of frames (" + numframes
   1321                                 + ") exceeds number of reference timestamps",
   1322                                 numframes < outputTimestamps.size());
   1323                         assertEquals("frame ts mismatch at frame " + numframes,
   1324                                 (long) outputTimestamps.get(numframes), info.presentationTimeUs);
   1325                     } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
   1326                         outputTimestamps.add(info.presentationTimeUs);
   1327                     }
   1328                     if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) {
   1329                         long sum = 0;   // note: checksum is 0 if buffer format unrecognized
   1330                         if (dochecksum) {
   1331                             Image image = codec.getOutputImage(outputBufIndex);
   1332                             // use image to do crc if it's available
   1333                             // fall back to buffer if image is not available
   1334                             if (image != null) {
   1335                                 sum = checksum(image);
   1336                             } else {
   1337                                 // TODO: add stride - right now just use info.size (as before)
   1338                                 //sum = checksum(codecOutputBuffers[outputBufIndex], width, height,
   1339                                 //        stride);
   1340                                 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufIndex);
   1341                                 outputBuffer.position(info.offset);
   1342                                 sum = checksum(outputBuffer, info.size);
   1343                             }
   1344                         }
   1345                         if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) {
   1346                             assertTrue("number of frames (" + numframes
   1347                                     + ") exceeds number of reference checksums",
   1348                                     numframes < outputChecksums.size());
   1349                             Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes)
   1350                                     + " new checksum: " + sum);
   1351                             assertEquals("frame data mismatch at frame " + numframes,
   1352                                     (long) outputChecksums.get(numframes), sum);
   1353                         } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
   1354                             outputChecksums.add(sum);
   1355                         }
   1356                     }
   1357                     if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) {
   1358                         assertTrue("output timestamp " + info.presentationTimeUs
   1359                                 + " without corresponding input timestamp"
   1360                                 , timestamps.remove(info.presentationTimeUs));
   1361                     }
   1362                     outputSize += info.size;
   1363                     numframes++;
   1364                 }
   1365                 // Log.d(TAG, "got frame, size " + info.size + "/" +
   1366                 // info.presentationTimeUs +
   1367                 // "/" + numframes + "/" + info.flags);
   1368                 codec.releaseOutputBuffer(outputBufIndex, true /* render */);
   1369                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
   1370                     Log.d(TAG, "saw output EOS.");
   1371                     sawOutputEOS = true;
   1372                 }
   1373             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
   1374                 codecOutputBuffers = codec.getOutputBuffers();
   1375                 Log.d(TAG, "output buffers have changed.");
   1376             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
   1377                 MediaFormat oformat = codec.getOutputFormat();
   1378                 if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) &&
   1379                         oformat.containsKey(MediaFormat.KEY_WIDTH) &&
   1380                         oformat.containsKey(MediaFormat.KEY_HEIGHT)) {
   1381                     int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
   1382                     width = oformat.getInteger(MediaFormat.KEY_WIDTH);
   1383                     height = oformat.getInteger(MediaFormat.KEY_HEIGHT);
   1384                     dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw
   1385                                                                   // buf formats
   1386                     Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height);
   1387                 } else {
   1388                     dochecksum = false; // check with audio later
   1389                     width = height = 0;
   1390                     Log.d(TAG, "output format has changed to (unknown video) " + oformat);
   1391                 }
   1392             } else {
   1393                 assertEquals(
   1394                         "codec.dequeueOutputBuffer() unrecognized return index: "
   1395                                 + outputBufIndex,
   1396                         MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex);
   1397             }
   1398         }
   1399         codec.stop();
   1400         codec.release();
   1401 
   1402         assertTrue("last frame didn't have EOS", sawOutputEOS);
   1403         if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) {
   1404             assertEquals("I!=O", samplenum, numframes);
   1405             if (stopAtSample != 0) {
   1406                 assertEquals("did not stop with right number of frames", stopAtSample, numframes);
   1407             }
   1408         }
   1409         return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize :
   1410                 (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes :
   1411                         0;
   1412     }
   1413 
   1414     public void testEOSBehaviorH264() throws Exception {
   1415         // this video has an I frame at 44
   1416         testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
   1417                 new int[] {44, 45, 55});
   1418     }
   1419     public void testEOSBehaviorHEVC() throws Exception {
   1420         testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 17);
   1421         testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 23);
   1422         testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 49);
   1423     }
   1424 
   1425     public void testEOSBehaviorH263() throws Exception {
   1426         // this video has an I frame every 12 frames.
   1427         testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
   1428                 new int[] {24, 25, 48, 50});
   1429     }
   1430 
   1431     public void testEOSBehaviorMpeg4() throws Exception {
   1432         // this video has an I frame every 12 frames
   1433         testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
   1434                 new int[] {24, 25, 48, 50, 2});
   1435     }
   1436 
   1437     public void testEOSBehaviorVP8() throws Exception {
   1438         // this video has an I frame at 46
   1439         testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
   1440                 new int[] {46, 47, 57, 45});
   1441     }
   1442 
   1443     public void testEOSBehaviorVP9() throws Exception {
   1444         // this video has an I frame at 44
   1445         testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
   1446                 new int[] {44, 45, 55, 43});
   1447     }
   1448 
   1449     /* from EncodeDecodeTest */
   1450     private static boolean isRecognizedFormat(int colorFormat) {
   1451         // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
   1452         switch (colorFormat) {
   1453         // these are the formats we know how to handle for this test
   1454             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
   1455             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
   1456             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
   1457             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
   1458             case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
   1459             case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
   1460                 /*
   1461                  * TODO: Check newer formats or ignore.
   1462                  * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
   1463                  * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2
   1464                  * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5
   1465                  */
   1466                 return true;
   1467             default:
   1468                 return false;
   1469         }
   1470     }
   1471 
   1472     private static long checksum(ByteBuffer buf, int size) {
   1473         int cap = buf.capacity();
   1474         assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap,
   1475                 size > 0 && size <= cap);
   1476         CRC32 crc = new CRC32();
   1477         if (buf.hasArray()) {
   1478             crc.update(buf.array(), buf.position() + buf.arrayOffset(), size);
   1479         } else {
   1480             int pos = buf.position();
   1481             final int rdsize = Math.min(4096, size);
   1482             byte bb[] = new byte[rdsize];
   1483             int chk;
   1484             for (int i = 0; i < size; i += chk) {
   1485                 chk = Math.min(rdsize, size - i);
   1486                 buf.get(bb, 0, chk);
   1487                 crc.update(bb, 0, chk);
   1488             }
   1489             buf.position(pos);
   1490         }
   1491         return crc.getValue();
   1492     }
   1493 
   1494     private static long checksum(ByteBuffer buf, int width, int height, int stride) {
   1495         int cap = buf.capacity();
   1496         assertTrue("checksum() params are invalid: w x h , s = "
   1497                 + width + " x " + height + " , " + stride + " cap = " + cap,
   1498                 width > 0 && width <= stride && height > 0 && height * stride <= cap);
   1499         // YUV 4:2:0 should generally have a data storage height 1.5x greater
   1500         // than the declared image height, representing the UV planes.
   1501         //
   1502         // We only check Y frame for now. Somewhat unknown with tiling effects.
   1503         //
   1504         //long tm = System.nanoTime();
   1505         final int lineinterval = 1; // line sampling frequency
   1506         CRC32 crc = new CRC32();
   1507         if (buf.hasArray()) {
   1508             byte b[] = buf.array();
   1509             int offs = buf.arrayOffset();
   1510             for (int i = 0; i < height; i += lineinterval) {
   1511                 crc.update(b, i * stride + offs, width);
   1512             }
   1513         } else { // almost always ends up here due to direct buffers
   1514             int pos = buf.position();
   1515             if (true) { // this {} is 80x times faster than else {} below.
   1516                 byte[] bb = new byte[width]; // local line buffer
   1517                 for (int i = 0; i < height; i += lineinterval) {
   1518                     buf.position(pos + i * stride);
   1519                     buf.get(bb, 0, width);
   1520                     crc.update(bb, 0, width);
   1521                 }
   1522             } else {
   1523                 for (int i = 0; i < height; i += lineinterval) {
   1524                     buf.position(pos + i * stride);
   1525                     for (int j = 0; j < width; ++j) {
   1526                         crc.update(buf.get());
   1527                     }
   1528                 }
   1529             }
   1530             buf.position(pos);
   1531         }
   1532         //tm = System.nanoTime() - tm;
   1533         //Log.d(TAG, "checksum time " + tm);
   1534         return crc.getValue();
   1535     }
   1536 
   1537     private static long checksum(Image image) {
   1538         int format = image.getFormat();
   1539         assertEquals("unsupported image format", ImageFormat.YUV_420_888, format);
   1540 
   1541         CRC32 crc = new CRC32();
   1542 
   1543         int imageWidth = image.getWidth();
   1544         int imageHeight = image.getHeight();
   1545 
   1546         Image.Plane[] planes = image.getPlanes();
   1547         for (int i = 0; i < planes.length; ++i) {
   1548             ByteBuffer buf = planes[i].getBuffer();
   1549 
   1550             int width, height, rowStride, pixelStride, x, y;
   1551             rowStride = planes[i].getRowStride();
   1552             pixelStride = planes[i].getPixelStride();
   1553             if (i == 0) {
   1554                 width = imageWidth;
   1555                 height = imageHeight;
   1556             } else {
   1557                 width = imageWidth / 2;
   1558                 height = imageHeight /2;
   1559             }
   1560             // local contiguous pixel buffer
   1561             byte[] bb = new byte[width * height];
   1562             if (buf.hasArray()) {
   1563                 byte b[] = buf.array();
   1564                 int offs = buf.arrayOffset();
   1565                 if (pixelStride == 1) {
   1566                     for (y = 0; y < height; ++y) {
   1567                         System.arraycopy(bb, y * width, b, y * rowStride + offs, width);
   1568                     }
   1569                 } else {
   1570                     // do it pixel-by-pixel
   1571                     for (y = 0; y < height; ++y) {
   1572                         int lineOffset = offs + y * rowStride;
   1573                         for (x = 0; x < width; ++x) {
   1574                             bb[y * width + x] = b[lineOffset + x * pixelStride];
   1575                         }
   1576                     }
   1577                 }
   1578             } else { // almost always ends up here due to direct buffers
   1579                 int pos = buf.position();
   1580                 if (pixelStride == 1) {
   1581                     for (y = 0; y < height; ++y) {
   1582                         buf.position(pos + y * rowStride);
   1583                         buf.get(bb, y * width, width);
   1584                     }
   1585                 } else {
   1586                     // local line buffer
   1587                     byte[] lb = new byte[rowStride];
   1588                     // do it pixel-by-pixel
   1589                     for (y = 0; y < height; ++y) {
   1590                         buf.position(pos + y * rowStride);
   1591                         // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
   1592                         buf.get(lb, 0, pixelStride * (width - 1) + 1);
   1593                         for (x = 0; x < width; ++x) {
   1594                             bb[y * width + x] = lb[x * pixelStride];
   1595                         }
   1596                     }
   1597                 }
   1598                 buf.position(pos);
   1599             }
   1600             crc.update(bb, 0, width * height);
   1601         }
   1602 
   1603         return crc.getValue();
   1604     }
   1605 
   1606     public void testFlush() throws Exception {
   1607         testFlush(R.raw.loudsoftwav);
   1608         testFlush(R.raw.loudsoftogg);
   1609         testFlush(R.raw.loudsoftmp3);
   1610         testFlush(R.raw.loudsoftaac);
   1611         testFlush(R.raw.loudsoftfaac);
   1612         testFlush(R.raw.loudsoftitunes);
   1613     }
   1614 
   1615     private void testFlush(int resource) throws Exception {
   1616 
   1617         AssetFileDescriptor testFd = mResources.openRawResourceFd(resource);
   1618 
   1619         MediaExtractor extractor;
   1620         MediaCodec codec;
   1621         ByteBuffer[] codecInputBuffers;
   1622         ByteBuffer[] codecOutputBuffers;
   1623 
   1624         extractor = new MediaExtractor();
   1625         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
   1626                 testFd.getLength());
   1627         testFd.close();
   1628 
   1629         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
   1630         MediaFormat format = extractor.getTrackFormat(0);
   1631         String mime = format.getString(MediaFormat.KEY_MIME);
   1632         assertTrue("not an audio file", mime.startsWith("audio/"));
   1633 
   1634         codec = MediaCodec.createDecoderByType(mime);
   1635         assertNotNull("couldn't find codec " + mime, codec);
   1636 
   1637         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
   1638         codec.start();
   1639         codecInputBuffers = codec.getInputBuffers();
   1640         codecOutputBuffers = codec.getOutputBuffers();
   1641 
   1642         extractor.selectTrack(0);
   1643 
   1644         // decode a bit of the first part of the file, and verify the amplitude
   1645         short maxvalue1 = getAmplitude(extractor, codec);
   1646 
   1647         // flush the codec and seek the extractor a different position, then decode a bit more
   1648         // and check the amplitude
   1649         extractor.seekTo(8000000, 0);
   1650         codec.flush();
   1651         short maxvalue2 = getAmplitude(extractor, codec);
   1652 
   1653         assertTrue("first section amplitude too low", maxvalue1 > 20000);
   1654         assertTrue("second section amplitude too high", maxvalue2 < 5000);
   1655         codec.stop();
   1656         codec.release();
   1657 
   1658     }
   1659 
   1660     private short getAmplitude(MediaExtractor extractor, MediaCodec codec) {
   1661         short maxvalue = 0;
   1662         int numBytesDecoded = 0;
   1663         final long kTimeOutUs = 5000;
   1664         ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
   1665         ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
   1666         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
   1667 
   1668         while(numBytesDecoded < 44100 * 2) {
   1669             int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
   1670 
   1671             if (inputBufIndex >= 0) {
   1672                 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
   1673 
   1674                 int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */);
   1675                 long presentationTimeUs = extractor.getSampleTime();
   1676 
   1677                 codec.queueInputBuffer(
   1678                         inputBufIndex,
   1679                         0 /* offset */,
   1680                         sampleSize,
   1681                         presentationTimeUs,
   1682                         0 /* flags */);
   1683 
   1684                 extractor.advance();
   1685             }
   1686             int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
   1687 
   1688             if (res >= 0) {
   1689 
   1690                 int outputBufIndex = res;
   1691                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
   1692 
   1693                 buf.position(info.offset);
   1694                 for (int i = 0; i < info.size; i += 2) {
   1695                     short sample = buf.getShort();
   1696                     if (maxvalue < sample) {
   1697                         maxvalue = sample;
   1698                     }
   1699                     int idx = (numBytesDecoded + i) / 2;
   1700                 }
   1701 
   1702                 numBytesDecoded += info.size;
   1703 
   1704                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
   1705             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
   1706                 codecOutputBuffers = codec.getOutputBuffers();
   1707             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
   1708                 MediaFormat oformat = codec.getOutputFormat();
   1709             }
   1710         }
   1711         return maxvalue;
   1712     }
   1713 
   1714 }
   1715 
   1716