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_player.h"
12
13 #include <memory>
14
15 #include "api/array_view.h"
16 #include "api/task_queue/task_queue_base.h"
17 #include "modules/audio_device/android/audio_manager.h"
18 #include "modules/audio_device/fine_audio_buffer.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21
22 namespace webrtc {
23
AAudioPlayer(AudioManager * audio_manager)24 AAudioPlayer::AAudioPlayer(AudioManager* audio_manager)
25 : main_thread_(TaskQueueBase::Current()),
26 aaudio_(audio_manager, AAUDIO_DIRECTION_OUTPUT, this) {
27 RTC_LOG(LS_INFO) << "ctor";
28 thread_checker_aaudio_.Detach();
29 }
30
~AAudioPlayer()31 AAudioPlayer::~AAudioPlayer() {
32 RTC_LOG(LS_INFO) << "dtor";
33 RTC_DCHECK_RUN_ON(&main_thread_checker_);
34 Terminate();
35 RTC_LOG(LS_INFO) << "#detected underruns: " << underrun_count_;
36 }
37
Init()38 int AAudioPlayer::Init() {
39 RTC_LOG(LS_INFO) << "Init";
40 RTC_DCHECK_RUN_ON(&main_thread_checker_);
41 if (aaudio_.audio_parameters().channels() == 2) {
42 RTC_DLOG(LS_WARNING) << "Stereo mode is enabled";
43 }
44 return 0;
45 }
46
Terminate()47 int AAudioPlayer::Terminate() {
48 RTC_LOG(LS_INFO) << "Terminate";
49 RTC_DCHECK_RUN_ON(&main_thread_checker_);
50 StopPlayout();
51 return 0;
52 }
53
InitPlayout()54 int AAudioPlayer::InitPlayout() {
55 RTC_LOG(LS_INFO) << "InitPlayout";
56 RTC_DCHECK_RUN_ON(&main_thread_checker_);
57 RTC_DCHECK(!initialized_);
58 RTC_DCHECK(!playing_);
59 if (!aaudio_.Init()) {
60 return -1;
61 }
62 initialized_ = true;
63 return 0;
64 }
65
PlayoutIsInitialized() const66 bool AAudioPlayer::PlayoutIsInitialized() const {
67 RTC_DCHECK_RUN_ON(&main_thread_checker_);
68 return initialized_;
69 }
70
StartPlayout()71 int AAudioPlayer::StartPlayout() {
72 RTC_LOG(LS_INFO) << "StartPlayout";
73 RTC_DCHECK_RUN_ON(&main_thread_checker_);
74 RTC_DCHECK(!playing_);
75 if (!initialized_) {
76 RTC_DLOG(LS_WARNING)
77 << "Playout can not start since InitPlayout must succeed first";
78 return 0;
79 }
80 if (fine_audio_buffer_) {
81 fine_audio_buffer_->ResetPlayout();
82 }
83 if (!aaudio_.Start()) {
84 return -1;
85 }
86 underrun_count_ = aaudio_.xrun_count();
87 first_data_callback_ = true;
88 playing_ = true;
89 return 0;
90 }
91
StopPlayout()92 int AAudioPlayer::StopPlayout() {
93 RTC_LOG(LS_INFO) << "StopPlayout";
94 RTC_DCHECK_RUN_ON(&main_thread_checker_);
95 if (!initialized_ || !playing_) {
96 return 0;
97 }
98 if (!aaudio_.Stop()) {
99 RTC_LOG(LS_ERROR) << "StopPlayout failed";
100 return -1;
101 }
102 thread_checker_aaudio_.Detach();
103 initialized_ = false;
104 playing_ = false;
105 return 0;
106 }
107
Playing() const108 bool AAudioPlayer::Playing() const {
109 RTC_DCHECK_RUN_ON(&main_thread_checker_);
110 return playing_;
111 }
112
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)113 void AAudioPlayer::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
114 RTC_DLOG(LS_INFO) << "AttachAudioBuffer";
115 RTC_DCHECK_RUN_ON(&main_thread_checker_);
116 audio_device_buffer_ = audioBuffer;
117 const AudioParameters audio_parameters = aaudio_.audio_parameters();
118 audio_device_buffer_->SetPlayoutSampleRate(audio_parameters.sample_rate());
119 audio_device_buffer_->SetPlayoutChannels(audio_parameters.channels());
120 RTC_CHECK(audio_device_buffer_);
121 // Create a modified audio buffer class which allows us to ask for any number
122 // of samples (and not only multiple of 10ms) to match the optimal buffer
123 // size per callback used by AAudio.
124 fine_audio_buffer_ = std::make_unique<FineAudioBuffer>(audio_device_buffer_);
125 }
126
SpeakerVolumeIsAvailable(bool & available)127 int AAudioPlayer::SpeakerVolumeIsAvailable(bool& available) {
128 available = false;
129 return 0;
130 }
131
OnErrorCallback(aaudio_result_t error)132 void AAudioPlayer::OnErrorCallback(aaudio_result_t error) {
133 RTC_LOG(LS_ERROR) << "OnErrorCallback: " << AAudio_convertResultToText(error);
134 // TODO(henrika): investigate if we can use a thread checker here. Initial
135 // tests shows that this callback can sometimes be called on a unique thread
136 // but according to the documentation it should be on the same thread as the
137 // data callback.
138 // RTC_DCHECK_RUN_ON(&thread_checker_aaudio_);
139 if (aaudio_.stream_state() == AAUDIO_STREAM_STATE_DISCONNECTED) {
140 // The stream is disconnected and any attempt to use it will return
141 // AAUDIO_ERROR_DISCONNECTED.
142 RTC_LOG(LS_WARNING) << "Output stream disconnected";
143 // AAudio documentation states: "You should not close or reopen the stream
144 // from the callback, use another thread instead". A message is therefore
145 // sent to the main thread to do the restart operation.
146 RTC_DCHECK(main_thread_);
147 main_thread_->PostTask([this] { HandleStreamDisconnected(); });
148 }
149 }
150
OnDataCallback(void * audio_data,int32_t num_frames)151 aaudio_data_callback_result_t AAudioPlayer::OnDataCallback(void* audio_data,
152 int32_t num_frames) {
153 RTC_DCHECK_RUN_ON(&thread_checker_aaudio_);
154 // Log device id in first data callback to ensure that a valid device is
155 // utilized.
156 if (first_data_callback_) {
157 RTC_LOG(LS_INFO) << "--- First output data callback: "
158 "device id="
159 << aaudio_.device_id();
160 first_data_callback_ = false;
161 }
162
163 // Check if the underrun count has increased. If it has, increase the buffer
164 // size by adding the size of a burst. It will reduce the risk of underruns
165 // at the expense of an increased latency.
166 // TODO(henrika): enable possibility to disable and/or tune the algorithm.
167 const int32_t underrun_count = aaudio_.xrun_count();
168 if (underrun_count > underrun_count_) {
169 RTC_LOG(LS_ERROR) << "Underrun detected: " << underrun_count;
170 underrun_count_ = underrun_count;
171 aaudio_.IncreaseOutputBufferSize();
172 }
173
174 // Estimate latency between writing an audio frame to the output stream and
175 // the time that same frame is played out on the output audio device.
176 latency_millis_ = aaudio_.EstimateLatencyMillis();
177 // TODO(henrika): use for development only.
178 if (aaudio_.frames_written() % (1000 * aaudio_.frames_per_burst()) == 0) {
179 RTC_DLOG(LS_INFO) << "output latency: " << latency_millis_
180 << ", num_frames: " << num_frames;
181 }
182
183 // Read audio data from the WebRTC source using the FineAudioBuffer object
184 // and write that data into `audio_data` to be played out by AAudio.
185 // Prime output with zeros during a short initial phase to avoid distortion.
186 // TODO(henrika): do more work to figure out of if the initial forced silence
187 // period is really needed.
188 if (aaudio_.frames_written() < 50 * aaudio_.frames_per_burst()) {
189 const size_t num_bytes =
190 sizeof(int16_t) * aaudio_.samples_per_frame() * num_frames;
191 memset(audio_data, 0, num_bytes);
192 } else {
193 fine_audio_buffer_->GetPlayoutData(
194 rtc::MakeArrayView(static_cast<int16_t*>(audio_data),
195 aaudio_.samples_per_frame() * num_frames),
196 static_cast<int>(latency_millis_ + 0.5));
197 }
198
199 // TODO(henrika): possibly add trace here to be included in systrace.
200 // See https://developer.android.com/studio/profile/systrace-commandline.html.
201 return AAUDIO_CALLBACK_RESULT_CONTINUE;
202 }
203
HandleStreamDisconnected()204 void AAudioPlayer::HandleStreamDisconnected() {
205 RTC_DCHECK_RUN_ON(&main_thread_checker_);
206 RTC_DLOG(LS_INFO) << "HandleStreamDisconnected";
207 if (!initialized_ || !playing_) {
208 return;
209 }
210 // Perform a restart by first closing the disconnected stream and then start
211 // a new stream; this time using the new (preferred) audio output device.
212 StopPlayout();
213 InitPlayout();
214 StartPlayout();
215 }
216 } // namespace webrtc
217