1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include PATH(APM_XSD_ENUMS_H_FILENAME)
18 #include <android-base/properties.h>
19 #include <chrono>
20 #include <thread>
21 #include <log/log.h>
22 #include <utils/Mutex.h>
23 #include <utils/Timers.h>
24 #include <utils/ThreadDefs.h>
25 #include "device_port_sink.h"
26 #include "talsa.h"
27 #include "audio_ops.h"
28 #include "ring_buffer.h"
29 #include "util.h"
30 #include "debug.h"
31
32 using ::android::base::GetBoolProperty;
33
34 namespace xsd {
35 using namespace ::android::audio::policy::configuration::CPP_VERSION;
36 }
37
38 namespace android {
39 namespace hardware {
40 namespace audio {
41 namespace CPP_VERSION {
42 namespace implementation {
43
44 namespace {
45
46 constexpr int kMaxJitterUs = 3000; // Enforced by CTS, should be <= 6ms
47
48 struct TinyalsaSink : public DevicePortSink {
TinyalsaSinkandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink49 TinyalsaSink(unsigned pcmCard, unsigned pcmDevice,
50 const AudioConfig &cfg,
51 uint64_t &frames)
52 : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC))
53 , mSampleRateHz(cfg.base.sampleRateHz)
54 , mFrameSize(util::countChannels(cfg.base.channelMask) * sizeof(int16_t))
55 , mWriteSizeFrames(cfg.frameCount)
56 , mInitialFrames(frames)
57 , mFrames(frames)
58 , mRingBuffer(mFrameSize * cfg.frameCount * 3)
59 , mMixer(pcmCard)
60 , mPcm(talsa::pcmOpen(pcmCard, pcmDevice,
61 util::countChannels(cfg.base.channelMask),
62 cfg.base.sampleRateHz,
63 cfg.frameCount,
64 true /* isOut */)) {
65 if (mPcm) {
66 mConsumeThread = std::thread(&TinyalsaSink::consumeThread, this);
67 } else {
68 mConsumeThread = std::thread([](){});
69 }
70 }
71
~TinyalsaSinkandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink72 ~TinyalsaSink() {
73 mConsumeThreadRunning = false;
74 mConsumeThread.join();
75 }
76
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink77 static int getLatencyMs(const AudioConfig &cfg) {
78 constexpr size_t inMs = 1000;
79 const talsa::PcmPeriodSettings periodSettings =
80 talsa::pcmGetPcmPeriodSettings();
81 const size_t numerator = periodSettings.periodSizeMultiplier * cfg.frameCount;
82 const size_t denominator = periodSettings.periodCount * cfg.base.sampleRateHz / inMs;
83
84 // integer division with rounding
85 return (numerator + (denominator >> 1)) / denominator + talsa::pcmGetHostLatencyMs();
86 }
87
getPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink88 Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
89 const AutoMutex lock(mFrameCountersMutex);
90
91 nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
92 const uint64_t nowFrames = getPresentationFramesLocked(nowNs);
93 auto presentedFrames = nowFrames - mMissedFrames;
94 if (presentedFrames > mReceivedFrames) {
95 // There is another underrun that is not yet accounted for in mMissedFrames
96 auto delta = presentedFrames - mReceivedFrames;
97 presentedFrames -= delta;
98 // The last frame was presented some time ago, reflect that in the result
99 nowNs -= delta * 1000000000 / mSampleRateHz;
100 }
101 mFrames = presentedFrames + mInitialFrames;
102
103 frames = mFrames;
104 ts = util::nsecs2TimeSpec(nowNs);
105 return Result::OK;
106 }
107
getPresentationFramesLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink108 uint64_t getPresentationFramesLocked(const nsecs_t nowNs) const {
109 return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000;
110 }
111
calcAvailableFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink112 size_t calcAvailableFramesNowLocked() {
113 const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
114 auto presentationFrames = getPresentationFramesLocked(nowNs);
115 if (mReceivedFrames + mMissedFrames < presentationFrames) {
116 // There has been an underrun
117 mMissedFrames = presentationFrames - mReceivedFrames;
118 }
119 size_t pendingFrames = mReceivedFrames + mMissedFrames - presentationFrames;
120 return mRingBuffer.capacity() / mFrameSize - pendingFrames;
121 }
122
calcWaitFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink123 size_t calcWaitFramesNowLocked(const size_t requestedFrames) {
124 const size_t availableFrames = calcAvailableFramesNowLocked();
125 return (requestedFrames > availableFrames)
126 ? (requestedFrames - availableFrames) : 0;
127 }
128
writeandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink129 size_t write(float volume, size_t bytesToWrite, IReader &reader) {
130 const AutoMutex lock(mFrameCountersMutex);
131
132 size_t framesLost = 0;
133 const size_t waitFrames = calcWaitFramesNowLocked(bytesToWrite / mFrameSize);
134 const auto blockUntil =
135 std::chrono::high_resolution_clock::now() +
136 + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz);
137
138 while (bytesToWrite > 0) {
139 if (mRingBuffer.waitForProduceAvailable(blockUntil
140 + std::chrono::microseconds(kMaxJitterUs))) {
141 auto produceChunk = mRingBuffer.getProduceChunk();
142 if (produceChunk.size >= bytesToWrite) {
143 // Since the ring buffer has more bytes free than we need,
144 // make sure we are not too early here: tinyalsa is jittery,
145 // we don't want to go faster than SYSTEM_TIME_MONOTONIC
146 std::this_thread::sleep_until(blockUntil);
147 }
148
149 const size_t szFrames =
150 std::min(produceChunk.size, bytesToWrite) / mFrameSize;
151 const size_t szBytes = szFrames * mFrameSize;
152 LOG_ALWAYS_FATAL_IF(reader(produceChunk.data, szBytes) < szBytes);
153
154 aops::multiplyByVolume(volume,
155 static_cast<int16_t *>(produceChunk.data),
156 szBytes / sizeof(int16_t));
157
158 LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(szBytes) < szBytes);
159 mReceivedFrames += szFrames;
160 bytesToWrite -= szBytes;
161 } else {
162 ALOGV("TinyalsaSink::%s:%d pcm_write was late reading "
163 "frames, dropping %zu us of audio",
164 __func__, __LINE__,
165 size_t(1000000 * bytesToWrite / mFrameSize / mSampleRateHz));
166
167 // drop old audio to make room for new
168 const size_t bytesLost = mRingBuffer.makeRoomForProduce(bytesToWrite);
169 framesLost += bytesLost / mFrameSize;
170
171 while (bytesToWrite > 0) {
172 auto produceChunk = mRingBuffer.getProduceChunk();
173 const size_t szFrames =
174 std::min(produceChunk.size, bytesToWrite) / mFrameSize;
175 const size_t szBytes = szFrames * mFrameSize;
176 LOG_ALWAYS_FATAL_IF(reader(produceChunk.data, szBytes) < szBytes);
177
178 aops::multiplyByVolume(volume,
179 static_cast<int16_t *>(produceChunk.data),
180 szBytes / sizeof(int16_t));
181
182 LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(szBytes) < szBytes);
183 mReceivedFrames += szFrames;
184 bytesToWrite -= szBytes;
185 }
186 break;
187 }
188 }
189
190 return framesLost;
191 }
192
consumeThreadandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink193 void consumeThread() {
194 util::setThreadPriority(PRIORITY_URGENT_AUDIO);
195 std::vector<uint8_t> writeBuffer(mWriteSizeFrames * mFrameSize);
196
197 while (mConsumeThreadRunning) {
198 if (mRingBuffer.waitForConsumeAvailable(
199 std::chrono::high_resolution_clock::now()
200 + std::chrono::microseconds(100000))) {
201 size_t szBytes;
202 {
203 auto chunk = mRingBuffer.getConsumeChunk();
204 szBytes = std::min(writeBuffer.size(), chunk.size);
205 // We have to memcpy because the consumer holds the lock
206 // into RingBuffer and pcm_write takes too long to hold
207 // this lock.
208 memcpy(writeBuffer.data(), chunk.data, szBytes);
209 LOG_ALWAYS_FATAL_IF(mRingBuffer.consume(chunk, szBytes) < szBytes);
210 }
211
212 talsa::pcmWrite(mPcm.get(), writeBuffer.data(), szBytes);
213 }
214 }
215 }
216
createandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::TinyalsaSink217 static std::unique_ptr<TinyalsaSink> create(unsigned pcmCard,
218 unsigned pcmDevice,
219 const AudioConfig &cfg,
220 size_t readerBufferSizeHint,
221 uint64_t &frames) {
222 (void)readerBufferSizeHint;
223 auto sink = std::make_unique<TinyalsaSink>(pcmCard, pcmDevice,
224 cfg, frames);
225 if (sink->mMixer && sink->mPcm) {
226 return sink;
227 } else {
228 return FAILURE(nullptr);
229 }
230 }
231
232 private:
233 const nsecs_t mStartNs;
234 const unsigned mSampleRateHz;
235 const unsigned mFrameSize;
236 const unsigned mWriteSizeFrames;
237 const uint64_t mInitialFrames;
238 uint64_t &mFrames GUARDED_BY(mFrameCountersMutex);
239 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
240 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
241 RingBuffer mRingBuffer;
242 talsa::Mixer mMixer;
243 talsa::PcmPtr mPcm;
244 std::thread mConsumeThread;
245 std::atomic<bool> mConsumeThreadRunning = true;
246 mutable Mutex mFrameCountersMutex;
247 };
248
249 struct NullSink : public DevicePortSink {
NullSinkandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink250 NullSink(const AudioConfig &cfg, uint64_t &frames)
251 : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC))
252 , mSampleRateHz(cfg.base.sampleRateHz)
253 , mFrameSize(util::countChannels(cfg.base.channelMask) * sizeof(int16_t))
254 , mInitialFrames(frames)
255 , mFrames(frames) {}
256
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink257 static int getLatencyMs(const AudioConfig &) {
258 return 1;
259 }
260
getPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink261 Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
262 const AutoMutex lock(mFrameCountersMutex);
263
264 nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
265 const uint64_t nowFrames = getPresentationFramesLocked(nowNs);
266 auto presentedFrames = nowFrames - mMissedFrames;
267 if (presentedFrames > mReceivedFrames) {
268 // There is another underrun that is not yet accounted for in mMissedFrames
269 auto delta = presentedFrames - mReceivedFrames;
270 presentedFrames -= delta;
271 // The last frame was presented some time ago, reflect that in the result
272 nowNs -= delta * 1000000000 / mSampleRateHz;
273 }
274 mFrames = presentedFrames + mInitialFrames;
275
276 frames = mFrames;
277 ts = util::nsecs2TimeSpec(nowNs);
278 return Result::OK;
279 }
280
getPresentationFramesLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink281 uint64_t getPresentationFramesLocked(const nsecs_t nowNs) const {
282 return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000;
283 }
284
calcAvailableFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink285 size_t calcAvailableFramesNowLocked() {
286 const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
287 auto presentationFrames = getPresentationFramesLocked(nowNs);
288 if (mReceivedFrames + mMissedFrames < presentationFrames) {
289 // There has been an underrun
290 mMissedFrames = presentationFrames - mReceivedFrames;
291 }
292 size_t pendingFrames = mReceivedFrames + mMissedFrames - presentationFrames;
293 return sizeof(mWriteBuffer) / mFrameSize - pendingFrames;
294 }
295
calcWaitFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink296 size_t calcWaitFramesNowLocked(const size_t requestedFrames) {
297 const size_t availableFrames = calcAvailableFramesNowLocked();
298 return (requestedFrames > availableFrames)
299 ? (requestedFrames - availableFrames) : 0;
300 }
301
writeandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink302 size_t write(float volume, size_t bytesToWrite, IReader &reader) override {
303 (void)volume;
304 const AutoMutex lock(mFrameCountersMutex);
305
306 const size_t waitFrames = calcWaitFramesNowLocked(bytesToWrite / mFrameSize);
307 const auto blockUntil =
308 std::chrono::high_resolution_clock::now() +
309 + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz);
310 std::this_thread::sleep_until(blockUntil);
311
312 while (bytesToWrite > 0) {
313 size_t chunkSize =
314 std::min(bytesToWrite, sizeof(mWriteBuffer)) / mFrameSize * mFrameSize;
315 chunkSize = reader(mWriteBuffer, chunkSize);
316 if (chunkSize > 0) {
317 mReceivedFrames += chunkSize / mFrameSize;
318 bytesToWrite -= chunkSize;
319 } else {
320 break; // reader failed
321 }
322 }
323
324 return 0;
325 }
326
createandroid::hardware::audio::CPP_VERSION::implementation::__anon36323d6f0111::NullSink327 static std::unique_ptr<NullSink> create(const AudioConfig &cfg,
328 size_t readerBufferSizeHint,
329 uint64_t &frames) {
330 (void)readerBufferSizeHint;
331 return std::make_unique<NullSink>(cfg, frames);
332 }
333
334 private:
335 const nsecs_t mStartNs;
336 const unsigned mSampleRateHz;
337 const unsigned mFrameSize;
338 const uint64_t mInitialFrames;
339 uint64_t &mFrames GUARDED_BY(mFrameCountersMutex);
340 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
341 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
342 char mWriteBuffer[1024];
343 mutable Mutex mFrameCountersMutex;
344 };
345
346 } // namespace
347
348 std::unique_ptr<DevicePortSink>
create(size_t readerBufferSizeHint,const DeviceAddress & address,const AudioConfig & cfg,const hidl_vec<AudioInOutFlag> & flags,uint64_t & frames)349 DevicePortSink::create(size_t readerBufferSizeHint,
350 const DeviceAddress &address,
351 const AudioConfig &cfg,
352 const hidl_vec<AudioInOutFlag> &flags,
353 uint64_t &frames) {
354 (void)flags;
355
356 if (xsd::stringToAudioFormat(cfg.base.format) != xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT) {
357 ALOGE("%s:%d, unexpected format: '%s'", __func__, __LINE__, cfg.base.format.c_str());
358 return FAILURE(nullptr);
359 }
360
361 if (GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false)) {
362 goto nullsink;
363 }
364
365 switch (xsd::stringToAudioDevice(address.deviceType)) {
366 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
367 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
368 {
369 auto sinkptr = TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice,
370 cfg, readerBufferSizeHint, frames);
371 if (sinkptr != nullptr) {
372 return sinkptr;
373 } else {
374 ALOGW("%s:%d failed to create alsa sink for '%s'; creating NullSink instead.",
375 __func__, __LINE__, address.deviceType.c_str());
376 }
377 }
378 break;
379
380 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
381 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
382 ALOGW("%s:%d creating NullSink for '%s'.", __func__, __LINE__, address.deviceType.c_str());
383 break;
384
385 default:
386 ALOGW("%s:%d unsupported device: '%s', creating NullSink", __func__, __LINE__, address.deviceType.c_str());
387 break;
388 }
389
390 nullsink:
391 return NullSink::create(cfg, readerBufferSizeHint, frames);
392 }
393
getLatencyMs(const DeviceAddress & address,const AudioConfig & cfg)394 int DevicePortSink::getLatencyMs(const DeviceAddress &address, const AudioConfig &cfg) {
395 switch (xsd::stringToAudioDevice(address.deviceType)) {
396 default:
397 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
398 return FAILURE(-1);
399
400 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
401 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
402 return TinyalsaSink::getLatencyMs(cfg);
403
404 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
405 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
406 return NullSink::getLatencyMs(cfg);
407 }
408 }
409
validateDeviceAddress(const DeviceAddress & address)410 bool DevicePortSink::validateDeviceAddress(const DeviceAddress& address) {
411 switch (xsd::stringToAudioDevice(address.deviceType)) {
412 default:
413 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
414 return FAILURE(false);
415
416 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
417 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
418 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
419 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
420 break;
421 }
422
423 return true;
424 }
425
426 } // namespace implementation
427 } // namespace CPP_VERSION
428 } // namespace audio
429 } // namespace hardware
430 } // namespace android
431