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