Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
|
DaleCurtis
2013/04/12 21:47:45
Leaving review to android/ folks.
| |
| 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.AudioFormat; | |
| 9 import android.media.MediaCodec; | |
| 10 import android.media.MediaCodec.BufferInfo; | |
| 11 import android.media.MediaExtractor; | |
| 12 import android.media.MediaFormat; | |
| 13 import android.util.Log; | |
| 14 | |
| 15 import java.nio.ByteBuffer; | |
| 16 import android.os.ParcelFileDescriptor; | |
| 17 | |
| 18 import org.chromium.base.CalledByNative; | |
| 19 import org.chromium.base.JNINamespace; | |
| 20 | |
| 21 @JNINamespace("media") | |
| 22 class WebAudioMediaCodecBridge { | |
| 23 private static final boolean DEBUG = true; | |
| 24 static final String LOG_TAG = "WebAudioMediaCodec"; | |
| 25 // TODO(rtoy): What is the correct timeout value for reading | |
| 26 // from a file in memory? | |
| 27 static final long TIMEOUT_MICROSECONDS = 500; | |
| 28 @CalledByNative | |
| 29 private static boolean decodeAudioFile(Context ctx, int nativeMediaCodecBrid ge, int inputFD) { | |
| 30 MediaExtractor extractor = new MediaExtractor(); | |
| 31 | |
| 32 ParcelFileDescriptor encodedFD; | |
| 33 encodedFD = ParcelFileDescriptor.adoptFd(inputFD); | |
| 34 try { | |
| 35 extractor.setDataSource(encodedFD.getFileDescriptor()); | |
| 36 } catch (Exception e) { | |
| 37 e.printStackTrace(); | |
| 38 encodedFD.detachFd(); | |
| 39 return false; | |
| 40 } | |
| 41 | |
| 42 if (extractor.getTrackCount() <= 0) { | |
| 43 encodedFD.detachFd(); | |
| 44 return false; | |
| 45 } | |
| 46 | |
| 47 MediaFormat format = extractor.getTrackFormat(0); | |
| 48 | |
| 49 int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); | |
| 50 int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
| 51 String mime = format.getString(MediaFormat.KEY_MIME); | |
| 52 | |
| 53 long duration_microseconds = 0; | |
| 54 if (format.containsKey(MediaFormat.KEY_DURATION)) { | |
| 55 try { | |
| 56 duration_microseconds = format.getLong(MediaFormat.KEY_DURATION) ; | |
| 57 } catch (Exception e) { | |
| 58 Log.d(LOG_TAG, "Cannot get duration"); | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 if (DEBUG) { | |
| 63 Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount() | |
| 64 + " Rate: " + sampleRate | |
| 65 + " Channels: " + channelCount | |
| 66 + " Mime: " + mime | |
| 67 + " Duration: " + duration_microseconds + " microsec"); | |
| 68 } | |
| 69 | |
| 70 // For audio/vorbis files, MediaFormat returns a really huge | |
| 71 // (multi-year) duration value even for short files. Tell | |
| 72 // nativeInitializeDestination that this is a vorbis file so | |
| 73 // it can handle it properly. | |
| 74 // | |
| 75 // See b/8528051 | |
| 76 nativeInitializeDestination(nativeMediaCodecBridge, | |
| 77 channelCount, | |
| 78 sampleRate, | |
| 79 duration_microseconds, | |
| 80 mime.equals("audio/vorbis")); | |
| 81 | |
| 82 // Create decoder | |
| 83 MediaCodec codec = MediaCodec.createDecoderByType(mime); | |
| 84 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flag s */); | |
| 85 codec.start(); | |
| 86 | |
| 87 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); | |
| 88 ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); | |
| 89 | |
| 90 // A track must be selected and will be used to read samples. | |
| 91 extractor.selectTrack(0); | |
| 92 | |
| 93 boolean sawInputEOS = false; | |
| 94 boolean sawOutputEOS = false; | |
| 95 | |
| 96 // Keep processing until the output is done. | |
| 97 while (!sawOutputEOS) { | |
| 98 if (!sawInputEOS) { | |
| 99 // Input side | |
| 100 int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECOND S); | |
| 101 | |
| 102 if (inputBufIndex >= 0) { | |
| 103 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; | |
| 104 int sampleSize = extractor.readSampleData(dstBuf, 0); | |
| 105 long presentationTimeMicroSec = 0; | |
| 106 | |
| 107 if (sampleSize < 0) { | |
| 108 sawInputEOS = true; | |
| 109 sampleSize = 0; | |
| 110 } else { | |
| 111 presentationTimeMicroSec = extractor.getSampleTime(); | |
| 112 } | |
| 113 | |
| 114 codec.queueInputBuffer(inputBufIndex, | |
| 115 0, /* offset */ | |
| 116 sampleSize, | |
| 117 presentationTimeMicroSec, | |
| 118 sawInputEOS ? MediaCodec.BUFFER_FLAG_ END_OF_STREAM : 0); | |
| 119 | |
| 120 if (!sawInputEOS) { | |
| 121 extractor.advance(); | |
| 122 } | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 // Output side | |
| 127 MediaCodec.BufferInfo info = new BufferInfo(); | |
| 128 final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_M ICROSECONDS); | |
| 129 | |
| 130 if (outputBufIndex >= 0) { | |
| 131 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; | |
| 132 | |
| 133 if (info.size > 0) { | |
| 134 nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size) ; | |
| 135 } | |
| 136 | |
| 137 buf.clear(); | |
| 138 codec.releaseOutputBuffer(outputBufIndex, false /* render */); | |
| 139 | |
| 140 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
| 141 sawOutputEOS = true; | |
| 142 } | |
| 143 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | |
| 144 codecOutputBuffers = codec.getOutputBuffers(); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 encodedFD.detachFd(); | |
| 149 | |
| 150 codec.stop(); | |
| 151 codec.release(); | |
| 152 codec = null; | |
| 153 | |
| 154 return true; | |
| 155 } | |
| 156 | |
| 157 private static native void nativeOnChunkDecoded( | |
| 158 int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size); | |
| 159 | |
| 160 private static native void nativeInitializeDestination( | |
| 161 int nativeWebAudioMediaCodecBridge, | |
| 162 int channelCount, | |
| 163 int sampleRate, | |
| 164 long duration_microseconds, | |
| 165 boolean is_vorbis); | |
| 166 } | |
| OLD | NEW |