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

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

Issue 2466463005: Support (E)AC3 passthrough
Patch Set: Add unit tests Created 3 years, 6 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
OLDNEW
(Empty)
1 // Copyright 2017 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.annotation.SuppressLint;
8 import android.media.AudioFormat;
9 import android.media.AudioManager;
10 import android.media.AudioTrack;
11 import android.os.Build;
12
13 import org.chromium.base.Log;
14 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.annotations.CalledByNative;
16 import org.chromium.base.annotations.JNINamespace;
17
18 import java.nio.ByteBuffer;
19
20 @JNINamespace("media")
21 class AudioTrackOutputStream {
22 // Provide dependency injection points for unit tests.
23 interface Callback {
24 int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFor mat);
25 AudioTrack createAudioTrack(int streamType, int sampleRateInHz, int chan nelConfig,
26 int audioFormat, int bufferSizeInBytes, int mode);
27 int onMoreData(ByteBuffer audioData, int totalPlayedFrames);
28 }
29
30 private static final String TAG = "AudioTrackOutput";
31 private Callback mCallback;
32 private AudioTrack mAudioTrack;
33 private long mNativeAudioTrackOutputStream;
34 private int mBufferSizeInBytes;
35
36 private ByteBuffer mAudioBuffer;
37 private WorkerThread mWorkerThread;
38
39 class WorkerThread extends Thread {
DaleCurtis 2017/06/15 21:46:32 I think instead of doing the threading in Java you
AndyWu 2017/08/02 01:43:39 TBD Will look into that. However, from test point
40 private volatile boolean mDone = false;
41
42 public void finish() {
43 mDone = true;
44 }
45
46 public void run() {
47 while (!mDone) {
48 if (!readMoreData()) {
49 msleep(10);
50 }
51 }
52 }
53
54 private void msleep(int msec) {
55 try {
56 Thread.sleep(msec);
57 } catch (InterruptedException e) {
58 }
59 }
60 }
61
62 @CalledByNative
63 private static AudioTrackOutputStream create() {
64 return new AudioTrackOutputStream(null);
65 }
66
67 @VisibleForTesting
68 static AudioTrackOutputStream create(Callback callback) {
69 return new AudioTrackOutputStream(callback);
70 }
71
72 private AudioTrackOutputStream(Callback callback) {
73 mCallback = callback;
74 if (mCallback == null) {
75 mCallback = new Callback() {
76 @Override
77 public int getMinBufferSize(
78 int sampleRateInHz, int channelConfig, int audioFormat) {
79 return AudioTrack.getMinBufferSize(sampleRateInHz, channelCo nfig, audioFormat);
80 }
81
82 @Override
83 public AudioTrack createAudioTrack(int streamType, int sampleRat eInHz,
84 int channelConfig, int audioFormat, int bufferSizeInByte s, int mode) {
85 return new AudioTrack(streamType, sampleRateInHz, channelCon fig, audioFormat,
86 bufferSizeInBytes, mode);
87 }
88
89 @Override
90 public int onMoreData(ByteBuffer audioData, int totalPlayedFrame s) {
91 return nativeOnMoreData(
92 mNativeAudioTrackOutputStream, audioData, totalPlaye dFrames);
93 }
94 };
95 }
96 }
97
98 @SuppressWarnings("deprecation")
DaleCurtis 2017/06/15 21:46:32 What's deprecated here?
AndyWu 2017/08/02 01:43:39 AudioFormat.CHANNEL_OUT_7POINT1
99 private int getChannelConfig(int channelCount) {
100 switch (channelCount) {
101 case 1:
102 return AudioFormat.CHANNEL_OUT_MONO;
103 case 2:
104 return AudioFormat.CHANNEL_OUT_STEREO;
105 case 4:
106 return AudioFormat.CHANNEL_OUT_QUAD;
107 case 6:
108 return AudioFormat.CHANNEL_OUT_5POINT1;
109 case 8:
110 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
111 return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
112 } else {
113 return AudioFormat.CHANNEL_OUT_7POINT1;
114 }
115 default:
116 return AudioFormat.CHANNEL_OUT_DEFAULT;
117 }
118 }
119
120 @CalledByNative
121 boolean open(int channelCount, int sampleRate, int sampleFormat) {
122 int channelConfig = getChannelConfig(channelCount);
123 mBufferSizeInBytes =
DaleCurtis 2017/06/15 21:46:32 You might consider 3x buffers here to handle momen
AndyWu 2017/08/02 01:43:39 Done.
124 2 * mCallback.getMinBufferSize(sampleRate, channelConfig, sample Format);
125
126 if (mAudioTrack != null) mAudioTrack.release();
DaleCurtis 2017/06/15 21:46:32 Assert instead, this should not happen.
AndyWu 2017/08/02 01:43:39 Done.
127
128 try {
129 Log.d(TAG, "Crate AudioTrack with sample rate:%d, channel:%d, format :%d ", sampleRate,
130 channelConfig, sampleFormat);
131
132 mAudioTrack = mCallback.createAudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
133 channelConfig, sampleFormat, mBufferSizeInBytes, AudioTrack. MODE_STREAM);
134 assert mAudioTrack != null;
135 } catch (IllegalArgumentException ile) {
136 Log.e(TAG, "Exception creating AudioTrack for playback: ", ile);
137 return false;
138 }
139
140 if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
141 Log.e(TAG, "Cannot create AudioTrack");
142 mAudioTrack = null;
143 return false;
144 }
145
146 return true;
147 }
148
149 @CalledByNative
150 void start(long nativeAudioTrackOutputStream) {
151 Log.d(TAG, "AudioTrackOutputStream.start()");
152 if (mWorkerThread != null) return;
153
154 mNativeAudioTrackOutputStream = nativeAudioTrackOutputStream;
155
156 mAudioBuffer = ByteBuffer.allocateDirect(mBufferSizeInBytes);
157 mAudioTrack.play();
158
159 mWorkerThread = new WorkerThread();
160 mWorkerThread.start();
161 }
162
163 @CalledByNative
164 void stop() {
165 Log.d(TAG, "AudioTrackOutputStream.stop()");
166 if (mWorkerThread != null) {
167 mWorkerThread.finish();
168 try {
169 mWorkerThread.interrupt();
170 mWorkerThread.join();
171 } catch (SecurityException e) {
172 Log.e(TAG, "Exception while waiting for AudioTrack worker thread finished: ", e);
173 } catch (InterruptedException e) {
174 Log.e(TAG, "Exception while waiting for AudioTrack worker thread finished: ", e);
175 }
176 mWorkerThread = null;
177 }
178
179 mAudioTrack.pause();
180 mAudioTrack.flush();
181 mNativeAudioTrackOutputStream = 0;
182 }
183
184 @SuppressWarnings("deprecation")
185 @CalledByNative
186 void setVolume(double volume) {
DaleCurtis 2017/06/15 21:46:32 Is this going to work at all for bitstream output?
AndyWu 2017/08/02 01:43:39 Good catch! It does not work for bitstream output.
187 // Chrome sends the volume in the range [0, 1.0], whereas Android
188 // expects the volume to be within [0, getMaxVolume()].
189 float scaledVolume = (float) (volume * mAudioTrack.getMaxVolume());
190 mAudioTrack.setStereoVolume(scaledVolume, scaledVolume);
191 }
192
193 @CalledByNative
194 void close() {
195 Log.d(TAG, "AudioTrackOutputStream.close()");
196 if (mAudioTrack != null) mAudioTrack.release();
197 }
DaleCurtis 2017/06/15 21:46:32 mAudioTrack = nullptr; note once this is called th
AndyWu 2017/08/02 01:43:39 Done.
198
199 private boolean readMoreData() {
chcunningham 2017/06/14 20:32:19 IIUC, the way this is structured you will lose dat
AndyWu 2017/08/02 01:43:39 You are right. However, I am using blocking versio
chcunningham 2017/08/04 19:26:40 From my read of the docs, it seems even the blocki
AndyWu 2017/08/04 21:45:52 Yes, you are right. See AudioTrackOutputStream.clo
AndyWu 2017/08/05 07:17:10 Done, thanks. media/base/android/java/src/test/org
200 if (mNativeAudioTrackOutputStream == 0) return false;
201
202 int position = mAudioTrack.getPlaybackHeadPosition();
chcunningham 2017/06/14 20:27:21 The documentation mentions this is secretly an uns
AndyWu 2017/08/02 01:43:39 Done.
203 int size = mCallback.onMoreData(mAudioBuffer, position);
204 if (size <= 0) {
205 return false;
206 }
207
208 ByteBuffer readOnlyBuffer = mAudioBuffer.asReadOnlyBuffer();
209 int result = writeAudio(readOnlyBuffer, size);
210
211 if (result < 0) {
212 Log.e(TAG, "AudioTrack.write() failed. Error:" + result);
213 return false;
214 } else if (result != size) {
215 Log.e(TAG, "AudioTrack.write() incomplete. Data size: %d, written si ze: %d", size,
216 result);
217 return false;
218 }
219
220 return true;
221 }
222
223 @SuppressLint("NewApi")
224 private int writeAudio(ByteBuffer buffer, int size) {
225 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
226 return mAudioTrack.write(buffer, size, AudioTrack.WRITE_BLOCKING);
227 } else {
228 if (buffer.hasArray()) {
229 return mAudioTrack.write(buffer.array(), buffer.arrayOffset(), s ize);
230 } else {
DaleCurtis 2017/06/15 21:46:32 How common is this? This seems like a high frequen
AndyWu 2017/08/02 01:43:40 Done.
231 byte[] array = new byte[size];
232 buffer.get(array);
233 return mAudioTrack.write(array, 0, size);
234 }
235 }
236 }
237
238 private native int nativeOnMoreData(
239 long nativeAudioTrackOutputStream, ByteBuffer audioData, int totalPl ayedFrames);
240 private native void nativeOnError(long nativeAudioTrackOutputStream);
241 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698