• 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.Context;
22 import android.media.MediaCodec;
23 import android.media.MediaCodecInfo;
24 import android.media.MediaCodecList;
25 import android.media.MediaFormat;
26 import android.test.AndroidTestCase;
27 import android.util.Log;
28 
29 import java.nio.ByteBuffer;
30 import java.util.LinkedList;
31 import java.util.List;
32 
33 public class EncoderTest extends AndroidTestCase {
34     private static final String TAG = "EncoderTest";
35     private static final boolean VERBOSE = false;
36 
37     private static final int kNumInputBytes = 256 * 1024;
38     private static final long kTimeoutUs = 10000;
39 
40     @Override
setContext(Context context)41     public void setContext(Context context) {
42         super.setContext(context);
43     }
44 
testAMRNBEncoders()45     public void testAMRNBEncoders() {
46         LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
47 
48         final int kBitRates[] =
49             { 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200 };
50 
51         for (int j = 0; j < kBitRates.length; ++j) {
52             MediaFormat format  = new MediaFormat();
53             format.setString(MediaFormat.KEY_MIME, "audio/3gpp");
54             format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
55             format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
56             format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
57             formats.push(format);
58         }
59 
60         testEncoderWithFormats("audio/3gpp", formats);
61     }
62 
testAMRWBEncoders()63     public void testAMRWBEncoders() {
64         LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
65 
66         final int kBitRates[] =
67             { 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850 };
68 
69         for (int j = 0; j < kBitRates.length; ++j) {
70             MediaFormat format  = new MediaFormat();
71             format.setString(MediaFormat.KEY_MIME, "audio/amr-wb");
72             format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 16000);
73             format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
74             format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
75             formats.push(format);
76         }
77 
78         testEncoderWithFormats("audio/amr-wb", formats);
79     }
80 
testAACEncoders()81     public void testAACEncoders() {
82         LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
83 
84         final int kAACProfiles[] = {
85             2 /* OMX_AUDIO_AACObjectLC */,
86             5 /* OMX_AUDIO_AACObjectHE */,
87             39 /* OMX_AUDIO_AACObjectELD */
88         };
89 
90         final int kSampleRates[] = { 8000, 11025, 22050, 44100, 48000 };
91         final int kBitRates[] = { 64000, 128000 };
92 
93         for (int k = 0; k < kAACProfiles.length; ++k) {
94             for (int i = 0; i < kSampleRates.length; ++i) {
95                 if (kAACProfiles[k] == 5 && kSampleRates[i] < 22050) {
96                     // Is this right? HE does not support sample rates < 22050Hz?
97                     continue;
98                 }
99                 for (int j = 0; j < kBitRates.length; ++j) {
100                     for (int ch = 1; ch <= 2; ++ch) {
101                         MediaFormat format  = new MediaFormat();
102                         format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
103 
104                         format.setInteger(
105                                 MediaFormat.KEY_AAC_PROFILE, kAACProfiles[k]);
106 
107                         format.setInteger(
108                                 MediaFormat.KEY_SAMPLE_RATE, kSampleRates[i]);
109 
110                         format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, ch);
111                         format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
112                         formats.push(format);
113                     }
114                 }
115             }
116         }
117 
118         testEncoderWithFormats("audio/mp4a-latm", formats);
119     }
120 
testEncoderWithFormats( String mime, List<MediaFormat> formats)121     private void testEncoderWithFormats(
122             String mime, List<MediaFormat> formats) {
123         List<String> componentNames = getEncoderNamesForType(mime);
124 
125         for (String componentName : componentNames) {
126             Log.d(TAG, "testing component '" + componentName + "'");
127             for (MediaFormat format : formats) {
128                 Log.d(TAG, "  testing format '" + format + "'");
129                 assertEquals(mime, format.getString(MediaFormat.KEY_MIME));
130                 testEncoder(componentName, format);
131             }
132         }
133     }
134 
getEncoderNamesForType(String mime)135     private List<String> getEncoderNamesForType(String mime) {
136         LinkedList<String> names = new LinkedList<String>();
137 
138         int n = MediaCodecList.getCodecCount();
139         for (int i = 0; i < n; ++i) {
140             MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
141 
142             if (!info.isEncoder()) {
143                 continue;
144             }
145 
146             if (!info.getName().startsWith("OMX.")) {
147                 // Unfortunately for legacy reasons, "AACEncoder", a
148                 // non OMX component had to be in this list for the video
149                 // editor code to work... but it cannot actually be instantiated
150                 // using MediaCodec.
151                 Log.d(TAG, "skipping '" + info.getName() + "'.");
152                 continue;
153             }
154 
155             String[] supportedTypes = info.getSupportedTypes();
156 
157             for (int j = 0; j < supportedTypes.length; ++j) {
158                 if (supportedTypes[j].equalsIgnoreCase(mime)) {
159                     names.push(info.getName());
160                     break;
161                 }
162             }
163         }
164 
165         return names;
166     }
167 
queueInputBuffer( MediaCodec codec, ByteBuffer[] inputBuffers, int index)168     private int queueInputBuffer(
169             MediaCodec codec, ByteBuffer[] inputBuffers, int index) {
170         ByteBuffer buffer = inputBuffers[index];
171         buffer.clear();
172 
173         int size = buffer.limit();
174 
175         byte[] zeroes = new byte[size];
176         buffer.put(zeroes);
177 
178         codec.queueInputBuffer(index, 0 /* offset */, size, 0 /* timeUs */, 0);
179 
180         return size;
181     }
182 
dequeueOutputBuffer( MediaCodec codec, ByteBuffer[] outputBuffers, int index, MediaCodec.BufferInfo info)183     private void dequeueOutputBuffer(
184             MediaCodec codec, ByteBuffer[] outputBuffers,
185             int index, MediaCodec.BufferInfo info) {
186         codec.releaseOutputBuffer(index, false /* render */);
187     }
188 
testEncoder(String componentName, MediaFormat format)189     private void testEncoder(String componentName, MediaFormat format) {
190         MediaCodec codec;
191         try {
192             codec = MediaCodec.createByCodecName(componentName);
193         } catch (Exception e) {
194             fail("codec '" + componentName + "' failed construction.");
195             return; /* does not get here, but avoids warning */
196         }
197         try {
198             codec.configure(
199                     format,
200                     null /* surface */,
201                     null /* crypto */,
202                     MediaCodec.CONFIGURE_FLAG_ENCODE);
203         } catch (IllegalStateException e) {
204             fail("codec '" + componentName + "' failed configuration.");
205         }
206 
207         codec.start();
208         ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
209         ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
210 
211         int numBytesSubmitted = 0;
212         boolean doneSubmittingInput = false;
213         int numBytesDequeued = 0;
214 
215         while (true) {
216             int index;
217 
218             if (!doneSubmittingInput) {
219                 index = codec.dequeueInputBuffer(kTimeoutUs /* timeoutUs */);
220 
221                 if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
222                     if (numBytesSubmitted >= kNumInputBytes) {
223                         codec.queueInputBuffer(
224                                 index,
225                                 0 /* offset */,
226                                 0 /* size */,
227                                 0 /* timeUs */,
228                                 MediaCodec.BUFFER_FLAG_END_OF_STREAM);
229 
230                         if (VERBOSE) {
231                             Log.d(TAG, "queued input EOS.");
232                         }
233 
234                         doneSubmittingInput = true;
235                     } else {
236                         int size = queueInputBuffer(
237                                 codec, codecInputBuffers, index);
238 
239                         numBytesSubmitted += size;
240 
241                         if (VERBOSE) {
242                             Log.d(TAG, "queued " + size + " bytes of input data.");
243                         }
244                     }
245                 }
246             }
247 
248             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
249             index = codec.dequeueOutputBuffer(info, kTimeoutUs /* timeoutUs */);
250 
251             if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
252             } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
253             } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
254                 codecOutputBuffers = codec.getOutputBuffers();
255             } else {
256                 dequeueOutputBuffer(codec, codecOutputBuffers, index, info);
257 
258                 numBytesDequeued += info.size;
259 
260                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
261                     if (VERBOSE) {
262                         Log.d(TAG, "dequeued output EOS.");
263                     }
264                     break;
265                 }
266 
267                 if (VERBOSE) {
268                     Log.d(TAG, "dequeued " + info.size + " bytes of output data.");
269                 }
270             }
271         }
272 
273         if (VERBOSE) {
274             Log.d(TAG, "queued a total of " + numBytesSubmitted + "bytes, "
275                     + "dequeued " + numBytesDequeued + " bytes.");
276         }
277 
278         int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
279         int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
280         int inBitrate = sampleRate * channelCount * 16;  // bit/sec
281         int outBitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
282 
283         float desiredRatio = (float)outBitrate / (float)inBitrate;
284         float actualRatio = (float)numBytesDequeued / (float)numBytesSubmitted;
285 
286         if (actualRatio < 0.9 * desiredRatio || actualRatio > 1.1 * desiredRatio) {
287             Log.w(TAG, "desiredRatio = " + desiredRatio
288                     + ", actualRatio = " + actualRatio);
289         }
290 
291         codec.release();
292         codec = null;
293     }
294 }
295 
296