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