blob: b0e5ddf5f6e59b4c00ac44d9099af69eba5afc24 [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
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"
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000021
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000022namespace webrtc {
23
henrikaee369e42015-05-25 10:11:27 +020024// AudioTrackJni::JavaAudioTrack implementation.
25AudioTrackJni::JavaAudioTrack::JavaAudioTrack(
kwiberg0eb15ed2015-12-17 03:04:15 -080026 NativeRegistration* native_reg,
kwibergf01633e2016-02-24 05:00:36 -080027 std::unique_ptr<GlobalRef> audio_track)
kwiberg0eb15ed2015-12-17 03:04:15 -080028 : audio_track_(std::move(audio_track)),
henrikaef38b562016-04-05 07:20:27 -070029 init_playout_(native_reg->GetMethodId("initPlayout", "(II)Z")),
henrikac14f5ff2015-09-23 14:08:33 +020030 start_playout_(native_reg->GetMethodId("startPlayout", "()Z")),
31 stop_playout_(native_reg->GetMethodId("stopPlayout", "()Z")),
32 set_stream_volume_(native_reg->GetMethodId("setStreamVolume", "(I)Z")),
kwiberg0eb15ed2015-12-17 03:04:15 -080033 get_stream_max_volume_(
34 native_reg->GetMethodId("getStreamMaxVolume", "()I")),
35 get_stream_volume_(native_reg->GetMethodId("getStreamVolume", "()I")) {}
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000036
henrikaee369e42015-05-25 10:11:27 +020037AudioTrackJni::JavaAudioTrack::~JavaAudioTrack() {}
38
henrikaef38b562016-04-05 07:20:27 -070039bool AudioTrackJni::JavaAudioTrack::InitPlayout(int sample_rate, int channels) {
40 return audio_track_->CallBooleanMethod(init_playout_, sample_rate, channels);
henrikaee369e42015-05-25 10:11:27 +020041}
42
43bool AudioTrackJni::JavaAudioTrack::StartPlayout() {
44 return audio_track_->CallBooleanMethod(start_playout_);
45}
46
47bool AudioTrackJni::JavaAudioTrack::StopPlayout() {
48 return audio_track_->CallBooleanMethod(stop_playout_);
49}
50
51bool AudioTrackJni::JavaAudioTrack::SetStreamVolume(int volume) {
52 return audio_track_->CallBooleanMethod(set_stream_volume_, volume);
53}
54
55int AudioTrackJni::JavaAudioTrack::GetStreamMaxVolume() {
56 return audio_track_->CallIntMethod(get_stream_max_volume_);
57}
58
59int AudioTrackJni::JavaAudioTrack::GetStreamVolume() {
60 return audio_track_->CallIntMethod(get_stream_volume_);
henrike@webrtc.org573a1b42014-01-10 22:58:06 +000061}
62
henrika8324b522015-03-27 10:56:23 +010063// TODO(henrika): possible extend usage of AudioManager and add it as member.
64AudioTrackJni::AudioTrackJni(AudioManager* audio_manager)
kwiberg1c7fdd82016-04-26 08:18:04 -070065 : j_environment_(JVM::GetInstance()->environment()),
henrikaee369e42015-05-25 10:11:27 +020066 audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
67 direct_buffer_address_(nullptr),
henrika@webrtc.org962c6242015-02-23 11:54:05 +000068 direct_buffer_capacity_in_bytes_(0),
69 frames_per_buffer_(0),
70 initialized_(false),
71 playing_(false),
henrikaee369e42015-05-25 10:11:27 +020072 audio_device_buffer_(nullptr) {
henrikac77b5282018-01-09 11:17:41 +010073 RTC_LOG(INFO) << "ctor";
henrikg91d6ede2015-09-17 00:24:34 -070074 RTC_DCHECK(audio_parameters_.is_valid());
75 RTC_CHECK(j_environment_);
henrikaee369e42015-05-25 10:11:27 +020076 JNINativeMethod native_methods[] = {
77 {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010078 reinterpret_cast<void*>(
79 &webrtc::AudioTrackJni::CacheDirectBufferAddress)},
henrikaee369e42015-05-25 10:11:27 +020080 {"nativeGetPlayoutData", "(IJ)V",
Mirko Bonadei72c42502017-11-09 09:33:23 +010081 reinterpret_cast<void*>(&webrtc::AudioTrackJni::GetPlayoutData)}};
kwiberg1c7fdd82016-04-26 08:18:04 -070082 j_native_registration_ = j_environment_->RegisterNatives(
83 "org/webrtc/voiceengine/WebRtcAudioTrack", native_methods,
84 arraysize(native_methods));
sakald7fdb802017-05-26 01:51:53 -070085 j_audio_track_.reset(
86 new JavaAudioTrack(j_native_registration_.get(),
87 j_native_registration_->NewObject(
88 "<init>", "(J)V", PointerTojlong(this))));
henrika@webrtc.org962c6242015-02-23 11:54:05 +000089 // Detach from this thread since we want to use the checker to verify calls
90 // from the Java based audio thread.
91 thread_checker_java_.DetachFromThread();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000092}
93
94AudioTrackJni::~AudioTrackJni() {
henrikac77b5282018-01-09 11:17:41 +010095 RTC_LOG(INFO) << "dtor";
henrikg91d6ede2015-09-17 00:24:34 -070096 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000097 Terminate();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +000098}
99
100int32_t AudioTrackJni::Init() {
henrikac77b5282018-01-09 11:17:41 +0100101 RTC_LOG(INFO) << "Init";
henrikg91d6ede2015-09-17 00:24:34 -0700102 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000103 return 0;
104}
105
106int32_t AudioTrackJni::Terminate() {
henrikac77b5282018-01-09 11:17:41 +0100107 RTC_LOG(INFO) << "Terminate";
henrikg91d6ede2015-09-17 00:24:34 -0700108 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000109 StopPlayout();
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000110 return 0;
111}
112
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000113int32_t AudioTrackJni::InitPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100114 RTC_LOG(INFO) << "InitPlayout";
henrikg91d6ede2015-09-17 00:24:34 -0700115 RTC_DCHECK(thread_checker_.CalledOnValidThread());
116 RTC_DCHECK(!initialized_);
117 RTC_DCHECK(!playing_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100118 if (!j_audio_track_->InitPlayout(audio_parameters_.sample_rate(),
119 audio_parameters_.channels())) {
henrikac77b5282018-01-09 11:17:41 +0100120 RTC_LOG(LS_ERROR) << "InitPlayout failed";
henrikaef38b562016-04-05 07:20:27 -0700121 return -1;
122 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000123 initialized_ = true;
124 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000125}
126
127int32_t AudioTrackJni::StartPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100128 RTC_LOG(INFO) << "StartPlayout";
henrikg91d6ede2015-09-17 00:24:34 -0700129 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikg91d6ede2015-09-17 00:24:34 -0700130 RTC_DCHECK(!playing_);
henrikacb87efd2018-02-08 15:20:07 +0100131 if (!initialized_) {
132 RTC_DLOG(LS_WARNING)
133 << "Playout can not start since InitPlayout must succeed first";
134 return 0;
135 }
henrikaee369e42015-05-25 10:11:27 +0200136 if (!j_audio_track_->StartPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100137 RTC_LOG(LS_ERROR) << "StartPlayout failed";
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000138 return -1;
139 }
140 playing_ = true;
141 return 0;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000142}
143
144int32_t AudioTrackJni::StopPlayout() {
henrikac77b5282018-01-09 11:17:41 +0100145 RTC_LOG(INFO) << "StopPlayout";
henrikg91d6ede2015-09-17 00:24:34 -0700146 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000147 if (!initialized_ || !playing_) {
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000148 return 0;
149 }
henrikaee369e42015-05-25 10:11:27 +0200150 if (!j_audio_track_->StopPlayout()) {
henrikac77b5282018-01-09 11:17:41 +0100151 RTC_LOG(LS_ERROR) << "StopPlayout failed";
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000152 return -1;
153 }
henrikg91d6ede2015-09-17 00:24:34 -0700154 // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
155 // next time StartRecording() is called since it will create a new Java
156 // thread.
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000157 thread_checker_java_.DetachFromThread();
158 initialized_ = false;
159 playing_ = false;
henrika82e20552015-09-25 04:26:14 -0700160 direct_buffer_address_ = nullptr;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000161 return 0;
162}
163
henrika8324b522015-03-27 10:56:23 +0100164int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {
165 available = true;
166 return 0;
167}
168
169int AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
henrikac77b5282018-01-09 11:17:41 +0100170 RTC_LOG(INFO) << "SetSpeakerVolume(" << volume << ")";
henrikg91d6ede2015-09-17 00:24:34 -0700171 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200172 return j_audio_track_->SetStreamVolume(volume) ? 0 : -1;
henrika8324b522015-03-27 10:56:23 +0100173}
174
175int AudioTrackJni::MaxSpeakerVolume(uint32_t& max_volume) const {
henrikg91d6ede2015-09-17 00:24:34 -0700176 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200177 max_volume = j_audio_track_->GetStreamMaxVolume();
henrika8324b522015-03-27 10:56:23 +0100178 return 0;
179}
180
181int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const {
henrikg91d6ede2015-09-17 00:24:34 -0700182 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika8324b522015-03-27 10:56:23 +0100183 min_volume = 0;
184 return 0;
185}
186
187int AudioTrackJni::SpeakerVolume(uint32_t& volume) const {
henrikg91d6ede2015-09-17 00:24:34 -0700188 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaee369e42015-05-25 10:11:27 +0200189 volume = j_audio_track_->GetStreamVolume();
henrikac77b5282018-01-09 11:17:41 +0100190 RTC_LOG(INFO) << "SpeakerVolume: " << volume;
henrika8324b522015-03-27 10:56:23 +0100191 return 0;
192}
193
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000194// TODO(henrika): possibly add stereo support.
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000195void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
henrikac77b5282018-01-09 11:17:41 +0100196 RTC_LOG(INFO) << "AttachAudioBuffer";
henrikg91d6ede2015-09-17 00:24:34 -0700197 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000198 audio_device_buffer_ = audioBuffer;
henrika8324b522015-03-27 10:56:23 +0100199 const int sample_rate_hz = audio_parameters_.sample_rate();
henrikac77b5282018-01-09 11:17:41 +0100200 RTC_LOG(INFO) << "SetPlayoutSampleRate(" << sample_rate_hz << ")";
henrika8324b522015-03-27 10:56:23 +0100201 audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz);
Peter Kasting69558702016-01-12 16:26:35 -0800202 const size_t channels = audio_parameters_.channels();
henrikac77b5282018-01-09 11:17:41 +0100203 RTC_LOG(INFO) << "SetPlayoutChannels(" << channels << ")";
henrika8324b522015-03-27 10:56:23 +0100204 audio_device_buffer_->SetPlayoutChannels(channels);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000205}
206
Yura Yaroshevich278d03a2018-03-23 11:47:19 +0300207JNI_FUNCTION_ALIGN
Mirko Bonadei72c42502017-11-09 09:33:23 +0100208void JNICALL AudioTrackJni::CacheDirectBufferAddress(JNIEnv* env,
209 jobject obj,
210 jobject byte_buffer,
211 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000212 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100213 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000214 this_object->OnCacheDirectBufferAddress(env, byte_buffer);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000215}
216
Mirko Bonadei72c42502017-11-09 09:33:23 +0100217void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env,
218 jobject byte_buffer) {
henrikac77b5282018-01-09 11:17:41 +0100219 RTC_LOG(INFO) << "OnCacheDirectBufferAddress";
henrikg91d6ede2015-09-17 00:24:34 -0700220 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrika82e20552015-09-25 04:26:14 -0700221 RTC_DCHECK(!direct_buffer_address_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100222 direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000223 jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
Yves Gerey665174f2018-06-19 15:03:05 +0200224 RTC_LOG(INFO) << "direct buffer capacity: " << capacity;
Peter Kastingdce40cf2015-08-24 14:52:23 -0700225 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
henrika779017d2016-11-16 06:30:46 -0800226 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
227 frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame;
Yves Gerey665174f2018-06-19 15:03:05 +0200228 RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_;
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000229}
230
Yura Yaroshevich278d03a2018-03-23 11:47:19 +0300231JNI_FUNCTION_ALIGN
Mirko Bonadei72c42502017-11-09 09:33:23 +0100232void JNICALL AudioTrackJni::GetPlayoutData(JNIEnv* env,
233 jobject obj,
234 jint length,
235 jlong nativeAudioTrack) {
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000236 webrtc::AudioTrackJni* this_object =
Mirko Bonadei72c42502017-11-09 09:33:23 +0100237 reinterpret_cast<webrtc::AudioTrackJni*>(nativeAudioTrack);
Peter Kastingdce40cf2015-08-24 14:52:23 -0700238 this_object->OnGetPlayoutData(static_cast<size_t>(length));
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000239}
240
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000241// This method is called on a high-priority thread from Java. The name of
242// the thread is 'AudioRecordTrack'.
Peter Kastingdce40cf2015-08-24 14:52:23 -0700243void AudioTrackJni::OnGetPlayoutData(size_t length) {
henrikg91d6ede2015-09-17 00:24:34 -0700244 RTC_DCHECK(thread_checker_java_.CalledOnValidThread());
henrika779017d2016-11-16 06:30:46 -0800245 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
246 RTC_DCHECK_EQ(frames_per_buffer_, length / bytes_per_frame);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000247 if (!audio_device_buffer_) {
henrikac77b5282018-01-09 11:17:41 +0100248 RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000249 return;
250 }
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000251 // Pull decoded data (in 16-bit PCM format) from jitter buffer.
252 int samples = audio_device_buffer_->RequestPlayoutData(frames_per_buffer_);
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000253 if (samples <= 0) {
henrikac77b5282018-01-09 11:17:41 +0100254 RTC_LOG(LS_ERROR) << "AudioDeviceBuffer::RequestPlayoutData failed";
henrika@webrtc.org474d1eb2015-03-09 12:39:53 +0000255 return;
256 }
kwiberg352444f2016-11-28 15:58:53 -0800257 RTC_DCHECK_EQ(samples, frames_per_buffer_);
henrika@webrtc.org962c6242015-02-23 11:54:05 +0000258 // Copy decoded data into common byte buffer to ensure that it can be
259 // written to the Java based audio track.
260 samples = audio_device_buffer_->GetPlayoutData(direct_buffer_address_);
henrika779017d2016-11-16 06:30:46 -0800261 RTC_DCHECK_EQ(length, bytes_per_frame * samples);
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000262}
263
henrike@webrtc.org9ee75e92013-12-11 21:42:44 +0000264} // namespace webrtc