Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java

Issue 1565623002: Replace WebAudio MediaCodec usage with FFmpeg. A ~4x improvement. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Delete expectations. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/base/android/BUILD.gn ('k') | media/base/android/media_jni_registrar.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « media/base/android/BUILD.gn ('k') | media/base/android/media_jni_registrar.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698