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 "modules/audio_device/android/audio_track_jni.h"
12 #include "modules/audio_device/android/audio_manager.h"
13 
14 #include <utility>
15 
16 #include <android/log.h>
17 
18 #include "rtc_base/arraysize.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_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 
29 namespace webrtc {
30 
31 // AudioTrackJni::JavaAudioTrack implementation.
JavaAudioTrack(NativeRegistration * native_reg,std::unique_ptr<GlobalRef> audio_track)32 AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
33     NativeRegistration* native_reg,
34     std::unique_ptr<GlobalRef> audio_track)
35     : audio_track_(std::move(audio_track)),
36       init_playout_(native_reg->GetMethodId("initPlayout", "(II)Z")),
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 
~JavaAudioTrack()44 AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
45 
InitPlayout(int sample_rate,int channels)46 bool AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
47   return audio_track_->CallBooleanMethod(init_playout_, sample_rate, channels);
48 }
49 
StartPlayout()50 bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
51   return audio_track_->CallBooleanMethod(start_playout_);
52 }
53 
StopPlayout()54 bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
55   return audio_track_->CallBooleanMethod(stop_playout_);
56 }
57 
SetStreamVolume(int volume)58 bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
59   return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
60 }
61 
GetStreamMaxVolume()62 int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
63   return audio_track_->CallIntMethod(get_stream_max_volume_);
64 }
65 
GetStreamVolume()66 int 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.
AudioTrackJni(AudioManager * audio_manager)71 AudioTrackJni::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", native_methods,
91       arraysize(native_methods));
92   j_audio_track_.reset(
93       new JavaAudioTrack(j_native_registration_.get(),
94                          j_native_registration_->NewObject(
95                              "<init>", "(J)V", PointerTojlong(this))));
96   // Detach from this thread since we want to use the checker to verify calls
97   // from the Java based audio thread.
98   thread_checker_java_.DetachFromThread();
99 }
100 
~AudioTrackJni()101 AudioTrackJni::~AudioTrackJni() {
102   ALOGD("~dtor%s", GetThreadInfo().c_str());
103   RTC_DCHECK(thread_checker_.CalledOnValidThread());
104   Terminate();
105 }
106 
Init()107 int32_t AudioTrackJni::Init() {
108   ALOGD("Init%s", GetThreadInfo().c_str());
109   RTC_DCHECK(thread_checker_.CalledOnValidThread());
110   return 0;
111 }
112 
Terminate()113 int32_t AudioTrackJni::Terminate() {
114   ALOGD("Terminate%s", GetThreadInfo().c_str());
115   RTC_DCHECK(thread_checker_.CalledOnValidThread());
116   StopPlayout();
117   return 0;
118 }
119 
InitPlayout()120 int32_t AudioTrackJni::InitPlayout() {
121   ALOGD("InitPlayout%s", GetThreadInfo().c_str());
122   RTC_DCHECK(thread_checker_.CalledOnValidThread());
123   RTC_DCHECK(!initialized_);
124   RTC_DCHECK(!playing_);
125   if (!j_audio_track_->InitPlayout(audio_parameters_.sample_rate(),
126                                    audio_parameters_.channels())) {
127     ALOGE("InitPlayout failed!");
128     return -1;
129   }
130   initialized_ = true;
131   return 0;
132 }
133 
StartPlayout()134 int32_t AudioTrackJni::StartPlayout() {
135   ALOGD("StartPlayout%s", GetThreadInfo().c_str());
136   RTC_DCHECK(thread_checker_.CalledOnValidThread());
137   RTC_DCHECK(initialized_);
138   RTC_DCHECK(!playing_);
139   if (!j_audio_track_->StartPlayout()) {
140     ALOGE("StartPlayout failed!");
141     return -1;
142   }
143   playing_ = true;
144   return 0;
145 }
146 
StopPlayout()147 int32_t AudioTrackJni::StopPlayout() {
148   ALOGD("StopPlayout%s", GetThreadInfo().c_str());
149   RTC_DCHECK(thread_checker_.CalledOnValidThread());
150   if (!initialized_ || !playing_) {
151     return 0;
152   }
153   if (!j_audio_track_->StopPlayout()) {
154     ALOGE("StopPlayout failed!");
155     return -1;
156   }
157   // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
158   // next time StartRecording() is called since it will create a new Java
159   // thread.
160   thread_checker_java_.DetachFromThread();
161   initialized_ = false;
162   playing_ = false;
163   direct_buffer_address_ = nullptr;
164   return 0;
165 }
166 
SpeakerVolumeIsAvailable(bool & available)167 int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
168   available = true;
169   return 0;
170 }
171 
SetSpeakerVolume(uint32_t volume)172 int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
173   ALOGD("SetSpeakerVolume(%d)%s", volume, GetThreadInfo().c_str());
174   RTC_DCHECK(thread_checker_.CalledOnValidThread());
175   return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
176 }
177 
MaxSpeakerVolume(uint32_t & max_volume) const178 int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
179   ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
180   RTC_DCHECK(thread_checker_.CalledOnValidThread());
181   max_volume = j_audio_track_->GetStreamMaxVolume();
182   return 0;
183 }
184 
MinSpeakerVolume(uint32_t & min_volume) const185 int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
186   ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
187   RTC_DCHECK(thread_checker_.CalledOnValidThread());
188   min_volume = 0;
189   return 0;
190 }
191 
SpeakerVolume(uint32_t & volume) const192 int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
193   ALOGD("SpeakerVolume%s", GetThreadInfo().c_str());
194   RTC_DCHECK(thread_checker_.CalledOnValidThread());
195   volume = j_audio_track_->GetStreamVolume();
196   return 0;
197 }
198 
199 // TODO(henrika): possibly add stereo support.
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)200 void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
201   ALOGD("AttachAudioBuffer%s", GetThreadInfo().c_str());
202   RTC_DCHECK(thread_checker_.CalledOnValidThread());
203   audio_device_buffer_ = audioBuffer;
204   const int sample_rate_hz = audio_parameters_.sample_rate();
205   ALOGD("SetPlayoutSampleRate(%d)", sample_rate_hz);
206   audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
207   const size_t channels = audio_parameters_.channels();
208   ALOGD("SetPlayoutChannels(%" PRIuS ")", channels);
209   audio_device_buffer_->SetPlayoutChannels(channels);
210 }
211 
CacheDirectBufferAddress(JNIEnv * env,jobject obj,jobject byte_buffer,jlong nativeAudioTrack)212 void JNICALL AudioTrackJni::CacheDirectBufferAddress(JNIEnv* env,
213                                                      jobject obj,
214                                                      jobject byte_buffer,
215                                                      jlong nativeAudioTrack) {
216   webrtc::AudioTrackJni* this_object =
217       reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
218   this_object->OnCacheDirectBufferAddress(env, byte_buffer);
219 }
220 
OnCacheDirectBufferAddress(JNIEnv * env,jobject byte_buffer)221 void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env,
222                                                jobject byte_buffer) {
223   ALOGD("OnCacheDirectBufferAddress");
224   RTC_DCHECK(thread_checker_.CalledOnValidThread());
225   RTC_DCHECK(!direct_buffer_address_);
226   direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
227   jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
228   ALOGD("direct buffer capacity: %lld", capacity);
229   direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
230   const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
231   frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame;
232   ALOGD("frames_per_buffer: %" PRIuS, frames_per_buffer_);
233 }
234 
GetPlayoutData(JNIEnv * env,jobject obj,jint length,jlong nativeAudioTrack)235 void JNICALL AudioTrackJni::GetPlayoutData(JNIEnv* env,
236                                            jobject obj,
237                                            jint length,
238                                            jlong nativeAudioTrack) {
239   webrtc::AudioTrackJni* this_object =
240       reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
241   this_object->OnGetPlayoutData(static_cast<size_t>(length));
242 }
243 
244 // This method is called on a high-priority thread from Java. The name of
245 // the thread is 'AudioRecordTrack'.
OnGetPlayoutData(size_t length)246 void AudioTrackJni::OnGetPlayoutData(size_t length) {
247   RTC_DCHECK(thread_checker_java_.CalledOnValidThread());
248   const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
249   RTC_DCHECK_EQ(frames_per_buffer_, length / bytes_per_frame);
250   if (!audio_device_buffer_) {
251     ALOGE("AttachAudioBuffer has not been called!");
252     return;
253   }
254   // Pull decoded data (in 16-bit PCM format) from jitter buffer.
255   int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
256   if (samples <= 0) {
257     ALOGE("AudioDeviceBuffer::RequestPlayoutData failed!");
258     return;
259   }
260   RTC_DCHECK_EQ(samples, frames_per_buffer_);
261   // Copy decoded data into common byte buffer to ensure that it can be
262   // written to the Java based audio track.
263   samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_);
264   RTC_DCHECK_EQ(length, bytes_per_frame * samples);
265 }
266 
267 }  // namespace webrtc
268