OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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 #include "media/audio/android/audio_track_output_android.h" | |
6 | |
7 #include "base/android/jni_android.h" | |
8 #include "base/logging.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/time.h" | |
11 | |
12 using base::android::AttachCurrentThread; | |
13 using base::android::CheckException; | |
14 | |
15 static const int kTimerIntervalInMilliseconds = 50; | |
16 | |
17 class AudioTrackOutputStream::StreamBuffer { | |
18 public: | |
19 explicit StreamBuffer(uint32 buffer_size); | |
20 | |
21 uint32 ReadStream(uint8* dest, uint32 max_size); | |
22 void ResetBuffer(uint32 data_size); | |
23 uint8* GetWritableBuffer(); | |
24 const uint8* ReadBuffer(); | |
25 void AdvancePosition(uint32 advance); | |
26 | |
27 uint32 buffer_size() { return buffer_size_; } | |
28 uint32 data_len() { return data_size_ - current_; } | |
29 | |
30 private: | |
31 scoped_array<uint8> buffer_; | |
32 uint32 buffer_size_; | |
33 uint32 data_size_; | |
34 uint32 current_; | |
35 }; | |
scherkus (not reviewing)
2011/12/01 19:01:14
DISALLOW_COPY_AND_ASSIGN()
michaelbai
2011/12/01 20:48:18
Done.
| |
36 | |
37 AudioTrackOutputStream::StreamBuffer::StreamBuffer(uint32 buffer_size) | |
38 : buffer_(new uint8[buffer_size]), | |
39 buffer_size_(buffer_size), | |
40 data_size_(0), | |
41 current_(0) { | |
42 } | |
43 | |
44 uint32 AudioTrackOutputStream::StreamBuffer::ReadStream(uint8* dest, | |
45 uint32 max_size) { | |
46 uint32 copy_size = data_len() < max_size ? data_len() : max_size; | |
47 memcpy(dest, buffer_.get() + current_, copy_size); | |
48 current_ += copy_size; | |
49 return copy_size; | |
50 } | |
51 | |
52 void AudioTrackOutputStream::StreamBuffer::ResetBuffer(uint32 data_size) { | |
53 CHECK_LE(data_size, buffer_size_); | |
54 data_size_ = data_size; | |
55 current_ = 0; | |
56 } | |
57 | |
58 uint8* AudioTrackOutputStream::StreamBuffer::GetWritableBuffer() { | |
59 return buffer_.get(); | |
60 } | |
61 | |
62 const uint8* AudioTrackOutputStream::StreamBuffer::ReadBuffer() { | |
63 return buffer_.get() + current_; | |
64 } | |
65 | |
66 void AudioTrackOutputStream::StreamBuffer::AdvancePosition(uint32 advance) { | |
67 current_ += advance; | |
68 CHECK(current_ <= data_size_); | |
69 } | |
70 | |
71 AudioTrackOutputStream::AudioTrackOutputStream(const AudioParameters& params) | |
72 : source_callback_(NULL), | |
73 params_(params.format, | |
74 params.sample_rate, | |
75 params.bits_per_sample, | |
76 params.samples_per_packet, | |
77 params.channels), | |
78 status_(IDLE), | |
79 volume_(0), | |
80 buffer_size_(0), | |
81 j_class_(NULL), | |
82 j_audio_track_(NULL) { | |
83 data_buffer_.reset( | |
84 new AudioTrackOutputStream::StreamBuffer(params.GetPacketSize())); | |
85 } | |
86 | |
87 AudioTrackOutputStream::~AudioTrackOutputStream() { | |
88 Close(); | |
89 | |
90 if (j_class_ && j_audio_track_) { | |
91 JNIEnv* env = AttachCurrentThread(); | |
92 CHECK(env); | |
93 env->DeleteGlobalRef(j_audio_track_); | |
94 j_audio_track_ = NULL; | |
95 env->DeleteGlobalRef(j_class_); | |
96 j_class_ = NULL; | |
97 } | |
98 } | |
99 | |
100 bool AudioTrackOutputStream::Open() { | |
101 if (!params_.IsValid()) | |
102 return false; | |
103 | |
104 if (status_ == OPENED) | |
105 return true; | |
106 else if (status_ != IDLE) | |
107 return false; | |
108 | |
109 JNIEnv* env = AttachCurrentThread(); | |
110 CHECK(env); | |
111 | |
112 jclass cls = env->FindClass("android/media/AudioTrack"); | |
113 CHECK(cls); | |
114 j_class_ = static_cast<jclass>(env->NewGlobalRef(cls)); | |
115 env->DeleteLocalRef(cls); | |
116 | |
117 jint channels; | |
118 if (params_.channels == 1) | |
119 channels = GetStaticIntField("AudioFormat", "CHANNEL_OUT_MONO"); | |
120 else if (params_.channels == 2) | |
121 channels = GetStaticIntField("AudioFormat", "CHANNEL_OUT_STEREO"); | |
122 else if (params_.channels == 4) | |
123 channels = GetStaticIntField("AudioFormat", "CHANNEL_OUT_QUAD"); | |
124 else | |
125 return false; | |
126 | |
127 jint bits_per_sample; | |
128 if (params_.bits_per_sample == 16) | |
129 bits_per_sample = GetStaticIntField("AudioFormat", "ENCODING_PCM_16BIT"); | |
130 else if (params_.bits_per_sample == 8) | |
131 bits_per_sample = GetStaticIntField("AudioFormat", "ENCODING_PCM_8BIT"); | |
132 else | |
133 return false; | |
134 | |
135 jmethodID min_method = env->GetStaticMethodID(j_class_, "getMinBufferSize", | |
136 "(III)I"); | |
137 CHECK(min_method); | |
138 | |
139 int min_buffer_size = env->CallStaticIntMethod( | |
140 j_class_, min_method, static_cast<jint>(params_.sample_rate), | |
141 channels, bits_per_sample); | |
142 | |
143 if (params_.GetPacketSize() < min_buffer_size) | |
144 return false; | |
145 buffer_size_ = params_.GetPacketSize(); | |
146 | |
147 jmethodID constructor = env->GetMethodID(j_class_, "<init>", "(IIIIII)V"); | |
148 CHECK(constructor); | |
149 | |
150 jobject tmp = env->NewObject( | |
151 j_class_, constructor, | |
152 GetStaticIntField("AudioManager", "STREAM_MUSIC"), | |
153 static_cast<jint>(params_.sample_rate), channels, bits_per_sample, | |
154 static_cast<jint>(buffer_size_), | |
155 GetStaticIntField("AudioTrack", "MODE_STREAM")); | |
156 | |
157 CHECK(tmp); | |
158 j_audio_track_ = env->NewGlobalRef(tmp); | |
159 env->DeleteLocalRef(tmp); | |
160 | |
161 status_ = OPENED; | |
162 return true; | |
163 } | |
164 | |
165 void AudioTrackOutputStream::Close() { | |
166 if (!j_audio_track_) | |
167 return; | |
168 | |
169 Stop(); | |
170 CallVoidMethod("flush"); | |
171 status_ = INVALID; | |
172 } | |
173 | |
174 void AudioTrackOutputStream::Start(AudioSourceCallback* callback) { | |
175 if (status_ != OPENED) | |
176 return; | |
177 if (!j_audio_track_) | |
178 return; | |
179 | |
180 source_callback_ = callback; | |
181 data_buffer_->ResetBuffer(0); | |
182 | |
183 FillAudioBufferTask(); | |
184 CallVoidMethod("play"); | |
185 status_ = PLAYING; | |
186 | |
187 timer_.Start( | |
188 FROM_HERE, | |
189 base::TimeDelta::FromMilliseconds(kTimerIntervalInMilliseconds), | |
190 this, &AudioTrackOutputStream::FillAudioBufferTask); | |
191 } | |
192 | |
193 void AudioTrackOutputStream::Stop() { | |
194 if (!j_audio_track_) | |
195 return; | |
196 | |
197 if (status_ == PLAYING) { | |
198 timer_.Stop(); | |
199 CallVoidMethod("stop"); | |
200 status_ = OPENED; | |
201 } | |
202 } | |
203 | |
204 void AudioTrackOutputStream::SetVolume(double volume) { | |
205 volume_ = volume; | |
206 | |
207 if (!j_audio_track_) | |
208 return; | |
209 | |
210 JNIEnv* env = AttachCurrentThread(); | |
211 CHECK(env); | |
212 | |
213 jmethodID method = env->GetMethodID(j_class_, "setStereoVolume", "(FF)I"); | |
214 CHECK(method); | |
215 env->CallIntMethod(j_audio_track_, method, static_cast<jfloat>(volume), | |
216 static_cast<jfloat>(volume)); | |
217 CheckException(env); | |
218 } | |
219 | |
220 void AudioTrackOutputStream::GetVolume(double* volume) { | |
221 if (volume) | |
222 *volume = volume_; | |
223 } | |
224 | |
225 // static | |
226 AudioOutputStream* AudioTrackOutputStream::MakeStream( | |
227 AudioManagerAndroid* manager, const AudioParameters& params) { | |
228 if (!manager && params.IsValid()) | |
scherkus (not reviewing)
2011/12/01 19:01:14
nit: do we even need to pass in manager to this me
michaelbai
2011/12/01 20:48:18
Done.
| |
229 return new AudioTrackOutputStream(params); | |
230 | |
231 return NULL; | |
232 } | |
233 | |
234 void AudioTrackOutputStream::CallVoidMethod(std::string method_name) { | |
235 JNIEnv* env = AttachCurrentThread(); | |
236 CHECK(env); | |
237 | |
238 jmethodID method = env->GetMethodID(j_class_, method_name.c_str(), "()V"); | |
239 CHECK(method); | |
240 env->CallVoidMethod(j_audio_track_, method); | |
241 CheckException(env); | |
242 } | |
243 | |
244 jint AudioTrackOutputStream::GetStaticIntField(std::string class_name, | |
245 std::string field_name) { | |
246 JNIEnv* env = AttachCurrentThread(); | |
247 CHECK(env); | |
248 | |
249 class_name.insert(0, "android/media/"); | |
250 jclass cls = env->FindClass(class_name.c_str()); | |
251 CHECK(cls); | |
252 | |
253 jfieldID field = env->GetStaticFieldID(cls, field_name.c_str(), "I"); | |
254 CHECK(field); | |
255 jint int_field = env->GetStaticIntField(cls, field); | |
256 env->DeleteLocalRef(cls); | |
257 return int_field; | |
258 } | |
259 | |
260 void AudioTrackOutputStream::FillAudioBufferTask() { | |
261 if (status_ != PLAYING) | |
262 return; | |
263 | |
264 JNIEnv* env = AttachCurrentThread(); | |
265 CHECK(env); | |
266 jmethodID method = env->GetMethodID(j_class_, "getPlaybackHeadPosition", | |
267 "()I"); | |
268 CHECK(method); | |
269 | |
270 int64 position = env->CallIntMethod(j_audio_track_, method); | |
271 CheckException(env); | |
272 | |
273 // Calculate how many bytes we can fill in. | |
274 position *= params_.sample_rate * params_.bits_per_sample * | |
275 params_.channels / 8; | |
276 position %= buffer_size_; | |
277 | |
278 int need_buffer = static_cast<int>(buffer_size_ - position); | |
279 CHECK(need_buffer >= 0 && need_buffer <= buffer_size_); | |
280 | |
281 if (!need_buffer) | |
282 return; | |
283 | |
284 // Fill the internal buffer first. | |
285 if (!data_buffer_->data_len()) { | |
286 uint32 src_data_size = source_callback_->OnMoreData( | |
287 this, | |
288 data_buffer_->GetWritableBuffer(), | |
289 data_buffer_->buffer_size(), | |
290 AudioBuffersState()); | |
291 data_buffer_->ResetBuffer(src_data_size); | |
292 } | |
293 need_buffer = std::min(need_buffer, | |
294 static_cast<int>(data_buffer_->data_len())); | |
295 | |
296 // Prepare a Java array that contains the samples. | |
297 jbyteArray buf = env->NewByteArray(need_buffer); | |
298 env->SetByteArrayRegion( | |
299 buf, 0, need_buffer, | |
300 reinterpret_cast<const jbyte*>(data_buffer_->ReadBuffer())); | |
301 data_buffer_->AdvancePosition(need_buffer); | |
302 | |
303 // Invoke method to submit samples. | |
304 method = env->GetMethodID(j_class_, "write", "([BII)I"); | |
305 env->CallIntMethod(j_audio_track_, method, buf, static_cast<jint>(0), | |
306 static_cast<jint>(need_buffer)); | |
307 CheckException(env); | |
308 env->DeleteLocalRef(buf); | |
309 } | |
OLD | NEW |