OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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.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 static final String LOG_TAG = "WebAudioMediaCodec"; | |
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 boolean decodeAudioFile(Context ctx, int nativeMediaCodecBrid ge, int inputFD) { | |
29 MediaExtractor extractor; | |
30 extractor = new MediaExtractor(); | |
31 | |
32 ParcelFileDescriptor encodedFD; | |
33 try { | |
34 encodedFD = ParcelFileDescriptor.adoptFd(inputFD); | |
35 extractor.setDataSource(encodedFD.getFileDescriptor()); | |
36 } catch (Exception e) { | |
37 e.printStackTrace(); | |
digit1
2013/04/04 15:56:47
I assume you would want to call encodedFD.detachFd
| |
38 return false; | |
39 } | |
40 | |
41 if (extractor.getTrackCount() <= 0) { | |
42 encodedFD.detachFd(); | |
43 return false; | |
44 } | |
45 | |
46 MediaFormat format = extractor.getTrackFormat(0); | |
47 | |
48 int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); | |
49 int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
50 String mime = format.getString(MediaFormat.KEY_MIME); | |
51 | |
52 long duration_microseconds = 0; | |
53 if (format.containsKey(MediaFormat.KEY_DURATION)) { | |
54 try { | |
55 duration_microseconds = format.getLong(MediaFormat.KEY_DURATION) ; | |
56 } catch (Exception e) { | |
57 Log.d(LOG_TAG, "Cannot get duration"); | |
digit1
2013/04/04 15:56:47
should you exit here, or is it ok to keep duration
Raymond Toy (Google)
2013/04/04 17:47:35
I believe it's ok to keep going. MediaCodec will
| |
58 } | |
59 } | |
60 | |
61 Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount() | |
digit1
2013/04/04 15:56:47
This Log.d() call is really slow: it will create a
Raymond Toy (Google)
2013/04/04 17:47:35
Ok. I'll add this, but I do hope to remove this p
| |
62 + " Rate: " + sampleRate | |
63 + " Channels: " + channelCount | |
64 + " Mime: " + mime | |
65 + " Duration: " + duration_microseconds + " microsec"); | |
66 | |
67 // For audio/vorbis files, MediaFormat returns a really huge | |
68 // (multi-year) duration value even for short files. Tell | |
69 // nativeInitializeDestination that this is a vorbis file so | |
70 // it can handle it properly. | |
71 // | |
72 // See b/8528051 | |
73 nativeInitializeDestination(nativeMediaCodecBridge, | |
74 channelCount, | |
75 sampleRate, | |
76 duration_microseconds, | |
77 mime.equals("audio/vorbis")); | |
78 | |
79 // Create decoder | |
80 MediaCodec codec = MediaCodec.createDecoderByType(mime); | |
81 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flag s */); | |
82 codec.start(); | |
83 | |
84 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); | |
85 ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); | |
86 | |
87 // A track must be selected and will be used to read samples. | |
88 extractor.selectTrack(0); | |
89 | |
90 boolean sawInputEOS = false; | |
91 boolean sawOutputEOS = false; | |
92 | |
93 // Keep processing until the output is done. | |
94 while (!sawOutputEOS) { | |
95 int sampleSize = 0; | |
96 if (!sawInputEOS) { | |
97 // Input side | |
98 int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECOND S); | |
99 | |
100 if (inputBufIndex >= 0) { | |
101 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; | |
102 sampleSize = extractor.readSampleData(dstBuf, 0); | |
103 long presentationTimeMicroSec = 0; | |
104 | |
105 if (sampleSize < 0) { | |
106 sawInputEOS = true; | |
107 sampleSize = 0; | |
108 } else { | |
109 presentationTimeMicroSec = extractor.getSampleTime(); | |
110 } | |
111 | |
112 codec.queueInputBuffer(inputBufIndex, | |
113 0, /* offset */ | |
114 sampleSize, | |
115 presentationTimeMicroSec, | |
116 sawInputEOS ? MediaCodec.BUFFER_FLAG_ END_OF_STREAM : 0); | |
117 | |
118 if (!sawInputEOS) { | |
119 extractor.advance(); | |
120 } | |
121 } | |
122 } | |
123 | |
124 // Output side | |
125 MediaCodec.BufferInfo info = new BufferInfo(); | |
126 final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_M ICROSECONDS); | |
127 | |
128 if (outputBufIndex >= 0) { | |
digit1
2013/04/04 15:56:47
Shouldn't you also handle INFO_OUTPUT_BUFFERS_CHAN
Raymond Toy (Google)
2013/04/04 17:47:35
Oops. The case for INFO_OUTPUT_BUFFERS_CHANGED wa
| |
129 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; | |
130 | |
131 if (info.size > 0) { | |
132 nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size) ; | |
133 } | |
134 | |
135 buf.clear(); | |
136 codec.releaseOutputBuffer(outputBufIndex, false /* render */); | |
137 | |
138 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
139 sawOutputEOS = true; | |
140 } | |
141 } | |
142 } | |
143 | |
144 encodedFD.detachFd(); | |
145 | |
146 codec.stop(); | |
147 codec.release(); | |
148 codec = null; | |
149 | |
150 return true; | |
151 } | |
152 | |
153 private static native void nativeOnChunkDecoded( | |
154 int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size); | |
155 | |
156 private static native void nativeInitializeDestination( | |
157 int nativeWebAudioMediaCodecBridge, | |
158 int channelCount, | |
159 int sampleRate, | |
160 long duration_microseconds, | |
161 boolean is_vorbis); | |
162 } | |
OLD | NEW |