blob: 89d4af0508b4c67f0f79fc7bd6bb7fc852be7d8b [file] [log] [blame]
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_device/android/audio_track_jni.h"
Mirko Bonadei72c42502017-11-09 09:33:23 +010012#include "modules/audio_device/android/audio_manager.h"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000013
kwiberg0eb15ed2015-12-17 03:04:15 -080014#include <utility>
15
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000016#include <android/log.h>
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/arraysize.h"
19#include "rtc_base/checks.h"
20#include "rtc_base/format_macros.h"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000021
henrika@webrtc.org962c6242015-02-23 11:54:05 +000022#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__)
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000028
29namespace webrtc {
30
henrikaee369e42015-05-25 10:11:27 +020031// AudioTrackJni::JavaAudioTrack implementation.
32AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
kwiberg0eb15ed2015-12-17 03:04:15 -080033 NativeRegistration* native_reg,
kwibergf01633e2016-02-24 05:00:36 -080034 std::unique_ptr<GlobalRef> audio_track)
kwiberg0eb15ed2015-12-17 03:04:15 -080035 : audio_track_(std::move(audio_track)),
henrikaef38b562016-04-05 07:20:27 -070036 init_playout_(native_reg->GetMethodId("initPlayout", "(II)Z")),
henrikac14f5ff2015-09-23 14:08:33 +020037 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")),
kwiberg0eb15ed2015-12-17 03:04:15 -080040 get_stream_max_volume_(
41 native_reg->GetMethodId("getStreamMaxVolume", "()I")),
42 get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")) {}
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000043
henrikaee369e42015-05-25 10:11:27 +020044AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
45
henrikaef38b562016-04-05 07:20:27 -070046bool AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
47 return audio_track_->CallBooleanMethod(init_playout_, sample_rate, channels);
henrikaee369e42015-05-25 10:11:27 +020048}
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_);
henrike@webrtc.org573a1b42014-01-10 22:58:06 +000068}
69
henrika8324b522015-03-27 10:56:23 +010070// TODO(henrika): possible extend usage of AudioManager and add it as member.
71AudioTrackJni::AudioTrackJni(AudioManager* audio_manager)
kwiberg1c7fdd82016-04-26 08:18:04 -070072 : j_environment_(JVM::GetInstance()->environment()),
henrikaee369e42015-05-25 10:11:27 +020073 audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
74 direct_buffer_address_(nullptr),
henrika@webrtc.org962c6242015-02-23 11:54:05 +000075 direct_buffer_capacity_in_bytes_(0),
76 frames_per_buffer_(0),
77 initialized_(false),
78 playing_(false),
henrikaee369e42015-05-25 10:11:27 +020079 audio_device_buffer_(nullptr) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +000080 ALOGD("ctor%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -070081 RTC_DCHECK(audio_parameters_.is_valid());
82 RTC_CHECK(j_environment_);
henrikaee369e42015-05-25 10:11:27 +020083 JNINativeMethod native_methods[] = {
84 {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010085 reinterpret_cast<void*>(
86 &webrtc::AudioTrackJni::CacheDirectBufferAddress)},
henrikaee369e42015-05-25 10:11:27 +020087 {"nativeGetPlayoutData", "(IJ)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010088 reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}};
kwiberg1c7fdd82016-04-26 08:18:04 -070089 j_native_registration_ = j_environment_->RegisterNatives(
90 "org/webrtc/voiceengine/WebRtcAudioTrack", native_methods,
91 arraysize(native_methods));
sakald7fdb802017-05-26 01:51:53 -070092 j_audio_track_.reset(
93 new JavaAudioTrack(j_native_registration_.get(),
94 j_native_registration_->NewObject(
95 "<init>", "(J)V", PointerTojlong(this))));
henrika@webrtc.org962c6242015-02-23 11:54:05 +000096 // 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();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000099}
100
101AudioTrackJni::~AudioTrackJni() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000102 ALOGD("~dtor%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700103 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000104 Terminate();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000105}
106
107int32_t AudioTrackJni::Init() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000108 ALOGD("Init%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700109 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000110 return 0;
111}
112
113int32_t AudioTrackJni::Terminate() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000114 ALOGD("Terminate%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700115 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000116 StopPlayout();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000117 return 0;
118}
119
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000120int32_t AudioTrackJni::InitPlayout() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000121 ALOGD("InitPlayout%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700122 RTC_DCHECK(thread_checker_.CalledOnValidThread());
123 RTC_DCHECK(!initialized_);
124 RTC_DCHECK(!playing_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100125 if (!j_audio_track_->InitPlayout(audio_parameters_.sample_rate(),
126 audio_parameters_.channels())) {
henrikaef38b562016-04-05 07:20:27 -0700127 ALOGE("InitPlayout failed!");
128 return -1;
129 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000130 initialized_ = true;
131 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000132}
133
134int32_t AudioTrackJni::StartPlayout() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000135 ALOGD("StartPlayout%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700136 RTC_DCHECK(thread_checker_.CalledOnValidThread());
137 RTC_DCHECK(initialized_);
138 RTC_DCHECK(!playing_);
henrikaee369e42015-05-25 10:11:27 +0200139 if (!j_audio_track_->StartPlayout()) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000140 ALOGE("StartPlayout failed!");
141 return -1;
142 }
143 playing_ = true;
144 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000145}
146
147int32_t AudioTrackJni::StopPlayout() {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000148 ALOGD("StopPlayout%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700149 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000150 if (!initialized_ || !playing_) {
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000151 return 0;
152 }
henrikaee369e42015-05-25 10:11:27 +0200153 if (!j_audio_track_->StopPlayout()) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000154 ALOGE("StopPlayout failed!");
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000155 return -1;
156 }
henrikg91d6ede2015-09-17 00:24:34 -0700157 // 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.
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000160 thread_checker_java_.DetachFromThread();
161 initialized_ = false;
162 playing_ = false;
henrika82e20552015-09-25 04:26:14 -0700163 direct_buffer_address_ = nullptr;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000164 return 0;
165}
166
henrika8324b522015-03-27 10:56:23 +0100167int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
168 available = true;
169 return 0;
170}
171
172int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
173 ALOGD("SetSpeakerVolume(%d)%s", volume, GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700174 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200175 return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
henrika8324b522015-03-27 10:56:23 +0100176}
177
178int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
179 ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700180 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200181 max_volume = j_audio_track_->GetStreamMaxVolume();
henrika8324b522015-03-27 10:56:23 +0100182 return 0;
183}
184
185int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
186 ALOGD("MaxSpeakerVolume%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700187 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika8324b522015-03-27 10:56:23 +0100188 min_volume = 0;
189 return 0;
190}
191
192int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
193 ALOGD("SpeakerVolume%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700194 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200195 volume = j_audio_track_->GetStreamVolume();
henrika8324b522015-03-27 10:56:23 +0100196 return 0;
197}
198
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000199// TODO(henrika): possibly add stereo support.
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000200void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
henrika8324b522015-03-27 10:56:23 +0100201 ALOGD("AttachAudioBuffer%s", GetThreadInfo().c_str());
henrikg91d6ede2015-09-17 00:24:34 -0700202 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000203 audio_device_buffer_ = audioBuffer;
henrika8324b522015-03-27 10:56:23 +0100204 const int sample_rate_hz = audio_parameters_.sample_rate();
205 ALOGD("SetPlayoutSampleRate(%d)", sample_rate_hz);
206 audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
Peter Kasting69558702016-01-12 16:26:35 -0800207 const size_t channels = audio_parameters_.channels();
208 ALOGD("SetPlayoutChannels(%" PRIuS ")", channels);
henrika8324b522015-03-27 10:56:23 +0100209 audio_device_buffer_->SetPlayoutChannels(channels);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000210}
211
Mirko Bonadei72c42502017-11-09 09:33:23 +0100212void JNICALL AudioTrackJni::CacheDirectBufferAddress(JNIEnv* env,
213 jobject obj,
214 jobject byte_buffer,
215 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000216 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100217 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000218 this_object->OnCacheDirectBufferAddress(env, byte_buffer);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000219}
220
Mirko Bonadei72c42502017-11-09 09:33:23 +0100221void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env,
222 jobject byte_buffer) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000223 ALOGD("OnCacheDirectBufferAddress");
henrikg91d6ede2015-09-17 00:24:34 -0700224 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika82e20552015-09-25 04:26:14 -0700225 RTC_DCHECK(!direct_buffer_address_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100226 direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000227 jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
228 ALOGD("direct buffer capacity: %lld", capacity);
Peter Kastingdce40cf2015-08-24 14:52:23 -0700229 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
henrika779017d2016-11-16 06:30:46 -0800230 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
231 frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700232 ALOGD("frames_per_buffer: %" PRIuS, frames_per_buffer_);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000233}
234
Mirko Bonadei72c42502017-11-09 09:33:23 +0100235void JNICALL AudioTrackJni::GetPlayoutData(JNIEnv* env,
236 jobject obj,
237 jint length,
238 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000239 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100240 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
Peter Kastingdce40cf2015-08-24 14:52:23 -0700241 this_object->OnGetPlayoutData(static_cast<size_t>(length));
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000242}
243
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000244// This method is called on a high-priority thread from Java. The name of
245// the thread is 'AudioRecordTrack'.
Peter Kastingdce40cf2015-08-24 14:52:23 -0700246void AudioTrackJni::OnGetPlayoutData(size_t length) {
henrikg91d6ede2015-09-17 00:24:34 -0700247 RTC_DCHECK(thread_checker_java_.CalledOnValidThread());
henrika779017d2016-11-16 06:30:46 -0800248 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
249 RTC_DCHECK_EQ(frames_per_buffer_, length / bytes_per_frame);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000250 if (!audio_device_buffer_) {
251 ALOGE("AttachAudioBuffer has not been called!");
252 return;
253 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000254 // Pull decoded data (in 16-bit PCM format) from jitter buffer.
255 int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000256 if (samples <= 0) {
257 ALOGE("AudioDeviceBuffer::RequestPlayoutData failed!");
258 return;
259 }
kwiberg352444f2016-11-28 15:58:53 -0800260 RTC_DCHECK_EQ(samples, frames_per_buffer_);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000261 // 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_);
henrika779017d2016-11-16 06:30:46 -0800264 RTC_DCHECK_EQ(length, bytes_per_frame * samples);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000265}
266
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000267} // namespace webrtc