xref: /aosp_15_r20/external/webrtc/modules/audio_device/android/aaudio_player.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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