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

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: 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;
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698