1 /*
2 * Copyright (c) 2018 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/aaudio_recorder.h"
12
13 #include <memory>
14
15 #include "api/array_view.h"
16 #include "modules/audio_device/fine_audio_buffer.h"
17 #include "rtc_base/checks.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/time_utils.h"
20
21 namespace webrtc {
22
23 namespace jni {
24
AAudioRecorder(const AudioParameters & audio_parameters)25 AAudioRecorder::AAudioRecorder(const AudioParameters& audio_parameters)
26 : main_thread_(TaskQueueBase::Current()),
27 aaudio_(audio_parameters, AAUDIO_DIRECTION_INPUT, this) {
28 RTC_LOG(LS_INFO) << "ctor";
29 thread_checker_aaudio_.Detach();
30 }
31
~AAudioRecorder()32 AAudioRecorder::~AAudioRecorder() {
33 RTC_LOG(LS_INFO) << "dtor";
34 RTC_DCHECK(thread_checker_.IsCurrent());
35 Terminate();
36 RTC_LOG(LS_INFO) << "detected owerflows: " << overflow_count_;
37 }
38
Init()39 int AAudioRecorder::Init() {
40 RTC_LOG(LS_INFO) << "Init";
41 RTC_DCHECK(thread_checker_.IsCurrent());
42 if (aaudio_.audio_parameters().channels() == 2) {
43 RTC_DLOG(LS_WARNING) << "Stereo mode is enabled";
44 }
45 return 0;
46 }
47
Terminate()48 int AAudioRecorder::Terminate() {
49 RTC_LOG(LS_INFO) << "Terminate";
50 RTC_DCHECK(thread_checker_.IsCurrent());
51 StopRecording();
52 return 0;
53 }
54
InitRecording()55 int AAudioRecorder::InitRecording() {
56 RTC_LOG(LS_INFO) << "InitRecording";
57 RTC_DCHECK(thread_checker_.IsCurrent());
58 RTC_DCHECK(!initialized_);
59 RTC_DCHECK(!recording_);
60 if (!aaudio_.Init()) {
61 return -1;
62 }
63 initialized_ = true;
64 return 0;
65 }
66
RecordingIsInitialized() const67 bool AAudioRecorder::RecordingIsInitialized() const {
68 return initialized_;
69 }
70
StartRecording()71 int AAudioRecorder::StartRecording() {
72 RTC_LOG(LS_INFO) << "StartRecording";
73 RTC_DCHECK(thread_checker_.IsCurrent());
74 RTC_DCHECK(initialized_);
75 RTC_DCHECK(!recording_);
76 if (fine_audio_buffer_) {
77 fine_audio_buffer_->ResetPlayout();
78 }
79 if (!aaudio_.Start()) {
80 return -1;
81 }
82 overflow_count_ = aaudio_.xrun_count();
83 first_data_callback_ = true;
84 recording_ = true;
85 return 0;
86 }
87
StopRecording()88 int AAudioRecorder::StopRecording() {
89 RTC_LOG(LS_INFO) << "StopRecording";
90 RTC_DCHECK(thread_checker_.IsCurrent());
91 if (!initialized_ || !recording_) {
92 return 0;
93 }
94 if (!aaudio_.Stop()) {
95 return -1;
96 }
97 thread_checker_aaudio_.Detach();
98 initialized_ = false;
99 recording_ = false;
100 return 0;
101 }
102
Recording() const103 bool AAudioRecorder::Recording() const {
104 return recording_;
105 }
106
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)107 void AAudioRecorder::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
108 RTC_LOG(LS_INFO) << "AttachAudioBuffer";
109 RTC_DCHECK(thread_checker_.IsCurrent());
110 audio_device_buffer_ = audioBuffer;
111 const AudioParameters audio_parameters = aaudio_.audio_parameters();
112 audio_device_buffer_->SetRecordingSampleRate(audio_parameters.sample_rate());
113 audio_device_buffer_->SetRecordingChannels(audio_parameters.channels());
114 RTC_CHECK(audio_device_buffer_);
115 // Create a modified audio buffer class which allows us to deliver any number
116 // of samples (and not only multiples of 10ms which WebRTC uses) to match the
117 // native AAudio buffer size.
118 fine_audio_buffer_ = std::make_unique<FineAudioBuffer>(audio_device_buffer_);
119 }
120
IsAcousticEchoCancelerSupported() const121 bool AAudioRecorder::IsAcousticEchoCancelerSupported() const {
122 return false;
123 }
124
IsNoiseSuppressorSupported() const125 bool AAudioRecorder::IsNoiseSuppressorSupported() const {
126 return false;
127 }
128
EnableBuiltInAEC(bool enable)129 int AAudioRecorder::EnableBuiltInAEC(bool enable) {
130 RTC_LOG(LS_INFO) << "EnableBuiltInAEC: " << enable;
131 RTC_LOG(LS_ERROR) << "Not implemented";
132 return -1;
133 }
134
EnableBuiltInNS(bool enable)135 int AAudioRecorder::EnableBuiltInNS(bool enable) {
136 RTC_LOG(LS_INFO) << "EnableBuiltInNS: " << enable;
137 RTC_LOG(LS_ERROR) << "Not implemented";
138 return -1;
139 }
140
OnErrorCallback(aaudio_result_t error)141 void AAudioRecorder::OnErrorCallback(aaudio_result_t error) {
142 RTC_LOG(LS_ERROR) << "OnErrorCallback: " << AAudio_convertResultToText(error);
143 // RTC_DCHECK(thread_checker_aaudio_.IsCurrent());
144 if (aaudio_.stream_state() == AAUDIO_STREAM_STATE_DISCONNECTED) {
145 // The stream is disconnected and any attempt to use it will return
146 // AAUDIO_ERROR_DISCONNECTED..
147 RTC_LOG(LS_WARNING) << "Input stream disconnected => restart is required";
148 // AAudio documentation states: "You should not close or reopen the stream
149 // from the callback, use another thread instead". A message is therefore
150 // sent to the main thread to do the restart operation.
151 RTC_DCHECK(main_thread_);
152 main_thread_->PostTask([this] { HandleStreamDisconnected(); });
153 }
154 }
155
156 // Read and process `num_frames` of data from the `audio_data` buffer.
157 // TODO(henrika): possibly add trace here to be included in systrace.
158 // See https://developer.android.com/studio/profile/systrace-commandline.html.
OnDataCallback(void * audio_data,int32_t num_frames)159 aaudio_data_callback_result_t AAudioRecorder::OnDataCallback(
160 void* audio_data,
161 int32_t num_frames) {
162 // TODO(henrika): figure out why we sometimes hit this one.
163 // RTC_DCHECK(thread_checker_aaudio_.IsCurrent());
164 // RTC_LOG(LS_INFO) << "OnDataCallback: " << num_frames;
165 // Drain the input buffer at first callback to ensure that it does not
166 // contain any old data. Will also ensure that the lowest possible latency
167 // is obtained.
168 if (first_data_callback_) {
169 RTC_LOG(LS_INFO) << "--- First input data callback: "
170 "device id="
171 << aaudio_.device_id();
172 aaudio_.ClearInputStream(audio_data, num_frames);
173 first_data_callback_ = false;
174 }
175 // Check if the overflow counter has increased and if so log a warning.
176 // TODO(henrika): possible add UMA stat or capacity extension.
177 const int32_t overflow_count = aaudio_.xrun_count();
178 if (overflow_count > overflow_count_) {
179 RTC_LOG(LS_ERROR) << "Overflow detected: " << overflow_count;
180 overflow_count_ = overflow_count;
181 }
182 // Estimated time between an audio frame was recorded by the input device and
183 // it can read on the input stream.
184 latency_millis_ = aaudio_.EstimateLatencyMillis();
185 // TODO(henrika): use for development only.
186 if (aaudio_.frames_read() % (1000 * aaudio_.frames_per_burst()) == 0) {
187 RTC_DLOG(LS_INFO) << "input latency: " << latency_millis_
188 << ", num_frames: " << num_frames;
189 }
190 // Copy recorded audio in `audio_data` to the WebRTC sink using the
191 // FineAudioBuffer object.
192 fine_audio_buffer_->DeliverRecordedData(
193 rtc::MakeArrayView(static_cast<const int16_t*>(audio_data),
194 aaudio_.samples_per_frame() * num_frames),
195 static_cast<int>(latency_millis_ + 0.5));
196
197 return AAUDIO_CALLBACK_RESULT_CONTINUE;
198 }
199
HandleStreamDisconnected()200 void AAudioRecorder::HandleStreamDisconnected() {
201 RTC_DCHECK_RUN_ON(&thread_checker_);
202 RTC_LOG(LS_INFO) << "HandleStreamDisconnected";
203 if (!initialized_ || !recording_) {
204 return;
205 }
206 // Perform a restart by first closing the disconnected stream and then start
207 // a new stream; this time using the new (preferred) audio input device.
208 // TODO(henrika): resolve issue where a one restart attempt leads to a long
209 // sequence of new calls to OnErrorCallback().
210 // See b/73148976 for details.
211 StopRecording();
212 InitRecording();
213 StartRecording();
214 }
215
216 } // namespace jni
217
218 } // namespace webrtc
219