blob: f8af9653145191e312c8b1c41f70e78019952d8a [file] [log] [blame]
henrika883d00f2018-03-16 10:09:49 +01001/*
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 "api/array_view.h"
14#include "modules/audio_device/android/audio_manager.h"
15#include "modules/audio_device/fine_audio_buffer.h"
16#include "rtc_base/checks.h"
17#include "rtc_base/logging.h"
18
19namespace webrtc {
20
21enum AudioDeviceMessageType : uint32_t {
22 kMessageOutputStreamDisconnected,
23};
24
25AAudioPlayer::AAudioPlayer(AudioManager* audio_manager)
26 : main_thread_(rtc::Thread::Current()),
27 aaudio_(audio_manager, AAUDIO_DIRECTION_OUTPUT, this) {
28 RTC_LOG(INFO) << "ctor";
29 thread_checker_aaudio_.DetachFromThread();
30}
31
32AAudioPlayer::~AAudioPlayer() {
33 RTC_LOG(INFO) << "dtor";
34 RTC_DCHECK_RUN_ON(&main_thread_checker_);
35 Terminate();
36 RTC_LOG(INFO) << "#detected underruns: " << underrun_count_;
37}
38
39int AAudioPlayer::Init() {
40 RTC_LOG(INFO) << "Init";
41 RTC_DCHECK_RUN_ON(&main_thread_checker_);
42 RTC_DCHECK_EQ(aaudio_.audio_parameters().channels(), 1u);
43 return 0;
44}
45
46int AAudioPlayer::Terminate() {
47 RTC_LOG(INFO) << "Terminate";
48 RTC_DCHECK_RUN_ON(&main_thread_checker_);
49 StopPlayout();
50 return 0;
51}
52
53int AAudioPlayer::InitPlayout() {
54 RTC_LOG(INFO) << "InitPlayout";
55 RTC_DCHECK_RUN_ON(&main_thread_checker_);
56 RTC_DCHECK(!initialized_);
57 RTC_DCHECK(!playing_);
58 if (!aaudio_.Init()) {
59 return -1;
60 }
61 initialized_ = true;
62 return 0;
63}
64
65bool AAudioPlayer::PlayoutIsInitialized() const {
66 RTC_DCHECK_RUN_ON(&main_thread_checker_);
67 return initialized_;
68}
69
70int AAudioPlayer::StartPlayout() {
71 RTC_LOG(INFO) << "StartPlayout";
72 RTC_DCHECK_RUN_ON(&main_thread_checker_);
73 RTC_DCHECK(!playing_);
74 if (!initialized_) {
75 RTC_DLOG(LS_WARNING)
76 << "Playout can not start since InitPlayout must succeed first";
77 return 0;
78 }
79 if (fine_audio_buffer_) {
80 fine_audio_buffer_->ResetPlayout();
81 }
82 if (!aaudio_.Start()) {
83 return -1;
84 }
85 underrun_count_ = aaudio_.xrun_count();
86 first_data_callback_ = true;
87 playing_ = true;
88 return 0;
89}
90
91int AAudioPlayer::StopPlayout() {
92 RTC_LOG(INFO) << "StopPlayout";
93 RTC_DCHECK_RUN_ON(&main_thread_checker_);
94 if (!initialized_ || !playing_) {
95 return 0;
96 }
97 if (!aaudio_.Stop()) {
98 RTC_LOG(LS_ERROR) << "StopPlayout failed";
99 return -1;
100 }
101 thread_checker_aaudio_.DetachFromThread();
102 initialized_ = false;
103 playing_ = false;
104 return 0;
105}
106
107bool AAudioPlayer::Playing() const {
108 RTC_DCHECK_RUN_ON(&main_thread_checker_);
109 return playing_;
110}
111
112void AAudioPlayer::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
113 RTC_DLOG(INFO) << "AttachAudioBuffer";
114 RTC_DCHECK_RUN_ON(&main_thread_checker_);
115 audio_device_buffer_ = audioBuffer;
116 const AudioParameters audio_parameters = aaudio_.audio_parameters();
117 audio_device_buffer_->SetPlayoutSampleRate(audio_parameters.sample_rate());
118 audio_device_buffer_->SetPlayoutChannels(audio_parameters.channels());
119 RTC_CHECK(audio_device_buffer_);
120 // Create a modified audio buffer class which allows us to ask for any number
121 // of samples (and not only multiple of 10ms) to match the optimal buffer
122 // size per callback used by AAudio. Use an initial capacity of 50ms to ensure
123 // that the buffer can cache old data and at the same time be prepared for
124 // increased burst size in AAudio if buffer underruns are detected.
125 const size_t capacity = 5 * audio_parameters.GetBytesPer10msBuffer();
126 fine_audio_buffer_.reset(new FineAudioBuffer(
127 audio_device_buffer_, audio_parameters.sample_rate(), capacity));
128}
129
130int AAudioPlayer::SpeakerVolumeIsAvailable(bool& available) {
131 available = false;
132 return 0;
133}
134
135void 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
154aaudio_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=" << aaudio_.device_id();
162 first_data_callback_ = false;
163 }
164
165 // Check if the underrun count has increased. If it has, increase the buffer
166 // size by adding the size of a burst. It will reduce the risk of underruns
167 // at the expense of an increased latency.
168 // TODO(henrika): enable possibility to disable and/or tune the algorithm.
169 const int32_t underrun_count = aaudio_.xrun_count();
170 if (underrun_count > underrun_count_) {
171 RTC_LOG(LS_ERROR) << "Underrun detected: " << underrun_count;
172 underrun_count_ = underrun_count;
173 aaudio_.IncreaseOutputBufferSize();
174 }
175
176 // Estimate latency between writing an audio frame to the output stream and
177 // the time that same frame is played out on the output audio device.
178 latency_millis_ = aaudio_.EstimateLatencyMillis();
179 // TODO(henrika): use for development only.
180 if (aaudio_.frames_written() % (1000 * aaudio_.frames_per_burst()) == 0) {
181 RTC_DLOG(INFO) << "output latency: " << latency_millis_
182 << ", num_frames: " << num_frames;
183 }
184
185 // Read audio data from the WebRTC source using the FineAudioBuffer object
186 // and write that data into |audio_data| to be played out by AAudio.
187 const size_t num_bytes =
188 sizeof(int16_t) * aaudio_.samples_per_frame() * num_frames;
189 // Prime output with zeros during a short initial phase to avoid distortion.
190 // TODO(henrika): do more work to figure out of if the initial forced silence
191 // period is really needed.
192 if (aaudio_.frames_written() < 50 * aaudio_.frames_per_burst()) {
193 memset(audio_data, 0, num_bytes);
194 } else {
195 fine_audio_buffer_->GetPlayoutData(
196 rtc::ArrayView<int8_t>(static_cast<int8_t*>(audio_data), num_bytes),
197 static_cast<int>(latency_millis_ + 0.5));
198 }
199
200 // TODO(henrika): possibly add trace here to be included in systrace.
201 // See https://developer.android.com/studio/profile/systrace-commandline.html.
202 return AAUDIO_CALLBACK_RESULT_CONTINUE;
203}
204
205void AAudioPlayer::OnMessage(rtc::Message* msg) {
206 RTC_DCHECK_RUN_ON(&main_thread_checker_);
207 switch (msg->message_id) {
208 case kMessageOutputStreamDisconnected:
209 HandleStreamDisconnected();
210 break;
211 }
212}
213
214void AAudioPlayer::HandleStreamDisconnected() {
215 RTC_DCHECK_RUN_ON(&main_thread_checker_);
216 RTC_DLOG(INFO) << "HandleStreamDisconnected";
217 if (!initialized_ || !playing_) {
218 return;
219 }
220 // Perform a restart by first closing the disconnected stream and then start
221 // a new stream; this time using the new (preferred) audio output device.
222 audio_device_buffer_->NativeAudioPlayoutInterrupted();
223 StopPlayout();
224 InitPlayout();
225 StartPlayout();
226}
227} // namespace webrtc