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

Side by Side Diff: media/audio/android/audio_track_output_android.cc

Issue 8718014: Upstream: Media implementation for Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 9 years 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 | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698