| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.media; | |
| 6 | |
| 7 import android.content.Context; | |
| 8 import android.media.MediaCodec; | |
| 9 import android.media.MediaCodec.BufferInfo; | |
| 10 import android.media.MediaExtractor; | |
| 11 import android.media.MediaFormat; | |
| 12 import android.os.ParcelFileDescriptor; | |
| 13 | |
| 14 import org.chromium.base.Log; | |
| 15 import org.chromium.base.annotations.CalledByNative; | |
| 16 import org.chromium.base.annotations.JNINamespace; | |
| 17 | |
| 18 import java.io.File; | |
| 19 import java.nio.ByteBuffer; | |
| 20 | |
| 21 @JNINamespace("media") | |
| 22 class WebAudioMediaCodecBridge { | |
| 23 private static final String TAG = "cr.media"; | |
| 24 // TODO(rtoy): What is the correct timeout value for reading | |
| 25 // from a file in memory? | |
| 26 static final long TIMEOUT_MICROSECONDS = 500; | |
| 27 @CalledByNative | |
| 28 private static String createTempFile(Context ctx) throws java.io.IOException
{ | |
| 29 File outputDirectory = ctx.getCacheDir(); | |
| 30 File outputFile = File.createTempFile("webaudio", ".dat", outputDirector
y); | |
| 31 return outputFile.getAbsolutePath(); | |
| 32 } | |
| 33 | |
| 34 @SuppressWarnings("deprecation") | |
| 35 @CalledByNative | |
| 36 private static boolean decodeAudioFile(Context ctx, long nativeMediaCodecBri
dge, | |
| 37 int inputFD, long dataSize) { | |
| 38 | |
| 39 if (dataSize < 0 || dataSize > 0x7fffffff) return false; | |
| 40 | |
| 41 MediaExtractor extractor = new MediaExtractor(); | |
| 42 | |
| 43 ParcelFileDescriptor encodedFD; | |
| 44 encodedFD = ParcelFileDescriptor.adoptFd(inputFD); | |
| 45 try { | |
| 46 extractor.setDataSource(encodedFD.getFileDescriptor(), 0, dataSize); | |
| 47 } catch (Exception e) { | |
| 48 e.printStackTrace(); | |
| 49 encodedFD.detachFd(); | |
| 50 return false; | |
| 51 } | |
| 52 | |
| 53 if (extractor.getTrackCount() <= 0) { | |
| 54 encodedFD.detachFd(); | |
| 55 return false; | |
| 56 } | |
| 57 | |
| 58 MediaFormat format = extractor.getTrackFormat(0); | |
| 59 | |
| 60 // If we are unable to get the input channel count, the sample | |
| 61 // rate or the mime type for any reason, just give up. | |
| 62 // Without these, we don't know what to do. | |
| 63 | |
| 64 int inputChannelCount; | |
| 65 try { | |
| 66 // Number of channels specified in the file | |
| 67 inputChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
; | |
| 68 } catch (Exception e) { | |
| 69 // Give up. | |
| 70 Log.w(TAG, "Unable to determine number of channels"); | |
| 71 encodedFD.detachFd(); | |
| 72 return false; | |
| 73 } | |
| 74 | |
| 75 // Number of channels the decoder will provide. (Not | |
| 76 // necessarily the same as inputChannelCount. See | |
| 77 // crbug.com/266006.) | |
| 78 int outputChannelCount = inputChannelCount; | |
| 79 | |
| 80 int sampleRate; | |
| 81 try { | |
| 82 sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
| 83 } catch (Exception e) { | |
| 84 // Give up. | |
| 85 Log.w(TAG, "Unable to determine sample rate"); | |
| 86 encodedFD.detachFd(); | |
| 87 return false; | |
| 88 } | |
| 89 | |
| 90 String mime; | |
| 91 try { | |
| 92 mime = format.getString(MediaFormat.KEY_MIME); | |
| 93 } catch (Exception e) { | |
| 94 // Give up. | |
| 95 Log.w(TAG, "Unable to determine type of encoding used by the file"); | |
| 96 encodedFD.detachFd(); | |
| 97 return false; | |
| 98 } | |
| 99 | |
| 100 long durationMicroseconds = 0; | |
| 101 if (format.containsKey(MediaFormat.KEY_DURATION)) { | |
| 102 try { | |
| 103 durationMicroseconds = format.getLong(MediaFormat.KEY_DURATION); | |
| 104 } catch (Exception e) { | |
| 105 Log.d(TAG, "Cannot get duration"); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 // If the duration is too long, set to 0 to force the caller | |
| 110 // not to preallocate space. See crbug.com/326856. | |
| 111 // FIXME: What should be the limit? We're arbitrarily using | |
| 112 // about 2148 sec (35.8 min). | |
| 113 if (durationMicroseconds > 0x7fffffff) { | |
| 114 durationMicroseconds = 0; | |
| 115 } | |
| 116 | |
| 117 Log.d(TAG, "Initial: Tracks: %d Format: %s", extractor.getTrackCount(),
format); | |
| 118 | |
| 119 // Create decoder | |
| 120 MediaCodec codec; | |
| 121 try { | |
| 122 codec = MediaCodec.createDecoderByType(mime); | |
| 123 } catch (Exception e) { | |
| 124 Log.w(TAG, "Failed to create MediaCodec for mime type: %s", mime); | |
| 125 encodedFD.detachFd(); | |
| 126 return false; | |
| 127 } | |
| 128 | |
| 129 try { | |
| 130 codec.configure(format, null /* surface */, null /* crypto */, 0 /*
flags */); | |
| 131 } catch (Exception e) { | |
| 132 Log.w(TAG, "Unable to configure codec for format " + format, e); | |
| 133 encodedFD.detachFd(); | |
| 134 return false; | |
| 135 } | |
| 136 try { | |
| 137 codec.start(); | |
| 138 } catch (Exception e) { | |
| 139 Log.w(TAG, "Unable to start()", e); | |
| 140 encodedFD.detachFd(); | |
| 141 return false; | |
| 142 } | |
| 143 | |
| 144 ByteBuffer[] codecInputBuffers; | |
| 145 try { | |
| 146 codecInputBuffers = codec.getInputBuffers(); | |
| 147 } catch (Exception e) { | |
| 148 Log.w(TAG, "getInputBuffers() failed", e); | |
| 149 encodedFD.detachFd(); | |
| 150 return false; | |
| 151 } | |
| 152 ByteBuffer[] codecOutputBuffers; | |
| 153 try { | |
| 154 codecOutputBuffers = codec.getOutputBuffers(); | |
| 155 } catch (Exception e) { | |
| 156 Log.w(TAG, "getOutputBuffers() failed", e); | |
| 157 encodedFD.detachFd(); | |
| 158 return false; | |
| 159 } | |
| 160 | |
| 161 // A track must be selected and will be used to read samples. | |
| 162 extractor.selectTrack(0); | |
| 163 | |
| 164 boolean sawInputEOS = false; | |
| 165 boolean sawOutputEOS = false; | |
| 166 boolean destinationInitialized = false; | |
| 167 boolean decodedSuccessfully = true; | |
| 168 | |
| 169 // Keep processing until the output is done. | |
| 170 while (!sawOutputEOS) { | |
| 171 if (!sawInputEOS) { | |
| 172 // Input side | |
| 173 int inputBufIndex; | |
| 174 try { | |
| 175 inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECOND
S); | |
| 176 } catch (Exception e) { | |
| 177 Log.w(TAG, "dequeueInputBuffer(%d) failed.", TIMEOUT_MICROSE
CONDS, e); | |
| 178 decodedSuccessfully = false; | |
| 179 break; | |
| 180 } | |
| 181 | |
| 182 if (inputBufIndex >= 0) { | |
| 183 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; | |
| 184 int sampleSize; | |
| 185 | |
| 186 try { | |
| 187 sampleSize = extractor.readSampleData(dstBuf, 0); | |
| 188 } catch (Exception e) { | |
| 189 Log.w(TAG, "readSampleData failed."); | |
| 190 decodedSuccessfully = false; | |
| 191 break; | |
| 192 } | |
| 193 | |
| 194 long presentationTimeMicroSec = 0; | |
| 195 | |
| 196 if (sampleSize < 0) { | |
| 197 sawInputEOS = true; | |
| 198 sampleSize = 0; | |
| 199 } else { | |
| 200 presentationTimeMicroSec = extractor.getSampleTime(); | |
| 201 } | |
| 202 | |
| 203 try { | |
| 204 codec.queueInputBuffer(inputBufIndex, | |
| 205 0, /* offset */ | |
| 206 sampleSize, | |
| 207 presentationTimeMicroSec, | |
| 208 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STRE
AM : 0); | |
| 209 } catch (Exception e) { | |
| 210 Log.w(TAG, "queueInputBuffer(%d, 0, %d, %d, %d) failed."
, | |
| 211 inputBufIndex, sampleSize, presentationTimeMicro
Sec, | |
| 212 (sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STR
EAM : 0), e); | |
| 213 decodedSuccessfully = false; | |
| 214 break; | |
| 215 } | |
| 216 | |
| 217 if (!sawInputEOS) { | |
| 218 extractor.advance(); | |
| 219 } | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 // Output side | |
| 224 MediaCodec.BufferInfo info = new BufferInfo(); | |
| 225 final int outputBufIndex; | |
| 226 | |
| 227 try { | |
| 228 outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_MICROSE
CONDS); | |
| 229 } catch (Exception e) { | |
| 230 Log.w(TAG, "dequeueOutputBuffer(%s, %d) failed", info, TIMEOUT_M
ICROSECONDS); | |
| 231 e.printStackTrace(); | |
| 232 decodedSuccessfully = false; | |
| 233 break; | |
| 234 } | |
| 235 | |
| 236 if (outputBufIndex >= 0) { | |
| 237 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; | |
| 238 | |
| 239 if (!destinationInitialized) { | |
| 240 // Initialize the destination as late as possible to | |
| 241 // catch any changes in format. But be sure to | |
| 242 // initialize it BEFORE we send any decoded audio, | |
| 243 // and only initialize once. | |
| 244 Log.d(TAG, "Final: Rate: %d Channels: %d Mime: %s Duration:
%d microsec", | |
| 245 sampleRate, inputChannelCount, mime, durationMicrose
conds); | |
| 246 | |
| 247 nativeInitializeDestination(nativeMediaCodecBridge, | |
| 248 inputChannelCount, | |
| 249 sampleRate, | |
| 250 durationMicroseconds); | |
| 251 destinationInitialized = true; | |
| 252 } | |
| 253 | |
| 254 if (destinationInitialized && info.size > 0) { | |
| 255 nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size, | |
| 256 inputChannelCount, outputChannelCount); | |
| 257 } | |
| 258 | |
| 259 buf.clear(); | |
| 260 codec.releaseOutputBuffer(outputBufIndex, false /* render */); | |
| 261 | |
| 262 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
| 263 sawOutputEOS = true; | |
| 264 } | |
| 265 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
{ | |
| 266 codecOutputBuffers = codec.getOutputBuffers(); | |
| 267 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{ | |
| 268 MediaFormat newFormat = codec.getOutputFormat(); | |
| 269 outputChannelCount = newFormat.getInteger(MediaFormat.KEY_CHANNE
L_COUNT); | |
| 270 sampleRate = newFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
| 271 Log.d(TAG, "output format changed to " + newFormat); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 encodedFD.detachFd(); | |
| 276 | |
| 277 codec.stop(); | |
| 278 codec.release(); | |
| 279 codec = null; | |
| 280 | |
| 281 return decodedSuccessfully; | |
| 282 } | |
| 283 | |
| 284 private static native void nativeOnChunkDecoded( | |
| 285 long nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size, | |
| 286 int inputChannelCount, int outputChannelCount); | |
| 287 | |
| 288 private static native void nativeInitializeDestination( | |
| 289 long nativeWebAudioMediaCodecBridge, | |
| 290 int inputChannelCount, | |
| 291 int sampleRate, | |
| 292 long durationMicroseconds); | |
| 293 } | |
| OLD | NEW |