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 |