Chromium Code Reviews| 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 |