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