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