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

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

Powered by Google App Engine
This is Rietveld 408576698