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