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::__anonfa9ba8300111::TinyalsaSink49 TinyalsaSink(unsigned pcmCard, unsigned pcmDevice,
50 const AudioConfig &cfg,
51 uint64_t initialFrames)
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(initialFrames)
57 , mFrames(initialFrames)
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::__anonfa9ba8300111::TinyalsaSink72 ~TinyalsaSink() {
73 mConsumeThreadRunning = false;
74 ALOGD("%s: joining consumeThread", __func__);
75 mConsumeThread.join();
76 if (mPcm) {
77 ALOGD("%s: stopping PCM stream", __func__);
78 LOG_ALWAYS_FATAL_IF(pcm_stop(mPcm.get()) != 0);
79 }
80 }
81
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink82 static int getLatencyMs(const AudioConfig &cfg) {
83 constexpr size_t inMs = 1000;
84 const talsa::PcmPeriodSettings periodSettings =
85 talsa::pcmGetPcmPeriodSettings();
86 const size_t numerator = periodSettings.periodSizeMultiplier * cfg.frameCount;
87 const size_t denominator = periodSettings.periodCount * cfg.base.sampleRateHz / inMs;
88
89 // integer division with rounding
90 return (numerator + (denominator >> 1)) / denominator + talsa::pcmGetHostLatencyMs();
91 }
92
getPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink93 Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
94 const AutoMutex lock(mFrameCountersMutex);
95
96 nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
97 const uint64_t nowFrames = getPresentationFramesLocked(nowNs);
98 auto presentedFrames = nowFrames - mMissedFrames;
99 if (presentedFrames > mReceivedFrames) {
100 // There is another underrun that is not yet accounted for in mMissedFrames
101 auto delta = presentedFrames - mReceivedFrames;
102 presentedFrames -= delta;
103 // The last frame was presented some time ago, reflect that in the result
104 nowNs -= delta * 1000000000 / mSampleRateHz;
105 }
106 mFrames = presentedFrames + mInitialFrames;
107
108 frames = mFrames;
109 ts = util::nsecs2TimeSpec(nowNs);
110 return Result::OK;
111 }
112
getPresentationFramesLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink113 uint64_t getPresentationFramesLocked(const nsecs_t nowNs) const {
114 return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000;
115 }
116
calcAvailableFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink117 size_t calcAvailableFramesNowLocked() {
118 const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
119 auto presentationFrames = getPresentationFramesLocked(nowNs);
120 if (mReceivedFrames + mMissedFrames < presentationFrames) {
121 // There has been an underrun
122 mMissedFrames = presentationFrames - mReceivedFrames;
123 }
124 size_t pendingFrames = mReceivedFrames + mMissedFrames - presentationFrames;
125 return mRingBuffer.capacity() / mFrameSize - pendingFrames;
126 }
127
calcWaitFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink128 size_t calcWaitFramesNowLocked(const size_t requestedFrames) {
129 const size_t availableFrames = calcAvailableFramesNowLocked();
130 return (requestedFrames > availableFrames)
131 ? (requestedFrames - availableFrames) : 0;
132 }
133
writeandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink134 size_t write(float volume, size_t bytesToWrite, IReader &reader) override {
135 const AutoMutex lock(mFrameCountersMutex);
136
137 size_t framesLost = 0;
138 const size_t waitFrames = calcWaitFramesNowLocked(bytesToWrite / mFrameSize);
139 const auto blockUntil =
140 std::chrono::high_resolution_clock::now() +
141 + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz);
142
143 while (bytesToWrite > 0) {
144 if (mRingBuffer.waitForProduceAvailable(blockUntil
145 + std::chrono::microseconds(kMaxJitterUs))) {
146 auto produceChunk = mRingBuffer.getProduceChunk();
147 if (produceChunk.size >= bytesToWrite) {
148 // Since the ring buffer has more bytes free than we need,
149 // make sure we are not too early here: tinyalsa is jittery,
150 // we don't want to go faster than SYSTEM_TIME_MONOTONIC
151 std::this_thread::sleep_until(blockUntil);
152 }
153
154 const size_t szFrames =
155 std::min(produceChunk.size, bytesToWrite) / mFrameSize;
156 const size_t szBytes = szFrames * mFrameSize;
157 LOG_ALWAYS_FATAL_IF(reader(produceChunk.data, szBytes) < szBytes);
158
159 aops::multiplyByVolume(volume,
160 static_cast<int16_t *>(produceChunk.data),
161 szBytes / sizeof(int16_t));
162
163 LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(szBytes) < szBytes);
164 mReceivedFrames += szFrames;
165 bytesToWrite -= szBytes;
166 } else {
167 ALOGV("TinyalsaSink::%s:%d pcm_writei was late reading "
168 "frames, dropping %zu us of audio",
169 __func__, __LINE__,
170 size_t(1000000 * bytesToWrite / mFrameSize / mSampleRateHz));
171
172 // drop old audio to make room for new
173 const size_t bytesLost = mRingBuffer.makeRoomForProduce(bytesToWrite);
174 framesLost += bytesLost / mFrameSize;
175
176 while (bytesToWrite > 0) {
177 auto produceChunk = mRingBuffer.getProduceChunk();
178 const size_t szFrames =
179 std::min(produceChunk.size, bytesToWrite) / mFrameSize;
180 const size_t szBytes = szFrames * mFrameSize;
181 LOG_ALWAYS_FATAL_IF(reader(produceChunk.data, szBytes) < szBytes);
182
183 aops::multiplyByVolume(volume,
184 static_cast<int16_t *>(produceChunk.data),
185 szBytes / sizeof(int16_t));
186
187 LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(szBytes) < szBytes);
188 mReceivedFrames += szFrames;
189 bytesToWrite -= szBytes;
190 }
191 break;
192 }
193 }
194
195 return framesLost;
196 }
197
consumeThreadandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink198 void consumeThread() {
199 util::setThreadPriority(SP_AUDIO_SYS, PRIORITY_AUDIO);
200 std::vector<uint8_t> writeBuffer(mWriteSizeFrames * mFrameSize);
201
202 while (mConsumeThreadRunning) {
203 if (mRingBuffer.waitForConsumeAvailable(
204 std::chrono::high_resolution_clock::now()
205 + std::chrono::microseconds(100000))) {
206 size_t szBytes;
207 {
208 auto chunk = mRingBuffer.getConsumeChunk();
209 szBytes = std::min(writeBuffer.size(), chunk.size);
210 // We have to memcpy because the consumer holds the lock
211 // into RingBuffer and pcm_write takes too long to hold
212 // this lock.
213 memcpy(writeBuffer.data(), chunk.data, szBytes);
214 LOG_ALWAYS_FATAL_IF(mRingBuffer.consume(chunk, szBytes) < szBytes);
215 }
216
217 const uint8_t *data8 = writeBuffer.data();
218 while (szBytes > 0) {
219 const int n = talsa::pcmWrite(mPcm.get(), data8, szBytes, mFrameSize);
220 if (n < 0) {
221 break;
222 }
223 LOG_ALWAYS_FATAL_IF(static_cast<size_t>(n) > szBytes,
224 "n=%d szBytes=%zu mFrameSize=%u",
225 n, szBytes, mFrameSize);
226 data8 += n;
227 szBytes -= n;
228 }
229 }
230 }
231 ALOGD("%s: exiting", __func__);
232 }
233
createandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::TinyalsaSink234 static std::unique_ptr<TinyalsaSink> create(unsigned pcmCard,
235 unsigned pcmDevice,
236 const AudioConfig &cfg,
237 size_t readerBufferSizeHint,
238 uint64_t initialFrames) {
239 (void)readerBufferSizeHint;
240 auto sink = std::make_unique<TinyalsaSink>(pcmCard, pcmDevice,
241 cfg, initialFrames);
242 if (sink->mMixer && sink->mPcm) {
243 return sink;
244 } else {
245 return FAILURE(nullptr);
246 }
247 }
248
249 private:
250 const nsecs_t mStartNs;
251 const unsigned mSampleRateHz;
252 const unsigned mFrameSize;
253 const unsigned mWriteSizeFrames;
254 const uint64_t mInitialFrames;
255 uint64_t mFrames GUARDED_BY(mFrameCountersMutex);
256 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
257 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
258 RingBuffer mRingBuffer;
259 talsa::Mixer mMixer;
260 talsa::PcmPtr mPcm;
261 std::thread mConsumeThread;
262 std::atomic<bool> mConsumeThreadRunning = true;
263 mutable Mutex mFrameCountersMutex;
264 };
265
266 struct NullSink : public DevicePortSink {
NullSinkandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink267 NullSink(const AudioConfig &cfg, uint64_t initialFrames)
268 : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC))
269 , mSampleRateHz(cfg.base.sampleRateHz)
270 , mFrameSize(util::countChannels(cfg.base.channelMask) * sizeof(int16_t))
271 , mInitialFrames(initialFrames)
272 , mFrames(initialFrames) {}
273
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink274 static int getLatencyMs(const AudioConfig &) {
275 return 1;
276 }
277
getPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink278 Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
279 const AutoMutex lock(mFrameCountersMutex);
280
281 nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
282 const uint64_t nowFrames = getPresentationFramesLocked(nowNs);
283 auto presentedFrames = nowFrames - mMissedFrames;
284 if (presentedFrames > mReceivedFrames) {
285 // There is another underrun that is not yet accounted for in mMissedFrames
286 auto delta = presentedFrames - mReceivedFrames;
287 presentedFrames -= delta;
288 // The last frame was presented some time ago, reflect that in the result
289 nowNs -= delta * 1000000000 / mSampleRateHz;
290 }
291 mFrames = presentedFrames + mInitialFrames;
292
293 frames = mFrames;
294 ts = util::nsecs2TimeSpec(nowNs);
295 return Result::OK;
296 }
297
getPresentationFramesLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink298 uint64_t getPresentationFramesLocked(const nsecs_t nowNs) const {
299 return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000;
300 }
301
calcAvailableFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink302 size_t calcAvailableFramesNowLocked() {
303 const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
304 auto presentationFrames = getPresentationFramesLocked(nowNs);
305 if (mReceivedFrames + mMissedFrames < presentationFrames) {
306 // There has been an underrun
307 mMissedFrames = presentationFrames - mReceivedFrames;
308 }
309 size_t pendingFrames = mReceivedFrames + mMissedFrames - presentationFrames;
310 return sizeof(mWriteBuffer) / mFrameSize - pendingFrames;
311 }
312
calcWaitFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink313 size_t calcWaitFramesNowLocked(const size_t requestedFrames) {
314 const size_t availableFrames = calcAvailableFramesNowLocked();
315 return (requestedFrames > availableFrames)
316 ? (requestedFrames - availableFrames) : 0;
317 }
318
writeandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink319 size_t write(float volume, size_t bytesToWrite, IReader &reader) override {
320 (void)volume;
321 const AutoMutex lock(mFrameCountersMutex);
322
323 const size_t waitFrames = calcWaitFramesNowLocked(bytesToWrite / mFrameSize);
324 const auto blockUntil =
325 std::chrono::high_resolution_clock::now() +
326 + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz);
327 std::this_thread::sleep_until(blockUntil);
328
329 while (bytesToWrite > 0) {
330 size_t chunkSize =
331 std::min(bytesToWrite, sizeof(mWriteBuffer)) / mFrameSize * mFrameSize;
332 chunkSize = reader(mWriteBuffer, chunkSize);
333 if (chunkSize > 0) {
334 mReceivedFrames += chunkSize / mFrameSize;
335 bytesToWrite -= chunkSize;
336 } else {
337 break; // reader failed
338 }
339 }
340
341 return 0;
342 }
343
createandroid::hardware::audio::CPP_VERSION::implementation::__anonfa9ba8300111::NullSink344 static std::unique_ptr<NullSink> create(const AudioConfig &cfg,
345 size_t readerBufferSizeHint,
346 uint64_t initialFrames) {
347 (void)readerBufferSizeHint;
348 return std::make_unique<NullSink>(cfg, initialFrames);
349 }
350
351 private:
352 const nsecs_t mStartNs;
353 const unsigned mSampleRateHz;
354 const unsigned mFrameSize;
355 const uint64_t mInitialFrames;
356 uint64_t mFrames GUARDED_BY(mFrameCountersMutex);
357 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
358 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
359 char mWriteBuffer[1024];
360 mutable Mutex mFrameCountersMutex;
361 };
362
363 } // namespace
364
365 std::unique_ptr<DevicePortSink>
create(size_t readerBufferSizeHint,const DeviceAddress & address,const AudioConfig & cfg,const hidl_vec<AudioInOutFlag> & flags,uint64_t initialFrames)366 DevicePortSink::create(size_t readerBufferSizeHint,
367 const DeviceAddress &address,
368 const AudioConfig &cfg,
369 const hidl_vec<AudioInOutFlag> &flags,
370 uint64_t initialFrames) {
371 (void)flags;
372
373 if (xsd::stringToAudioFormat(cfg.base.format) != xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT) {
374 ALOGE("%s:%d, unexpected format: '%s'", __func__, __LINE__, cfg.base.format.c_str());
375 return FAILURE(nullptr);
376 }
377
378 if (GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false)) {
379 goto nullsink;
380 }
381
382 switch (xsd::stringToAudioDevice(address.deviceType)) {
383 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
384 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
385 {
386 auto sinkptr = TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice,
387 cfg, readerBufferSizeHint, initialFrames);
388 if (sinkptr != nullptr) {
389 return sinkptr;
390 } else {
391 ALOGW("%s:%d failed to create alsa sink for '%s'; creating NullSink instead.",
392 __func__, __LINE__, address.deviceType.c_str());
393 }
394 }
395 break;
396
397 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
398 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
399 ALOGW("%s:%d creating NullSink for '%s'.", __func__, __LINE__, address.deviceType.c_str());
400 break;
401
402 default:
403 ALOGW("%s:%d unsupported device: '%s', creating NullSink", __func__, __LINE__, address.deviceType.c_str());
404 break;
405 }
406
407 nullsink:
408 return NullSink::create(cfg, readerBufferSizeHint, initialFrames);
409 }
410
getLatencyMs(const DeviceAddress & address,const AudioConfig & cfg)411 int DevicePortSink::getLatencyMs(const DeviceAddress &address, const AudioConfig &cfg) {
412 switch (xsd::stringToAudioDevice(address.deviceType)) {
413 default:
414 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
415 return FAILURE(-1);
416
417 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
418 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
419 return TinyalsaSink::getLatencyMs(cfg);
420
421 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
422 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
423 return NullSink::getLatencyMs(cfg);
424 }
425 }
426
validateDeviceAddress(const DeviceAddress & address)427 bool DevicePortSink::validateDeviceAddress(const DeviceAddress& address) {
428 switch (xsd::stringToAudioDevice(address.deviceType)) {
429 default:
430 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
431 return FAILURE(false);
432
433 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
434 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
435 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
436 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
437 break;
438 }
439
440 return true;
441 }
442
443 } // namespace implementation
444 } // namespace CPP_VERSION
445 } // namespace audio
446 } // namespace hardware
447 } // namespace android
448