blob: daaeeca1eaf5e19288b9a769e41249d11b58dd16 [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"
Ivo Creusenbdb58302020-05-27 13:41:25 +020022#include "system_wrappers/include/metrics.h"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000023
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000024namespace webrtc {
25
henrikaee369e42015-05-25 10:11:27 +020026// AudioTrackJni::JavaAudioTrack implementation.
27AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
kwiberg0eb15ed2015-12-17 03:04:15 -080028 NativeRegistration* native_reg,
kwibergf01633e2016-02-24 05:00:36 -080029 std::unique_ptr<GlobalRef> audio_track)
kwiberg0eb15ed2015-12-17 03:04:15 -080030 : audio_track_(std::move(audio_track)),
Ivo Creusenbdb58302020-05-27 13:41:25 +020031 init_playout_(native_reg->GetMethodId("initPlayout", "(IID)I")),
henrikac14f5ff2015-09-23 14:08:33 +020032 start_playout_(native_reg->GetMethodId("startPlayout", "()Z")),
33 stop_playout_(native_reg->GetMethodId("stopPlayout", "()Z")),
34 set_stream_volume_(native_reg->GetMethodId("setStreamVolume", "(I)Z")),
kwiberg0eb15ed2015-12-17 03:04:15 -080035 get_stream_max_volume_(
36 native_reg->GetMethodId("getStreamMaxVolume", "()I")),
Ivo Creusenf1393e22020-05-28 13:54:49 +020037 get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")),
38 get_buffer_size_in_frames_(
39 native_reg->GetMethodId("getBufferSizeInFrames", "()I")) {}
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000040
henrikaee369e42015-05-25 10:11:27 +020041AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
42
henrikaef38b562016-04-05 07:20:27 -070043bool AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
Alex Narest44dc2412019-08-29 13:17:11 +020044 double buffer_size_factor =
45 strtod(webrtc::field_trial::FindFullName(
46 "WebRTC-AudioDevicePlayoutBufferSizeFactor")
47 .c_str(),
48 nullptr);
49 if (buffer_size_factor == 0)
50 buffer_size_factor = 1.0;
Ivo Creusenf1393e22020-05-28 13:54:49 +020051 int requested_buffer_size_bytes = audio_track_->CallIntMethod(
Ivo Creusenbdb58302020-05-27 13:41:25 +020052 init_playout_, sample_rate, channels, buffer_size_factor);
Ivo Creusenf1393e22020-05-28 13:54:49 +020053 // Update UMA histograms for both the requested and actual buffer size.
54 if (requested_buffer_size_bytes >= 0) {
Ivo Creusenbdb58302020-05-27 13:41:25 +020055 // To avoid division by zero, we assume the sample rate is 48k if an invalid
56 // value is found.
57 sample_rate = sample_rate <= 0 ? 48000 : sample_rate;
Ivo Creusenf1393e22020-05-28 13:54:49 +020058 // This calculation assumes that audio is mono.
59 const int requested_buffer_size_ms =
60 (requested_buffer_size_bytes * 1000) / (2 * sample_rate);
Ivo Creusenbdb58302020-05-27 13:41:25 +020061 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AndroidNativeRequestedAudioBufferSizeMs",
Ivo Creusenf1393e22020-05-28 13:54:49 +020062 requested_buffer_size_ms, 0, 1000, 100);
63 int actual_buffer_size_frames =
64 audio_track_->CallIntMethod(get_buffer_size_in_frames_);
65 if (actual_buffer_size_frames >= 0) {
66 const int actual_buffer_size_ms =
67 actual_buffer_size_frames * 1000 / sample_rate;
68 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AndroidNativeAudioBufferSizeMs",
69 actual_buffer_size_ms, 0, 1000, 100);
70 }
Ivo Creusenbdb58302020-05-27 13:41:25 +020071 return true;
72 }
73 return false;
henrikaee369e42015-05-25 10:11:27 +020074}
75
76bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
77 return audio_track_->CallBooleanMethod(start_playout_);
78}
79
80bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
81 return audio_track_->CallBooleanMethod(stop_playout_);
82}
83
84bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
85 return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
86}
87
88int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
89 return audio_track_->CallIntMethod(get_stream_max_volume_);
90}
91
92int AudioTrackJni::JavaAudioTrack::GetStreamVolume() {
93 return audio_track_->CallIntMethod(get_stream_volume_);
henrike@webrtc.org573a1b42014-01-10 22:58:06 +000094}
95
henrika8324b522015-03-27 10:56:23 +010096// TODO(henrika): possible extend usage of AudioManager and add it as member.
97AudioTrackJni::AudioTrackJni(AudioManager* audio_manager)
kwiberg1c7fdd82016-04-26 08:18:04 -070098 : j_environment_(JVM::GetInstance()->environment()),
henrikaee369e42015-05-25 10:11:27 +020099 audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
100 direct_buffer_address_(nullptr),
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000101 direct_buffer_capacity_in_bytes_(0),
102 frames_per_buffer_(0),
103 initialized_(false),
104 playing_(false),
henrikaee369e42015-05-25 10:11:27 +0200105 audio_device_buffer_(nullptr) {
henrikac77b5282018-01-09 11:17:41 +0100106 RTC_LOG(INFO) << "ctor";
henrikg91d6ede2015-09-17 00:24:34 -0700107 RTC_DCHECK(audio_parameters_.is_valid());
108 RTC_CHECK(j_environment_);
henrikaee369e42015-05-25 10:11:27 +0200109 JNINativeMethod native_methods[] = {
110 {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +0100111 reinterpret_cast<void*>(
112 &webrtc::AudioTrackJni::CacheDirectBufferAddress)},
henrikaee369e42015-05-25 10:11:27 +0200113 {"nativeGetPlayoutData", "(IJ)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +0100114 reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}};
kwiberg1c7fdd82016-04-26 08:18:04 -0700115 j_native_registration_ = j_environment_->RegisterNatives(
116 "org/webrtc/voiceengine/WebRtcAudioTrack", native_methods,
117 arraysize(native_methods));
sakald7fdb802017-05-26 01:51:53 -0700118 j_audio_track_.reset(
119 new JavaAudioTrack(j_native_registration_.get(),
120 j_native_registration_->NewObject(
121 "<init>", "(J)V", PointerTojlong(this))));
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000122 // Detach from this thread since we want to use the checker to verify calls
123 // from the Java based audio thread.
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200124 thread_checker_java_.Detach();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000125}
126
127AudioTrackJni::~AudioTrackJni() {
henrikac77b5282018-01-09 11:17:41 +0100128 RTC_LOG(INFO) << "dtor";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200129 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000130 Terminate();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000131}
132
133int32_t AudioTrackJni::Init() {
henrikac77b5282018-01-09 11:17:41 +0100134 RTC_LOG(INFO) << "Init";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200135 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000136 return 0;
137}
138
139int32_t AudioTrackJni::Terminate() {
henrikac77b5282018-01-09 11:17:41 +0100140 RTC_LOG(INFO) << "Terminate";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200141 RTC_DCHECK(thread_checker_.IsCurrent());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000142 StopPlayout();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000143 return 0;
144}
145
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000146int32_t AudioTrackJni::InitPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100147 RTC_LOG(INFO) << "InitPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200148 RTC_DCHECK(thread_checker_.IsCurrent());
henrikg91d6ede2015-09-17 00:24:34 -0700149 RTC_DCHECK(!initialized_);
150 RTC_DCHECK(!playing_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100151 if (!j_audio_track_->InitPlayout(audio_parameters_.sample_rate(),
152 audio_parameters_.channels())) {
henrikac77b5282018-01-09 11:17:41 +0100153 RTC_LOG(LS_ERROR) << "InitPlayout failed";
henrikaef38b562016-04-05 07:20:27 -0700154 return -1;
155 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000156 initialized_ = true;
157 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000158}
159
160int32_t AudioTrackJni::StartPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100161 RTC_LOG(INFO) << "StartPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200162 RTC_DCHECK(thread_checker_.IsCurrent());
henrikg91d6ede2015-09-17 00:24:34 -0700163 RTC_DCHECK(!playing_);
henrikacb87efd2018-02-08 15:20:07 +0100164 if (!initialized_) {
165 RTC_DLOG(LS_WARNING)
166 << "Playout can not start since InitPlayout must succeed first";
167 return 0;
168 }
henrikaee369e42015-05-25 10:11:27 +0200169 if (!j_audio_track_->StartPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100170 RTC_LOG(LS_ERROR) << "StartPlayout failed";
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000171 return -1;
172 }
173 playing_ = true;
174 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000175}
176
177int32_t AudioTrackJni::StopPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100178 RTC_LOG(INFO) << "StopPlayout";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200179 RTC_DCHECK(thread_checker_.IsCurrent());
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000180 if (!initialized_ || !playing_) {
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000181 return 0;
182 }
henrikaee369e42015-05-25 10:11:27 +0200183 if (!j_audio_track_->StopPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100184 RTC_LOG(LS_ERROR) << "StopPlayout failed";
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000185 return -1;
186 }
henrikg91d6ede2015-09-17 00:24:34 -0700187 // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
188 // next time StartRecording() is called since it will create a new Java
189 // thread.
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200190 thread_checker_java_.Detach();
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000191 initialized_ = false;
192 playing_ = false;
henrika82e20552015-09-25 04:26:14 -0700193 direct_buffer_address_ = nullptr;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000194 return 0;
195}
196
henrika8324b522015-03-27 10:56:23 +0100197int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
198 available = true;
199 return 0;
200}
201
202int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
henrikac77b5282018-01-09 11:17:41 +0100203 RTC_LOG(INFO) << "SetSpeakerVolume(" << volume << ")";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200204 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200205 return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
henrika8324b522015-03-27 10:56:23 +0100206}
207
208int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200209 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200210 max_volume = j_audio_track_->GetStreamMaxVolume();
henrika8324b522015-03-27 10:56:23 +0100211 return 0;
212}
213
214int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200215 RTC_DCHECK(thread_checker_.IsCurrent());
henrika8324b522015-03-27 10:56:23 +0100216 min_volume = 0;
217 return 0;
218}
219
220int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200221 RTC_DCHECK(thread_checker_.IsCurrent());
henrikaee369e42015-05-25 10:11:27 +0200222 volume = j_audio_track_->GetStreamVolume();
henrikac77b5282018-01-09 11:17:41 +0100223 RTC_LOG(INFO) << "SpeakerVolume: " << volume;
henrika8324b522015-03-27 10:56:23 +0100224 return 0;
225}
226
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000227// TODO(henrika): possibly add stereo support.
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000228void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
henrikac77b5282018-01-09 11:17:41 +0100229 RTC_LOG(INFO) << "AttachAudioBuffer";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200230 RTC_DCHECK(thread_checker_.IsCurrent());
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000231 audio_device_buffer_ = audioBuffer;
henrika8324b522015-03-27 10:56:23 +0100232 const int sample_rate_hz = audio_parameters_.sample_rate();
henrikac77b5282018-01-09 11:17:41 +0100233 RTC_LOG(INFO) << "SetPlayoutSampleRate(" << sample_rate_hz << ")";
henrika8324b522015-03-27 10:56:23 +0100234 audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
Peter Kasting69558702016-01-12 16:26:35 -0800235 const size_t channels = audio_parameters_.channels();
henrikac77b5282018-01-09 11:17:41 +0100236 RTC_LOG(INFO) << "SetPlayoutChannels(" << channels << ")";
henrika8324b522015-03-27 10:56:23 +0100237 audio_device_buffer_->SetPlayoutChannels(channels);
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::CacheDirectBufferAddress(JNIEnv* env,
242 jobject obj,
243 jobject byte_buffer,
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);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000247 this_object->OnCacheDirectBufferAddress(env, byte_buffer);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000248}
249
Mirko Bonadei72c42502017-11-09 09:33:23 +0100250void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env,
251 jobject byte_buffer) {
henrikac77b5282018-01-09 11:17:41 +0100252 RTC_LOG(INFO) << "OnCacheDirectBufferAddress";
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200253 RTC_DCHECK(thread_checker_.IsCurrent());
henrika82e20552015-09-25 04:26:14 -0700254 RTC_DCHECK(!direct_buffer_address_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100255 direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000256 jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
Yves Gerey665174f2018-06-19 15:03:05 +0200257 RTC_LOG(INFO) << "direct buffer capacity: " << capacity;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700258 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
henrika779017d2016-11-16 06:30:46 -0800259 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
260 frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame;
Yves Gerey665174f2018-06-19 15:03:05 +0200261 RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000262}
263
Yura Yaroshevich278d03a2018-03-23 11:47:19 +0300264JNI_FUNCTION_ALIGN
Mirko Bonadei72c42502017-11-09 09:33:23 +0100265void JNICALL AudioTrackJni::GetPlayoutData(JNIEnv* env,
266 jobject obj,
267 jint length,
268 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000269 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100270 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
Peter Kastingdce40cf2015-08-24 14:52:23 -0700271 this_object->OnGetPlayoutData(static_cast<size_t>(length));
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000272}
273
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000274// This method is called on a high-priority thread from Java. The name of
275// the thread is 'AudioRecordTrack'.
Peter Kastingdce40cf2015-08-24 14:52:23 -0700276void AudioTrackJni::OnGetPlayoutData(size_t length) {
Sebastian Janssonc01367d2019-04-08 15:20:44 +0200277 RTC_DCHECK(thread_checker_java_.IsCurrent());
henrika779017d2016-11-16 06:30:46 -0800278 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
279 RTC_DCHECK_EQ(frames_per_buffer_, length / bytes_per_frame);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000280 if (!audio_device_buffer_) {
henrikac77b5282018-01-09 11:17:41 +0100281 RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000282 return;
283 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000284 // Pull decoded data (in 16-bit PCM format) from jitter buffer.
285 int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000286 if (samples <= 0) {
henrikac77b5282018-01-09 11:17:41 +0100287 RTC_LOG(LS_ERROR) << "AudioDeviceBuffer::RequestPlayoutData failed";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000288 return;
289 }
kwiberg352444f2016-11-28 15:58:53 -0800290 RTC_DCHECK_EQ(samples, frames_per_buffer_);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000291 // Copy decoded data into common byte buffer to ensure that it can be
292 // written to the Java based audio track.
293 samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_);
henrika779017d2016-11-16 06:30:46 -0800294 RTC_DCHECK_EQ(length, bytes_per_frame * samples);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000295}
296
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000297} // namespace webrtc