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
29 namespace webrtc {
30
31 // AudioTrackJni::JavaAudioTrack implementation.
JavaAudioTrack(NativeRegistration * native_reg,rtc::scoped_ptr<GlobalRef> audio_track)32 AudioTrackJni::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
~JavaAudioTrack()44 AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
45
InitPlayout(int sample_rate,int channels)46 void AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
47 audio_track_->CallVoidMethod(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",
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
~AudioTrackJni()102 AudioTrackJni::~AudioTrackJni() {
103 ALOGD("~dtor%s", GetThreadInfo().c_str());
104 RTC_DCHECK(thread_checker_.CalledOnValidThread());
105 Terminate();
106 }
107
Init()108 int32_t AudioTrackJni::Init() {
109 ALOGD("Init%s", GetThreadInfo().c_str());
110 RTC_DCHECK(thread_checker_.CalledOnValidThread());
111 return 0;
112 }
113
Terminate()114 int32_t AudioTrackJni::Terminate() {
115 ALOGD("Terminate%s", GetThreadInfo().c_str());
116 RTC_DCHECK(thread_checker_.CalledOnValidThread());
117 StopPlayout();
118 return 0;
119 }
120
InitPlayout()121 int32_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
StartPlayout()132 int32_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
StopPlayout()145 int32_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
SpeakerVolumeIsAvailable(bool & available)165 int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
166 available = true;
167 return 0;
168 }
169
SetSpeakerVolume(uint32_t volume)170 int 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
MaxSpeakerVolume(uint32_t & max_volume) const176 int 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
MinSpeakerVolume(uint32_t & min_volume) const183 int 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
SpeakerVolume(uint32_t & volume) const190 int 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.
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)198 void 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
CacheDirectBufferAddress(JNIEnv * env,jobject obj,jobject byte_buffer,jlong nativeAudioTrack)210 void 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
OnCacheDirectBufferAddress(JNIEnv * env,jobject byte_buffer)217 void 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
GetPlayoutData(JNIEnv * env,jobject obj,jint length,jlong nativeAudioTrack)231 void 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'.
OnGetPlayoutData(size_t length)240 void 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