blob: 776f0cfd704c5bb1924e95f86433e645c62958a9 [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"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000012
kwiberg0eb15ed2015-12-17 03:04:15 -080013#include <utility>
14
Jonas Olssona4d87372019-07-05 19:08:33 +020015#include "modules/audio_device/android/audio_manager.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/arraysize.h"
17#include "rtc_base/checks.h"
18#include "rtc_base/format_macros.h"
henrikac77b5282018-01-09 11:17:41 +010019#include "rtc_base/logging.h"
Magnus Jedvert9185bde2017-12-28 14:12:05 +010020#include "rtc_base/platform_thread.h"
Alex Narest44dc2412019-08-29 13:17:11 +020021#include "system_wrappers/include/field_trial.h"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000022
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000023namespace webrtc {
24
henrikaee369e42015-05-25 10:11:27 +020025// AudioTrackJni::JavaAudioTrack implementation.
26AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
kwiberg0eb15ed2015-12-17 03:04:15 -080027 NativeRegistration* native_reg,
kwibergf01633e2016-02-24 05:00:36 -080028 std::unique_ptr<GlobalRef> audio_track)
kwiberg0eb15ed2015-12-17 03:04:15 -080029 : audio_track_(std::move(audio_track)),
Alex Narest44dc2412019-08-29 13:17:11 +020030 init_playout_(native_reg->GetMethodId("initPlayout", "(IID)Z")),
henrikac14f5ff2015-09-23 14:08:33 +020031 start_playout_(native_reg->GetMethodId("startPlayout", "()Z")),
32 stop_playout_(native_reg->GetMethodId("stopPlayout", "()Z")),
33 set_stream_volume_(native_reg->GetMethodId("setStreamVolume", "(I)Z")),
kwiberg0eb15ed2015-12-17 03:04:15 -080034 get_stream_max_volume_(
35 native_reg->GetMethodId("getStreamMaxVolume", "()I")),
36 get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")) {}
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000037
henrikaee369e42015-05-25 10:11:27 +020038AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
39
henrikaef38b562016-04-05 07:20:27 -070040bool AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
Alex Narest44dc2412019-08-29 13:17:11 +020041 double buffer_size_factor =
42 strtod(webrtc::field_trial::FindFullName(
43 "WebRTC-AudioDevicePlayoutBufferSizeFactor")
44 .c_str(),
45 nullptr);
46 if (buffer_size_factor == 0)
47 buffer_size_factor = 1.0;
48 return audio_track_->CallBooleanMethod(init_playout_, sample_rate, channels,
49 buffer_size_factor);
henrikaee369e42015-05-25 10:11:27 +020050}
51
52bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
53 return audio_track_->CallBooleanMethod(start_playout_);
54}
55
56bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
57 return audio_track_->CallBooleanMethod(stop_playout_);
58}
59
60bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
61 return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
62}
63
64int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
65 return audio_track_->CallIntMethod(get_stream_max_volume_);
66}
67
68int AudioTrackJni::JavaAudioTrack::GetStreamVolume() {
69 return audio_track_->CallIntMethod(get_stream_volume_);
henrike@webrtc.org573a1b42014-01-10 22:58:06 +000070}
71
henrika8324b522015-03-27 10:56:23 +010072// TODO(henrika): possible extend usage of AudioManager and add it as member.
73AudioTrackJni::AudioTrackJni(AudioManager* audio_manager)
kwiberg1c7fdd82016-04-26 08:18:04 -070074 : j_environment_(JVM::GetInstance()->environment()),
henrikaee369e42015-05-25 10:11:27 +020075 audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
76 direct_buffer_address_(nullptr),
henrika@webrtc.org962c6242015-02-23 11:54:05 +000077 direct_buffer_capacity_in_bytes_(0),
78 frames_per_buffer_(0),
79 initialized_(false),
80 playing_(false),
henrikaee369e42015-05-25 10:11:27 +020081 audio_device_buffer_(nullptr) {
henrikac77b5282018-01-09 11:17:41 +010082 RTC_LOG(INFO) << "ctor";
henrikg91d6ede2015-09-17 00:24:34 -070083 RTC_DCHECK(audio_parameters_.is_valid());
84 RTC_CHECK(j_environment_);
henrikaee369e42015-05-25 10:11:27 +020085 JNINativeMethod native_methods[] = {
86 {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010087 reinterpret_cast<void*>(
88 &webrtc::AudioTrackJni::CacheDirectBufferAddress)},
henrikaee369e42015-05-25 10:11:27 +020089 {"nativeGetPlayoutData", "(IJ)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010090 reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}};
kwiberg1c7fdd82016-04-26 08:18:04 -070091 j_native_registration_ = j_environment_->RegisterNatives(
92 "org/webrtc/voiceengine/WebRtcAudioTrack", native_methods,
93 arraysize(native_methods));
sakald7fdb802017-05-26 01:51:53 -070094 j_audio_track_.reset(
95 new JavaAudioTrack(j_native_registration_.get(),
96 j_native_registration_->NewObject(
97 "<init>", "(J)V", PointerTojlong(this))));
henrika@webrtc.org962c6242015-02-23 11:54:05 +000098 // Detach from this thread since we want to use the checker to verify calls
99 // from the Java based audio thread.
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200100 thread_checker_java_.Detach();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000101}
102
103AudioTrackJni::~AudioTrackJni() {
henrikac77b5282018-01-09 11:17:41 +0100104 RTC_LOG(INFO) << "dtor";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200105 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000106 Terminate();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000107}
108
109int32_t AudioTrackJni::Init() {
henrikac77b5282018-01-09 11:17:41 +0100110 RTC_LOG(INFO) << "Init";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200111 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000112 return 0;
113}
114
115int32_t AudioTrackJni::Terminate() {
henrikac77b5282018-01-09 11:17:41 +0100116 RTC_LOG(INFO) << "Terminate";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200117 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000118 StopPlayout();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000119 return 0;
120}
121
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000122int32_t AudioTrackJni::InitPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100123 RTC_LOG(INFO) << "InitPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200124 RTC_DCHECK(thread_checker_.IsCurrent());
henrikg91d6ede2015-09-17 00:24:34 -0700125 RTC_DCHECK(!initialized_);
126 RTC_DCHECK(!playing_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100127 if (!j_audio_track_->InitPlayout(audio_parameters_.sample_rate(),
128 audio_parameters_.channels())) {
henrikac77b5282018-01-09 11:17:41 +0100129 RTC_LOG(LS_ERROR) << "InitPlayout failed";
henrikaef38b562016-04-05 07:20:27 -0700130 return -1;
131 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000132 initialized_ = true;
133 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000134}
135
136int32_t AudioTrackJni::StartPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100137 RTC_LOG(INFO) << "StartPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200138 RTC_DCHECK(thread_checker_.IsCurrent());
henrikg91d6ede2015-09-17 00:24:34 -0700139 RTC_DCHECK(!playing_);
henrikacb87efd2018-02-08 15:20:07 +0100140 if (!initialized_) {
141 RTC_DLOG(LS_WARNING)
142 << "Playout can not start since InitPlayout must succeed first";
143 return 0;
144 }
henrikaee369e42015-05-25 10:11:27 +0200145 if (!j_audio_track_->StartPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100146 RTC_LOG(LS_ERROR) << "StartPlayout failed";
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000147 return -1;
148 }
149 playing_ = true;
150 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000151}
152
153int32_t AudioTrackJni::StopPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100154 RTC_LOG(INFO) << "StopPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200155 RTC_DCHECK(thread_checker_.IsCurrent());
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000156 if (!initialized_ || !playing_) {
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000157 return 0;
158 }
henrikaee369e42015-05-25 10:11:27 +0200159 if (!j_audio_track_->StopPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100160 RTC_LOG(LS_ERROR) << "StopPlayout failed";
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000161 return -1;
162 }
henrikg91d6ede2015-09-17 00:24:34 -0700163 // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
164 // next time StartRecording() is called since it will create a new Java
165 // thread.
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200166 thread_checker_java_.Detach();
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000167 initialized_ = false;
168 playing_ = false;
henrika82e20552015-09-25 04:26:14 -0700169 direct_buffer_address_ = nullptr;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000170 return 0;
171}
172
henrika8324b522015-03-27 10:56:23 +0100173int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
174 available = true;
175 return 0;
176}
177
178int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
henrikac77b5282018-01-09 11:17:41 +0100179 RTC_LOG(INFO) << "SetSpeakerVolume(" << volume << ")";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200180 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200181 return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
henrika8324b522015-03-27 10:56:23 +0100182}
183
184int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200185 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200186 max_volume = j_audio_track_->GetStreamMaxVolume();
henrika8324b522015-03-27 10:56:23 +0100187 return 0;
188}
189
190int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200191 RTC_DCHECK(thread_checker_.IsCurrent());
henrika8324b522015-03-27 10:56:23 +0100192 min_volume = 0;
193 return 0;
194}
195
196int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200197 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200198 volume = j_audio_track_->GetStreamVolume();
henrikac77b5282018-01-09 11:17:41 +0100199 RTC_LOG(INFO) << "SpeakerVolume: " << volume;
henrika8324b522015-03-27 10:56:23 +0100200 return 0;
201}
202
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000203// TODO(henrika): possibly add stereo support.
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000204void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
henrikac77b5282018-01-09 11:17:41 +0100205 RTC_LOG(INFO) << "AttachAudioBuffer";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200206 RTC_DCHECK(thread_checker_.IsCurrent());
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000207 audio_device_buffer_ = audioBuffer;
henrika8324b522015-03-27 10:56:23 +0100208 const int sample_rate_hz = audio_parameters_.sample_rate();
henrikac77b5282018-01-09 11:17:41 +0100209 RTC_LOG(INFO) << "SetPlayoutSampleRate(" << sample_rate_hz << ")";
henrika8324b522015-03-27 10:56:23 +0100210 audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
Peter Kasting69558702016-01-12 16:26:35 -0800211 const size_t channels = audio_parameters_.channels();
henrikac77b5282018-01-09 11:17:41 +0100212 RTC_LOG(INFO) << "SetPlayoutChannels(" << channels << ")";
henrika8324b522015-03-27 10:56:23 +0100213 audio_device_buffer_->SetPlayoutChannels(channels);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000214}
215
Yura Yaroshevich278d03a2018-03-23 11:47:19 +0300216JNI_FUNCTION_ALIGN
Mirko Bonadei72c42502017-11-09 09:33:23 +0100217void JNICALL AudioTrackJni::CacheDirectBufferAddress(JNIEnv* env,
218 jobject obj,
219 jobject byte_buffer,
220 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000221 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100222 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000223 this_object->OnCacheDirectBufferAddress(env, byte_buffer);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000224}
225
Mirko Bonadei72c42502017-11-09 09:33:23 +0100226void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env,
227 jobject byte_buffer) {
henrikac77b5282018-01-09 11:17:41 +0100228 RTC_LOG(INFO) << "OnCacheDirectBufferAddress";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200229 RTC_DCHECK(thread_checker_.IsCurrent());
henrika82e20552015-09-25 04:26:14 -0700230 RTC_DCHECK(!direct_buffer_address_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100231 direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000232 jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
Yves Gerey665174f2018-06-19 15:03:05 +0200233 RTC_LOG(INFO) << "direct buffer capacity: " << capacity;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700234 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
henrika779017d2016-11-16 06:30:46 -0800235 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
236 frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame;
Yves Gerey665174f2018-06-19 15:03:05 +0200237 RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000238}
239
Yura Yaroshevich278d03a2018-03-23 11:47:19 +0300240JNI_FUNCTION_ALIGN
Mirko Bonadei72c42502017-11-09 09:33:23 +0100241void JNICALL AudioTrackJni::GetPlayoutData(JNIEnv* env,
242 jobject obj,
243 jint length,
244 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000245 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100246 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
Peter Kastingdce40cf2015-08-24 14:52:23 -0700247 this_object->OnGetPlayoutData(static_cast<size_t>(length));
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000248}
249
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000250// This method is called on a high-priority thread from Java. The name of
251// the thread is 'AudioRecordTrack'.
Peter Kastingdce40cf2015-08-24 14:52:23 -0700252void AudioTrackJni::OnGetPlayoutData(size_t length) {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200253 RTC_DCHECK(thread_checker_java_.IsCurrent());
henrika779017d2016-11-16 06:30:46 -0800254 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
255 RTC_DCHECK_EQ(frames_per_buffer_, length / bytes_per_frame);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000256 if (!audio_device_buffer_) {
henrikac77b5282018-01-09 11:17:41 +0100257 RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000258 return;
259 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000260 // Pull decoded data (in 16-bit PCM format) from jitter buffer.
261 int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000262 if (samples <= 0) {
henrikac77b5282018-01-09 11:17:41 +0100263 RTC_LOG(LS_ERROR) << "AudioDeviceBuffer::RequestPlayoutData failed";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000264 return;
265 }
kwiberg352444f2016-11-28 15:58:53 -0800266 RTC_DCHECK_EQ(samples, frames_per_buffer_);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000267 // Copy decoded data into common byte buffer to ensure that it can be
268 // written to the Java based audio track.
269 samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_);
henrika779017d2016-11-16 06:30:46 -0800270 RTC_DCHECK_EQ(length, bytes_per_frame * samples);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000271}
272
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000273} // namespace webrtc