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