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