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