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