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