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..281290365f565f544337ab09ef536aaf8bea5003 |
--- /dev/null |
+++ b/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java |
@@ -0,0 +1,191 @@ |
+// 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 native void nativeOnChunkDecoded( |
bulach
2013/03/28 13:39:25
nit: move these native declarations to the bottom
|
+ int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size); |
+ |
+ private static native void nativeInitializeDestination( |
+ int nativeWebAudioMediaCodecBridge, |
+ int channelCount, |
+ int sampleRate, |
+ long duration_us, |
+ boolean is_vorbis); |
+ |
+ @CalledByNative |
+ private static boolean decodeAudioFile(Context ctx, int nativeMediaCodecBridge, int inputFD) { |
+ MediaCodec codec; |
+ ByteBuffer[] codecInputBuffers; |
+ ByteBuffer[] codecOutputBuffers; |
+ String LOG_TAG = "WebAudioMediaCodec"; |
bulach
2013/03/28 13:39:25
nit: LOG_TAG is normally a private static final me
|
+ Log.d(LOG_TAG, |
+ "decodeAudioFile start ======================================="); |
bulach
2013/03/28 13:39:25
nit: remove the ==== decoration, saves some bytes.
|
+ |
+ |
+ // Todo(rtoy): What is the correct timeout value for reading |
bulach
2013/03/28 13:39:25
nit: s/Todo/TODO/
|
+ // from a file in memory? |
+ long TIMEOUT_US = 500; |
+ |
+ MediaExtractor extractor; |
+ extractor = new MediaExtractor(); |
+ |
+ ParcelFileDescriptor encodedFD; |
+ try { |
+ encodedFD = ParcelFileDescriptor.adoptFd(inputFD); |
+ extractor.setDataSource(encodedFD.getFileDescriptor()); |
+ } catch (Exception e) { |
+ // TODO Auto-generated catch block |
bulach
2013/03/28 13:39:25
nit: remove?
|
+ e.printStackTrace(); |
+ return false; |
+ } |
+ |
+ Log.d(LOG_TAG, String.format("TRACKS #: %d", extractor.getTrackCount())); |
+ |
+ if (extractor.getTrackCount() <= 0) { |
+ encodedFD.detachFd(); |
+ return false; |
+ } |
+ |
+ MediaFormat format = extractor.getTrackFormat(0); |
+ String mime = format.getString(MediaFormat.KEY_MIME); |
+ Log.d(LOG_TAG, String.format("MIME TYPE: %s", mime)); |
+ |
+ int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); |
+ Log.d(LOG_TAG, String.format("Sample rate: %d", sampleRate)); |
+ |
+ int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); |
+ Log.d(LOG_TAG, String.format("Channel count: %d", channelCount)); |
+ |
+ long duration_us = 0; |
+ if (format.containsKey(MediaFormat.KEY_CHANNEL_MASK)) { |
+ Log.d(LOG_TAG, |
+ String.format( |
+ "Channel mask: %d", |
+ format.getInteger(MediaFormat.KEY_CHANNEL_MASK))); |
+ } |
+ if (format.containsKey(MediaFormat.KEY_DURATION)) { |
+ try { |
+ duration_us = format.getLong(MediaFormat.KEY_DURATION); |
+ Log.d(LOG_TAG, "Duration : " + duration_us + " us"); |
+ } catch (Exception e) { |
+ Log.d(LOG_TAG, "Cannot get duration"); |
+ } |
+ } |
+ |
+ int channelConfig; |
+ |
+ if (channelCount == 2) { |
+ channelConfig = AudioFormat.CHANNEL_OUT_STEREO; |
+ } else { |
+ channelConfig = AudioFormat.CHANNEL_OUT_MONO; |
+ } |
bulach
2013/03/28 13:39:25
this would be simpler as:
int channelConfig = chan
|
+ |
+ nativeInitializeDestination(nativeMediaCodecBridge, |
+ channelCount, |
+ sampleRate, |
+ duration_us, |
+ mime.equals("audio/vorbis")); |
+ |
+ // Create decoder |
+ codec = MediaCodec.createDecoderByType(mime); |
+ codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); |
+ codec.start(); |
+ |
+ codecInputBuffers = codec.getInputBuffers(); |
+ codecOutputBuffers = codec.getOutputBuffers(); |
+ |
+ // You must select a track. You will read samples from the |
bulach
2013/03/28 13:39:25
nit: a while ago there's a discussion about not us
|
+ // media from this track! |
+ extractor.selectTrack(0); |
+ |
+ boolean sawInputEOS = false; |
+ boolean sawOutputEOS = false; |
+ |
+ // Keep processing until the output is done. |
+ while (!sawOutputEOS) { |
+ int sampleSize = 0; |
+ if (!sawInputEOS) { |
+ // Input side |
+ int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_US); |
+ |
+ if (inputBufIndex >= 0) { |
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; |
+ sampleSize = extractor.readSampleData(dstBuf, 0); |
+ long presentationTimeUs = 0; |
+ |
+ if (sampleSize < 0) { |
+ sawInputEOS = true; |
+ sampleSize = 0; |
+ } else { |
+ presentationTimeUs = extractor.getSampleTime(); |
+ } |
+ |
+ codec.queueInputBuffer(inputBufIndex, |
+ 0, //offset |
bulach
2013/03/28 13:39:25
nit: 0 /* offset */, is more common as it avoids c
|
+ sampleSize, |
+ presentationTimeUs, |
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); |
+ |
+ if (!sawInputEOS) { |
+ extractor.advance(); |
+ } |
+ } |
+ } |
+ // Output side |
+ |
+ MediaCodec.BufferInfo info = new BufferInfo(); |
+ final int res = codec.dequeueOutputBuffer(info, TIMEOUT_US); |
+ |
+ if (res >= 0) { |
+ int outputBufIndex = res; |
+ 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 (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
+ Log.d(LOG_TAG, "Output buffers changed"); |
+ codecOutputBuffers = codec.getOutputBuffers(); |
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
+ final MediaFormat oformat = codec.getOutputFormat(); |
+ Log.d(LOG_TAG, "Output format has changed to " + oformat); |
+ } |
+ } |
+ |
+ Log.d(LOG_TAG, |
+ "decodeAudioFile finish ======================================="); |
+ |
+ encodedFD.detachFd(); |
+ |
+ codec.stop(); |
+ codec.release(); |
+ codec = null; |
+ |
+ return true; |
+ } |
+} |