1/* 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "webrtc/modules/audio_device/android/audio_manager.h" 12#include "webrtc/modules/audio_device/android/audio_track_jni.h" 13 14#include <utility> 15 16#include <android/log.h> 17 18#include "webrtc/base/arraysize.h" 19#include "webrtc/base/checks.h" 20#include "webrtc/base/format_macros.h" 21 22#define TAG "AudioTrackJni" 23#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__) 24#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 25#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 26#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) 27#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) 28 29namespace webrtc { 30 31// AudioTrackJni::JavaAudioTrack implementation. 32AudioTrackJni::JavaAudioTrack::JavaAudioTrack( 33 NativeRegistration* native_reg, 34 rtc::scoped_ptr<GlobalRef> audio_track) 35 : audio_track_(std::move(audio_track)), 36 init_playout_(native_reg->GetMethodId("initPlayout", "(II)V")), 37 start_playout_(native_reg->GetMethodId("startPlayout", "()Z")), 38 stop_playout_(native_reg->GetMethodId("stopPlayout", "()Z")), 39 set_stream_volume_(native_reg->GetMethodId("setStreamVolume", "(I)Z")), 40 get_stream_max_volume_( 41 native_reg->GetMethodId("getStreamMaxVolume", "()I")), 42 get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")) {} 43 44AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {} 45 46void AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) { 47 audio_track_->CallVoidMethod(init_playout_, sample_rate, channels); 48} 49 50bool AudioTrackJni::JavaAudioTrack::StartPlayout() { 51 return audio_track_->CallBooleanMethod(start_playout_); 52} 53 54bool AudioTrackJni::JavaAudioTrack::StopPlayout() { 55 return audio_track_->CallBooleanMethod(stop_playout_); 56} 57 58bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) { 59 return audio_track_->CallBooleanMethod(set_stream_volume_, volume); 60} 61 62int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() { 63 return audio_track_->CallIntMethod(get_stream_max_volume_); 64} 65 66int AudioTrackJni::JavaAudioTrack::GetStreamVolume() { 67 return audio_track_->CallIntMethod(get_stream_volume_); 68} 69 70// TODO(henrika): possible extend usage of AudioManager and add it as member. 71AudioTrackJni::AudioTrackJni(AudioManager* audio_manager) 72 : j_environment_(JVM::GetInstance()->environment()), 73 audio_parameters_(audio_manager->GetPlayoutAudioParameters()), 74 direct_buffer_address_(nullptr), 75 direct_buffer_capacity_in_bytes_(0), 76 frames_per_buffer_(0), 77 initialized_(false), 78 playing_(false), 79 audio_device_buffer_(nullptr) { 80 ALOGD("ctor%s", GetThreadInfo().c_str()); 81 RTC_DCHECK(audio_parameters_.is_valid()); 82 RTC_CHECK(j_environment_); 83 JNINativeMethod native_methods[] = { 84 {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V", 85 reinterpret_cast<void*>( 86 &webrtc::AudioTrackJni::CacheDirectBufferAddress)}, 87 {"nativeGetPlayoutData", "(IJ)V", 88 reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}}; 89 j_native_registration_ = j_environment_->RegisterNatives( 90 "org/webrtc/voiceengine/WebRtcAudioTrack", 91 native_methods, arraysize(native_methods)); 92 j_audio_track_.reset(new JavaAudioTrack( 93 j_native_registration_.get(), 94 j_native_registration_->NewObject( 95 "<init>", "(Landroid/content/Context;J)V", 96 JVM::GetInstance()->context(), PointerTojlong(this)))); 97 // Detach from this thread since we want to use the checker to verify calls 98 // from the Java based audio thread. 99 thread_checker_java_.DetachFromThread(); 100} 101 102AudioTrackJni::~AudioTrackJni() { 103 ALOGD("~dtor%s", GetThreadInfo().c_str()); 104 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 105 Terminate(); 106} 107 108int32_t AudioTrackJni::Init() { 109 ALOGD("Init%s", GetThreadInfo().c_str()); 110 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 111 return 0; 112} 113 114int32_t AudioTrackJni::Terminate() { 115 ALOGD("Terminate%s", GetThreadInfo().c_str()); 116 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 117 StopPlayout(); 118 return 0; 119} 120 121int32_t AudioTrackJni::InitPlayout() { 122 ALOGD("InitPlayout%s", GetThreadInfo().c_str()); 123 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 124 RTC_DCHECK(!initialized_); 125 RTC_DCHECK(!playing_); 126 j_audio_track_->InitPlayout( 127 audio_parameters_.sample_rate(), audio_parameters_.channels()); 128 initialized_ = true; 129 return 0; 130} 131 132int32_t AudioTrackJni::StartPlayout() { 133 ALOGD("StartPlayout%s", GetThreadInfo().c_str()); 134 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 135 RTC_DCHECK(initialized_); 136 RTC_DCHECK(!playing_); 137 if (!j_audio_track_->StartPlayout()) { 138 ALOGE("StartPlayout failed!"); 139 return -1; 140 } 141 playing_ = true; 142 return 0; 143} 144 145int32_t AudioTrackJni::StopPlayout() { 146 ALOGD("StopPlayout%s", GetThreadInfo().c_str()); 147 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 148 if (!initialized_ || !playing_) { 149 return 0; 150 } 151 if (!j_audio_track_->StopPlayout()) { 152 ALOGE("StopPlayout failed!"); 153 return -1; 154 } 155 // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded() 156 // next time StartRecording() is called since it will create a new Java 157 // thread. 158 thread_checker_java_.DetachFromThread(); 159 initialized_ = false; 160 playing_ = false; 161 direct_buffer_address_ = nullptr; 162 return 0; 163} 164 165int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) { 166 available = true; 167 return 0; 168} 169 170int AudioTrackJni::SetSpeakerVolume(uint32_t volume) { 171 ALOGD("SetSpeakerVolume(%d)%s", volume, GetThreadInfo().c_str()); 172 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 173 return j_audio_track_->SetStreamVolume(volume) ? 0 : -1; 174} 175 176int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const { 177 ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str()); 178 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 179 max_volume = j_audio_track_->GetStreamMaxVolume(); 180 return 0; 181} 182 183int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const { 184 ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str()); 185 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 186 min_volume = 0; 187 return 0; 188} 189 190int AudioTrackJni::SpeakerVolume(uint32_t& volume) const { 191 ALOGD("SpeakerVolume%s", GetThreadInfo().c_str()); 192 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 193 volume = j_audio_track_->GetStreamVolume(); 194 return 0; 195} 196 197// TODO(henrika): possibly add stereo support. 198void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { 199 ALOGD("AttachAudioBuffer%s", GetThreadInfo().c_str()); 200 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 201 audio_device_buffer_ = audioBuffer; 202 const int sample_rate_hz = audio_parameters_.sample_rate(); 203 ALOGD("SetPlayoutSampleRate(%d)", sample_rate_hz); 204 audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz); 205 const size_t channels = audio_parameters_.channels(); 206 ALOGD("SetPlayoutChannels(%" PRIuS ")", channels); 207 audio_device_buffer_->SetPlayoutChannels(channels); 208} 209 210void JNICALL AudioTrackJni::CacheDirectBufferAddress( 211 JNIEnv* env, jobject obj, jobject byte_buffer, jlong nativeAudioTrack) { 212 webrtc::AudioTrackJni* this_object = 213 reinterpret_cast<webrtc::AudioTrackJni*> (nativeAudioTrack); 214 this_object->OnCacheDirectBufferAddress(env, byte_buffer); 215} 216 217void AudioTrackJni::OnCacheDirectBufferAddress( 218 JNIEnv* env, jobject byte_buffer) { 219 ALOGD("OnCacheDirectBufferAddress"); 220 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 221 RTC_DCHECK(!direct_buffer_address_); 222 direct_buffer_address_ = 223 env->GetDirectBufferAddress(byte_buffer); 224 jlong capacity = env->GetDirectBufferCapacity(byte_buffer); 225 ALOGD("direct buffer capacity: %lld", capacity); 226 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity); 227 frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / kBytesPerFrame; 228 ALOGD("frames_per_buffer: %" PRIuS, frames_per_buffer_); 229} 230 231void JNICALL AudioTrackJni::GetPlayoutData( 232 JNIEnv* env, jobject obj, jint length, jlong nativeAudioTrack) { 233 webrtc::AudioTrackJni* this_object = 234 reinterpret_cast<webrtc::AudioTrackJni*> (nativeAudioTrack); 235 this_object->OnGetPlayoutData(static_cast<size_t>(length)); 236} 237 238// This method is called on a high-priority thread from Java. The name of 239// the thread is 'AudioRecordTrack'. 240void AudioTrackJni::OnGetPlayoutData(size_t length) { 241 RTC_DCHECK(thread_checker_java_.CalledOnValidThread()); 242 RTC_DCHECK_EQ(frames_per_buffer_, length / kBytesPerFrame); 243 if (!audio_device_buffer_) { 244 ALOGE("AttachAudioBuffer has not been called!"); 245 return; 246 } 247 // Pull decoded data (in 16-bit PCM format) from jitter buffer. 248 int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_); 249 if (samples <= 0) { 250 ALOGE("AudioDeviceBuffer::RequestPlayoutData failed!"); 251 return; 252 } 253 RTC_DCHECK_EQ(static_cast<size_t>(samples), frames_per_buffer_); 254 // Copy decoded data into common byte buffer to ensure that it can be 255 // written to the Java based audio track. 256 samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_); 257 RTC_DCHECK_EQ(length, kBytesPerFrame * samples); 258} 259 260} // namespace webrtc 261