| Index: media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java
|
| diff --git a/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2802439149c759dc5598ad5bba27f0c9099d60a3
|
| --- /dev/null
|
| +++ b/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java
|
| @@ -0,0 +1,166 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.media;
|
| +
|
| +import android.content.Context;
|
| +import android.media.AudioFormat;
|
| +import android.media.MediaCodec;
|
| +import android.media.MediaCodec.BufferInfo;
|
| +import android.media.MediaExtractor;
|
| +import android.media.MediaFormat;
|
| +import android.util.Log;
|
| +
|
| +import java.nio.ByteBuffer;
|
| +import android.os.ParcelFileDescriptor;
|
| +
|
| +import org.chromium.base.CalledByNative;
|
| +import org.chromium.base.JNINamespace;
|
| +
|
| +@JNINamespace("media")
|
| +class WebAudioMediaCodecBridge {
|
| + private static final boolean DEBUG = true;
|
| + static final String LOG_TAG = "WebAudioMediaCodec";
|
| + // TODO(rtoy): What is the correct timeout value for reading
|
| + // from a file in memory?
|
| + static final long TIMEOUT_MICROSECONDS = 500;
|
| + @CalledByNative
|
| + private static boolean decodeAudioFile(Context ctx, int nativeMediaCodecBridge, int inputFD) {
|
| + MediaExtractor extractor = new MediaExtractor();
|
| +
|
| + ParcelFileDescriptor encodedFD;
|
| + encodedFD = ParcelFileDescriptor.adoptFd(inputFD);
|
| + try {
|
| + extractor.setDataSource(encodedFD.getFileDescriptor());
|
| + } catch (Exception e) {
|
| + e.printStackTrace();
|
| + encodedFD.detachFd();
|
| + return false;
|
| + }
|
| +
|
| + if (extractor.getTrackCount() <= 0) {
|
| + encodedFD.detachFd();
|
| + return false;
|
| + }
|
| +
|
| + MediaFormat format = extractor.getTrackFormat(0);
|
| +
|
| + int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
| + int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
| + String mime = format.getString(MediaFormat.KEY_MIME);
|
| +
|
| + long duration_microseconds = 0;
|
| + if (format.containsKey(MediaFormat.KEY_DURATION)) {
|
| + try {
|
| + duration_microseconds = format.getLong(MediaFormat.KEY_DURATION);
|
| + } catch (Exception e) {
|
| + Log.d(LOG_TAG, "Cannot get duration");
|
| + }
|
| + }
|
| +
|
| + if (DEBUG) {
|
| + Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount()
|
| + + " Rate: " + sampleRate
|
| + + " Channels: " + channelCount
|
| + + " Mime: " + mime
|
| + + " Duration: " + duration_microseconds + " microsec");
|
| + }
|
| +
|
| + // For audio/vorbis files, MediaFormat returns a really huge
|
| + // (multi-year) duration value even for short files. Tell
|
| + // nativeInitializeDestination that this is a vorbis file so
|
| + // it can handle it properly.
|
| + //
|
| + // See b/8528051
|
| + nativeInitializeDestination(nativeMediaCodecBridge,
|
| + channelCount,
|
| + sampleRate,
|
| + duration_microseconds,
|
| + mime.equals("audio/vorbis"));
|
| +
|
| + // Create decoder
|
| + MediaCodec codec = MediaCodec.createDecoderByType(mime);
|
| + codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
|
| + codec.start();
|
| +
|
| + ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
|
| + ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
|
| +
|
| + // A track must be selected and will be used to read samples.
|
| + extractor.selectTrack(0);
|
| +
|
| + boolean sawInputEOS = false;
|
| + boolean sawOutputEOS = false;
|
| +
|
| + // Keep processing until the output is done.
|
| + while (!sawOutputEOS) {
|
| + if (!sawInputEOS) {
|
| + // Input side
|
| + int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECONDS);
|
| +
|
| + if (inputBufIndex >= 0) {
|
| + ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
|
| + int sampleSize = extractor.readSampleData(dstBuf, 0);
|
| + long presentationTimeMicroSec = 0;
|
| +
|
| + if (sampleSize < 0) {
|
| + sawInputEOS = true;
|
| + sampleSize = 0;
|
| + } else {
|
| + presentationTimeMicroSec = extractor.getSampleTime();
|
| + }
|
| +
|
| + codec.queueInputBuffer(inputBufIndex,
|
| + 0, /* offset */
|
| + sampleSize,
|
| + presentationTimeMicroSec,
|
| + sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
|
| +
|
| + if (!sawInputEOS) {
|
| + extractor.advance();
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Output side
|
| + MediaCodec.BufferInfo info = new BufferInfo();
|
| + final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_MICROSECONDS);
|
| +
|
| + if (outputBufIndex >= 0) {
|
| + ByteBuffer buf = codecOutputBuffers[outputBufIndex];
|
| +
|
| + if (info.size > 0) {
|
| + nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size);
|
| + }
|
| +
|
| + buf.clear();
|
| + codec.releaseOutputBuffer(outputBufIndex, false /* render */);
|
| +
|
| + if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
| + sawOutputEOS = true;
|
| + }
|
| + } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
| + codecOutputBuffers = codec.getOutputBuffers();
|
| + }
|
| + }
|
| +
|
| + encodedFD.detachFd();
|
| +
|
| + codec.stop();
|
| + codec.release();
|
| + codec = null;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + private static native void nativeOnChunkDecoded(
|
| + int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size);
|
| +
|
| + private static native void nativeInitializeDestination(
|
| + int nativeWebAudioMediaCodecBridge,
|
| + int channelCount,
|
| + int sampleRate,
|
| + long duration_microseconds,
|
| + boolean is_vorbis);
|
| +}
|
|
|