xref: /aosp_15_r20/external/webrtc/modules/audio_device/android/audio_record_jni.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 "modules/audio_device/android/audio_record_jni.h"
12 
13 #include <string>
14 #include <utility>
15 
16 #include "modules/audio_device/android/audio_common.h"
17 #include "rtc_base/arraysize.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/platform_thread.h"
21 #include "rtc_base/time_utils.h"
22 #include "system_wrappers/include/metrics.h"
23 
24 namespace webrtc {
25 
26 namespace {
27 // Scoped class which logs its time of life as a UMA statistic. It generates
28 // a histogram which measures the time it takes for a method/scope to execute.
29 class ScopedHistogramTimer {
30  public:
ScopedHistogramTimer(const std::string & name)31   explicit ScopedHistogramTimer(const std::string& name)
32       : histogram_name_(name), start_time_ms_(rtc::TimeMillis()) {}
~ScopedHistogramTimer()33   ~ScopedHistogramTimer() {
34     const int64_t life_time_ms = rtc::TimeSince(start_time_ms_);
35     RTC_HISTOGRAM_COUNTS_1000(histogram_name_, life_time_ms);
36     RTC_LOG(LS_INFO) << histogram_name_ << ": " << life_time_ms;
37   }
38 
39  private:
40   const std::string histogram_name_;
41   int64_t start_time_ms_;
42 };
43 }  // namespace
44 
45 // AudioRecordJni::JavaAudioRecord implementation.
JavaAudioRecord(NativeRegistration * native_reg,std::unique_ptr<GlobalRef> audio_record)46 AudioRecordJni::JavaAudioRecord::JavaAudioRecord(
47     NativeRegistration* native_reg,
48     std::unique_ptr<GlobalRef> audio_record)
49     : audio_record_(std::move(audio_record)),
50       init_recording_(native_reg->GetMethodId("initRecording", "(II)I")),
51       start_recording_(native_reg->GetMethodId("startRecording", "()Z")),
52       stop_recording_(native_reg->GetMethodId("stopRecording", "()Z")),
53       enable_built_in_aec_(native_reg->GetMethodId("enableBuiltInAEC", "(Z)Z")),
54       enable_built_in_ns_(native_reg->GetMethodId("enableBuiltInNS", "(Z)Z")) {}
55 
~JavaAudioRecord()56 AudioRecordJni::JavaAudioRecord::~JavaAudioRecord() {}
57 
InitRecording(int sample_rate,size_t channels)58 int AudioRecordJni::JavaAudioRecord::InitRecording(int sample_rate,
59                                                    size_t channels) {
60   return audio_record_->CallIntMethod(init_recording_,
61                                       static_cast<jint>(sample_rate),
62                                       static_cast<jint>(channels));
63 }
64 
StartRecording()65 bool AudioRecordJni::JavaAudioRecord::StartRecording() {
66   return audio_record_->CallBooleanMethod(start_recording_);
67 }
68 
StopRecording()69 bool AudioRecordJni::JavaAudioRecord::StopRecording() {
70   return audio_record_->CallBooleanMethod(stop_recording_);
71 }
72 
EnableBuiltInAEC(bool enable)73 bool AudioRecordJni::JavaAudioRecord::EnableBuiltInAEC(bool enable) {
74   return audio_record_->CallBooleanMethod(enable_built_in_aec_,
75                                           static_cast<jboolean>(enable));
76 }
77 
EnableBuiltInNS(bool enable)78 bool AudioRecordJni::JavaAudioRecord::EnableBuiltInNS(bool enable) {
79   return audio_record_->CallBooleanMethod(enable_built_in_ns_,
80                                           static_cast<jboolean>(enable));
81 }
82 
83 // AudioRecordJni implementation.
AudioRecordJni(AudioManager * audio_manager)84 AudioRecordJni::AudioRecordJni(AudioManager* audio_manager)
85     : j_environment_(JVM::GetInstance()->environment()),
86       audio_manager_(audio_manager),
87       audio_parameters_(audio_manager->GetRecordAudioParameters()),
88       total_delay_in_milliseconds_(0),
89       direct_buffer_address_(nullptr),
90       direct_buffer_capacity_in_bytes_(0),
91       frames_per_buffer_(0),
92       initialized_(false),
93       recording_(false),
94       audio_device_buffer_(nullptr) {
95   RTC_LOG(LS_INFO) << "ctor";
96   RTC_DCHECK(audio_parameters_.is_valid());
97   RTC_CHECK(j_environment_);
98   JNINativeMethod native_methods[] = {
99       {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
100        reinterpret_cast<void*>(
101            &webrtc::AudioRecordJni::CacheDirectBufferAddress)},
102       {"nativeDataIsRecorded", "(IJ)V",
103        reinterpret_cast<void*>(&webrtc::AudioRecordJni::DataIsRecorded)}};
104   j_native_registration_ = j_environment_->RegisterNatives(
105       "org/webrtc/voiceengine/WebRtcAudioRecord", native_methods,
106       arraysize(native_methods));
107   j_audio_record_.reset(
108       new JavaAudioRecord(j_native_registration_.get(),
109                           j_native_registration_->NewObject(
110                               "<init>", "(J)V", PointerTojlong(this))));
111   // Detach from this thread since we want to use the checker to verify calls
112   // from the Java based audio thread.
113   thread_checker_java_.Detach();
114 }
115 
~AudioRecordJni()116 AudioRecordJni::~AudioRecordJni() {
117   RTC_LOG(LS_INFO) << "dtor";
118   RTC_DCHECK(thread_checker_.IsCurrent());
119   Terminate();
120 }
121 
Init()122 int32_t AudioRecordJni::Init() {
123   RTC_LOG(LS_INFO) << "Init";
124   RTC_DCHECK(thread_checker_.IsCurrent());
125   return 0;
126 }
127 
Terminate()128 int32_t AudioRecordJni::Terminate() {
129   RTC_LOG(LS_INFO) << "Terminate";
130   RTC_DCHECK(thread_checker_.IsCurrent());
131   StopRecording();
132   return 0;
133 }
134 
InitRecording()135 int32_t AudioRecordJni::InitRecording() {
136   RTC_LOG(LS_INFO) << "InitRecording";
137   RTC_DCHECK(thread_checker_.IsCurrent());
138   RTC_DCHECK(!initialized_);
139   RTC_DCHECK(!recording_);
140   ScopedHistogramTimer timer("WebRTC.Audio.InitRecordingDurationMs");
141   int frames_per_buffer = j_audio_record_->InitRecording(
142       audio_parameters_.sample_rate(), audio_parameters_.channels());
143   if (frames_per_buffer < 0) {
144     direct_buffer_address_ = nullptr;
145     RTC_LOG(LS_ERROR) << "InitRecording failed";
146     return -1;
147   }
148   frames_per_buffer_ = static_cast<size_t>(frames_per_buffer);
149   RTC_LOG(LS_INFO) << "frames_per_buffer: " << frames_per_buffer_;
150   const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
151   RTC_CHECK_EQ(direct_buffer_capacity_in_bytes_,
152                frames_per_buffer_ * bytes_per_frame);
153   RTC_CHECK_EQ(frames_per_buffer_, audio_parameters_.frames_per_10ms_buffer());
154   initialized_ = true;
155   return 0;
156 }
157 
StartRecording()158 int32_t AudioRecordJni::StartRecording() {
159   RTC_LOG(LS_INFO) << "StartRecording";
160   RTC_DCHECK(thread_checker_.IsCurrent());
161   RTC_DCHECK(!recording_);
162   if (!initialized_) {
163     RTC_DLOG(LS_WARNING)
164         << "Recording can not start since InitRecording must succeed first";
165     return 0;
166   }
167   ScopedHistogramTimer timer("WebRTC.Audio.StartRecordingDurationMs");
168   if (!j_audio_record_->StartRecording()) {
169     RTC_LOG(LS_ERROR) << "StartRecording failed";
170     return -1;
171   }
172   recording_ = true;
173   return 0;
174 }
175 
StopRecording()176 int32_t AudioRecordJni::StopRecording() {
177   RTC_LOG(LS_INFO) << "StopRecording";
178   RTC_DCHECK(thread_checker_.IsCurrent());
179   if (!initialized_ || !recording_) {
180     return 0;
181   }
182   if (!j_audio_record_->StopRecording()) {
183     RTC_LOG(LS_ERROR) << "StopRecording failed";
184     return -1;
185   }
186   // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
187   // next time StartRecording() is called since it will create a new Java
188   // thread.
189   thread_checker_java_.Detach();
190   initialized_ = false;
191   recording_ = false;
192   direct_buffer_address_ = nullptr;
193   return 0;
194 }
195 
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)196 void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
197   RTC_LOG(LS_INFO) << "AttachAudioBuffer";
198   RTC_DCHECK(thread_checker_.IsCurrent());
199   audio_device_buffer_ = audioBuffer;
200   const int sample_rate_hz = audio_parameters_.sample_rate();
201   RTC_LOG(LS_INFO) << "SetRecordingSampleRate(" << sample_rate_hz << ")";
202   audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz);
203   const size_t channels = audio_parameters_.channels();
204   RTC_LOG(LS_INFO) << "SetRecordingChannels(" << channels << ")";
205   audio_device_buffer_->SetRecordingChannels(channels);
206   total_delay_in_milliseconds_ =
207       audio_manager_->GetDelayEstimateInMilliseconds();
208   RTC_DCHECK_GT(total_delay_in_milliseconds_, 0);
209   RTC_LOG(LS_INFO) << "total_delay_in_milliseconds: "
210                    << total_delay_in_milliseconds_;
211 }
212 
EnableBuiltInAEC(bool enable)213 int32_t AudioRecordJni::EnableBuiltInAEC(bool enable) {
214   RTC_LOG(LS_INFO) << "EnableBuiltInAEC(" << enable << ")";
215   RTC_DCHECK(thread_checker_.IsCurrent());
216   return j_audio_record_->EnableBuiltInAEC(enable) ? 0 : -1;
217 }
218 
EnableBuiltInAGC(bool enable)219 int32_t AudioRecordJni::EnableBuiltInAGC(bool enable) {
220   // TODO(henrika): possibly remove when no longer used by any client.
221   RTC_CHECK_NOTREACHED();
222 }
223 
EnableBuiltInNS(bool enable)224 int32_t AudioRecordJni::EnableBuiltInNS(bool enable) {
225   RTC_LOG(LS_INFO) << "EnableBuiltInNS(" << enable << ")";
226   RTC_DCHECK(thread_checker_.IsCurrent());
227   return j_audio_record_->EnableBuiltInNS(enable) ? 0 : -1;
228 }
229 
230 JNI_FUNCTION_ALIGN
CacheDirectBufferAddress(JNIEnv * env,jobject obj,jobject byte_buffer,jlong nativeAudioRecord)231 void JNICALL AudioRecordJni::CacheDirectBufferAddress(JNIEnv* env,
232                                                       jobject obj,
233                                                       jobject byte_buffer,
234                                                       jlong nativeAudioRecord) {
235   webrtc::AudioRecordJni* this_object =
236       reinterpret_cast<webrtc::AudioRecordJni*>(nativeAudioRecord);
237   this_object->OnCacheDirectBufferAddress(env, byte_buffer);
238 }
239 
OnCacheDirectBufferAddress(JNIEnv * env,jobject byte_buffer)240 void AudioRecordJni::OnCacheDirectBufferAddress(JNIEnv* env,
241                                                 jobject byte_buffer) {
242   RTC_LOG(LS_INFO) << "OnCacheDirectBufferAddress";
243   RTC_DCHECK(thread_checker_.IsCurrent());
244   RTC_DCHECK(!direct_buffer_address_);
245   direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
246   jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
247   RTC_LOG(LS_INFO) << "direct buffer capacity: " << capacity;
248   direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
249 }
250 
251 JNI_FUNCTION_ALIGN
DataIsRecorded(JNIEnv * env,jobject obj,jint length,jlong nativeAudioRecord)252 void JNICALL AudioRecordJni::DataIsRecorded(JNIEnv* env,
253                                             jobject obj,
254                                             jint length,
255                                             jlong nativeAudioRecord) {
256   webrtc::AudioRecordJni* this_object =
257       reinterpret_cast<webrtc::AudioRecordJni*>(nativeAudioRecord);
258   this_object->OnDataIsRecorded(length);
259 }
260 
261 // This method is called on a high-priority thread from Java. The name of
262 // the thread is 'AudioRecordThread'.
OnDataIsRecorded(int length)263 void AudioRecordJni::OnDataIsRecorded(int length) {
264   RTC_DCHECK(thread_checker_java_.IsCurrent());
265   if (!audio_device_buffer_) {
266     RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
267     return;
268   }
269   audio_device_buffer_->SetRecordedBuffer(direct_buffer_address_,
270                                           frames_per_buffer_);
271   // We provide one (combined) fixed delay estimate for the APM and use the
272   // `playDelayMs` parameter only. Components like the AEC only sees the sum
273   // of `playDelayMs` and `recDelayMs`, hence the distributions does not matter.
274   audio_device_buffer_->SetVQEData(total_delay_in_milliseconds_, 0);
275   if (audio_device_buffer_->DeliverRecordedData() == -1) {
276     RTC_LOG(LS_INFO) << "AudioDeviceBuffer::DeliverRecordedData failed";
277   }
278 }
279 
280 }  // namespace webrtc
281