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 "sdk/android/src/jni/audio_device/audio_record_jni.h"
12
13 #include <string>
14 #include <utility>
15
16 #include "rtc_base/arraysize.h"
17 #include "rtc_base/checks.h"
18 #include "rtc_base/format_macros.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/platform_thread.h"
21 #include "rtc_base/time_utils.h"
22 #include "sdk/android/generated_java_audio_device_module_native_jni/WebRtcAudioRecord_jni.h"
23 #include "sdk/android/src/jni/audio_device/audio_common.h"
24 #include "sdk/android/src/jni/jni_helpers.h"
25 #include "system_wrappers/include/metrics.h"
26
27 namespace webrtc {
28
29 namespace jni {
30
31 namespace {
32 // Scoped class which logs its time of life as a UMA statistic. It generates
33 // a histogram which measures the time it takes for a method/scope to execute.
34 class ScopedHistogramTimer {
35 public:
ScopedHistogramTimer(const std::string & name)36 explicit ScopedHistogramTimer(const std::string& name)
37 : histogram_name_(name), start_time_ms_(rtc::TimeMillis()) {}
~ScopedHistogramTimer()38 ~ScopedHistogramTimer() {
39 const int64_t life_time_ms = rtc::TimeSince(start_time_ms_);
40 RTC_HISTOGRAM_COUNTS_1000(histogram_name_, life_time_ms);
41 RTC_LOG(INFO) << histogram_name_ << ": " << life_time_ms;
42 }
43
44 private:
45 const std::string histogram_name_;
46 int64_t start_time_ms_;
47 };
48
49 } // namespace
50
CreateJavaWebRtcAudioRecord(JNIEnv * env,const JavaRef<jobject> & j_context,const JavaRef<jobject> & j_audio_manager)51 ScopedJavaLocalRef<jobject> AudioRecordJni::CreateJavaWebRtcAudioRecord(
52 JNIEnv* env,
53 const JavaRef<jobject>& j_context,
54 const JavaRef<jobject>& j_audio_manager) {
55 return Java_WebRtcAudioRecord_Constructor(env, j_context, j_audio_manager);
56 }
57
AudioRecordJni(JNIEnv * env,const AudioParameters & audio_parameters,int total_delay_ms,const JavaRef<jobject> & j_audio_record)58 AudioRecordJni::AudioRecordJni(JNIEnv* env,
59 const AudioParameters& audio_parameters,
60 int total_delay_ms,
61 const JavaRef<jobject>& j_audio_record)
62 : j_audio_record_(env, j_audio_record),
63 audio_parameters_(audio_parameters),
64 total_delay_ms_(total_delay_ms),
65 direct_buffer_address_(nullptr),
66 direct_buffer_capacity_in_bytes_(0),
67 frames_per_buffer_(0),
68 initialized_(false),
69 recording_(false),
70 audio_device_buffer_(nullptr) {
71 RTC_LOG(INFO) << "ctor";
72 RTC_DCHECK(audio_parameters_.is_valid());
73 Java_WebRtcAudioRecord_setNativeAudioRecord(env, j_audio_record_,
74 jni::jlongFromPointer(this));
75 // Detach from this thread since construction is allowed to happen on a
76 // different thread.
77 thread_checker_.Detach();
78 thread_checker_java_.Detach();
79 }
80
~AudioRecordJni()81 AudioRecordJni::~AudioRecordJni() {
82 RTC_LOG(INFO) << "dtor";
83 RTC_DCHECK(thread_checker_.IsCurrent());
84 Terminate();
85 }
86
Init()87 int32_t AudioRecordJni::Init() {
88 RTC_LOG(INFO) << "Init";
89 env_ = AttachCurrentThreadIfNeeded();
90 RTC_DCHECK(thread_checker_.IsCurrent());
91 return 0;
92 }
93
Terminate()94 int32_t AudioRecordJni::Terminate() {
95 RTC_LOG(INFO) << "Terminate";
96 RTC_DCHECK(thread_checker_.IsCurrent());
97 StopRecording();
98 thread_checker_.Detach();
99 return 0;
100 }
101
InitRecording()102 int32_t AudioRecordJni::InitRecording() {
103 RTC_LOG(INFO) << "InitRecording";
104 RTC_DCHECK(thread_checker_.IsCurrent());
105 if (initialized_) {
106 // Already initialized.
107 return 0;
108 }
109 RTC_DCHECK(!recording_);
110 ScopedHistogramTimer timer("WebRTC.Audio.InitRecordingDurationMs");
111
112 int frames_per_buffer = Java_WebRtcAudioRecord_initRecording(
113 env_, j_audio_record_, audio_parameters_.sample_rate(),
114 static_cast<int>(audio_parameters_.channels()));
115 if (frames_per_buffer < 0) {
116 direct_buffer_address_ = nullptr;
117 RTC_LOG(LS_ERROR) << "InitRecording failed";
118 return -1;
119 }
120 frames_per_buffer_ = static_cast<size_t>(frames_per_buffer);
121 RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_;
122 const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
123 RTC_CHECK_EQ(direct_buffer_capacity_in_bytes_,
124 frames_per_buffer_ * bytes_per_frame);
125 RTC_CHECK_EQ(frames_per_buffer_, audio_parameters_.frames_per_10ms_buffer());
126 initialized_ = true;
127 return 0;
128 }
129
RecordingIsInitialized() const130 bool AudioRecordJni::RecordingIsInitialized() const {
131 return initialized_;
132 }
133
StartRecording()134 int32_t AudioRecordJni::StartRecording() {
135 RTC_LOG(INFO) << "StartRecording";
136 RTC_DCHECK(thread_checker_.IsCurrent());
137 if (recording_) {
138 // Already recording.
139 return 0;
140 }
141 if (!initialized_) {
142 RTC_DLOG(LS_WARNING)
143 << "Recording can not start since InitRecording must succeed first";
144 return 0;
145 }
146 ScopedHistogramTimer timer("WebRTC.Audio.StartRecordingDurationMs");
147 if (!Java_WebRtcAudioRecord_startRecording(env_, j_audio_record_)) {
148 RTC_LOG(LS_ERROR) << "StartRecording failed";
149 return -1;
150 }
151 recording_ = true;
152 return 0;
153 }
154
StopRecording()155 int32_t AudioRecordJni::StopRecording() {
156 RTC_LOG(INFO) << "StopRecording";
157 RTC_DCHECK(thread_checker_.IsCurrent());
158 if (!initialized_ || !recording_) {
159 return 0;
160 }
161 // Check if the audio source matched the activated recording session but only
162 // if a valid results exists to avoid invalid statistics.
163 if (Java_WebRtcAudioRecord_isAudioConfigVerified(env_, j_audio_record_)) {
164 const bool session_was_ok =
165 Java_WebRtcAudioRecord_isAudioSourceMatchingRecordingSession(
166 env_, j_audio_record_);
167 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.SourceMatchesRecordingSession",
168 session_was_ok);
169 RTC_LOG(INFO) << "HISTOGRAM(WebRTC.Audio.SourceMatchesRecordingSession): "
170 << session_was_ok;
171 }
172 if (!Java_WebRtcAudioRecord_stopRecording(env_, j_audio_record_)) {
173 RTC_LOG(LS_ERROR) << "StopRecording failed";
174 return -1;
175 }
176 // If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
177 // next time StartRecording() is called since it will create a new Java
178 // thread.
179 thread_checker_java_.Detach();
180 initialized_ = false;
181 recording_ = false;
182 direct_buffer_address_ = nullptr;
183 return 0;
184 }
185
Recording() const186 bool AudioRecordJni::Recording() const {
187 return recording_;
188 }
189
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)190 void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
191 RTC_LOG(INFO) << "AttachAudioBuffer";
192 RTC_DCHECK(thread_checker_.IsCurrent());
193 audio_device_buffer_ = audioBuffer;
194 const int sample_rate_hz = audio_parameters_.sample_rate();
195 RTC_LOG(INFO) << "SetRecordingSampleRate(" << sample_rate_hz << ")";
196 audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz);
197 const size_t channels = audio_parameters_.channels();
198 RTC_LOG(INFO) << "SetRecordingChannels(" << channels << ")";
199 audio_device_buffer_->SetRecordingChannels(channels);
200 }
201
IsAcousticEchoCancelerSupported() const202 bool AudioRecordJni::IsAcousticEchoCancelerSupported() const {
203 RTC_DCHECK(thread_checker_.IsCurrent());
204 return Java_WebRtcAudioRecord_isAcousticEchoCancelerSupported(
205 env_, j_audio_record_);
206 }
207
IsNoiseSuppressorSupported() const208 bool AudioRecordJni::IsNoiseSuppressorSupported() const {
209 RTC_DCHECK(thread_checker_.IsCurrent());
210 return Java_WebRtcAudioRecord_isNoiseSuppressorSupported(env_,
211 j_audio_record_);
212 }
213
EnableBuiltInAEC(bool enable)214 int32_t AudioRecordJni::EnableBuiltInAEC(bool enable) {
215 RTC_LOG(INFO) << "EnableBuiltInAEC(" << enable << ")";
216 RTC_DCHECK(thread_checker_.IsCurrent());
217 return Java_WebRtcAudioRecord_enableBuiltInAEC(env_, j_audio_record_, enable)
218 ? 0
219 : -1;
220 }
221
EnableBuiltInNS(bool enable)222 int32_t AudioRecordJni::EnableBuiltInNS(bool enable) {
223 RTC_LOG(INFO) << "EnableBuiltInNS(" << enable << ")";
224 RTC_DCHECK(thread_checker_.IsCurrent());
225 return Java_WebRtcAudioRecord_enableBuiltInNS(env_, j_audio_record_, enable)
226 ? 0
227 : -1;
228 }
229
CacheDirectBufferAddress(JNIEnv * env,const JavaParamRef<jobject> & j_caller,const JavaParamRef<jobject> & byte_buffer)230 void AudioRecordJni::CacheDirectBufferAddress(
231 JNIEnv* env,
232 const JavaParamRef<jobject>& j_caller,
233 const JavaParamRef<jobject>& byte_buffer) {
234 RTC_LOG(INFO) << "OnCacheDirectBufferAddress";
235 RTC_DCHECK(thread_checker_.IsCurrent());
236 RTC_DCHECK(!direct_buffer_address_);
237 direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer.obj());
238 jlong capacity = env->GetDirectBufferCapacity(byte_buffer.obj());
239 RTC_LOG(INFO) << "direct buffer capacity: " << capacity;
240 direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
241 }
242
243 // This method is called on a high-priority thread from Java. The name of
244 // the thread is 'AudioRecordThread'.
DataIsRecorded(JNIEnv * env,const JavaParamRef<jobject> & j_caller,int length)245 void AudioRecordJni::DataIsRecorded(JNIEnv* env,
246 const JavaParamRef<jobject>& j_caller,
247 int length) {
248 RTC_DCHECK(thread_checker_java_.IsCurrent());
249 if (!audio_device_buffer_) {
250 RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
251 return;
252 }
253 audio_device_buffer_->SetRecordedBuffer(direct_buffer_address_,
254 frames_per_buffer_);
255 // We provide one (combined) fixed delay estimate for the APM and use the
256 // |playDelayMs| parameter only. Components like the AEC only sees the sum
257 // of |playDelayMs| and |recDelayMs|, hence the distributions does not matter.
258 audio_device_buffer_->SetVQEData(total_delay_ms_, 0);
259 if (audio_device_buffer_->DeliverRecordedData() == -1) {
260 RTC_LOG(INFO) << "AudioDeviceBuffer::DeliverRecordedData failed";
261 }
262 }
263
264 } // namespace jni
265
266 } // namespace webrtc
267