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::__anonb8135f430111::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::__anonb8135f430111::TinyalsaSink72 ~TinyalsaSink() {
73 mConsumeThreadRunning = false;
74 mConsumeThread.join();
75 }
76
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::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::__anonb8135f430111::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::__anonb8135f430111::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::__anonb8135f430111::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::__anonb8135f430111::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::__anonb8135f430111::TinyalsaSink129 size_t write(float volume, size_t bytesToWrite, IReader &reader) override {
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_writei 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::__anonb8135f430111::TinyalsaSink193 void consumeThread() {
194 util::setThreadPriority(SP_AUDIO_SYS, PRIORITY_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 const uint8_t *data8 = writeBuffer.data();
213 while (szBytes > 0) {
214 const int n = talsa::pcmWrite(mPcm.get(), data8, szBytes, mFrameSize);
215 if (n < 0) {
216 break;
217 }
218 LOG_ALWAYS_FATAL_IF(static_cast<size_t>(n) > szBytes,
219 "n=%d szBytes=%zu mFrameSize=%u",
220 n, szBytes, mFrameSize);
221 data8 += n;
222 szBytes -= n;
223 }
224 }
225 }
226 }
227
createandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::TinyalsaSink228 static std::unique_ptr<TinyalsaSink> create(unsigned pcmCard,
229 unsigned pcmDevice,
230 const AudioConfig &cfg,
231 size_t readerBufferSizeHint,
232 uint64_t initialFrames) {
233 (void)readerBufferSizeHint;
234 auto sink = std::make_unique<TinyalsaSink>(pcmCard, pcmDevice,
235 cfg, initialFrames);
236 if (sink->mMixer && sink->mPcm) {
237 return sink;
238 } else {
239 return FAILURE(nullptr);
240 }
241 }
242
243 private:
244 const nsecs_t mStartNs;
245 const unsigned mSampleRateHz;
246 const unsigned mFrameSize;
247 const unsigned mWriteSizeFrames;
248 const uint64_t mInitialFrames;
249 uint64_t mFrames GUARDED_BY(mFrameCountersMutex);
250 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
251 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
252 RingBuffer mRingBuffer;
253 talsa::Mixer mMixer;
254 talsa::PcmPtr mPcm;
255 std::thread mConsumeThread;
256 std::atomic<bool> mConsumeThreadRunning = true;
257 mutable Mutex mFrameCountersMutex;
258 };
259
260 struct NullSink : public DevicePortSink {
NullSinkandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink261 NullSink(const AudioConfig &cfg, uint64_t initialFrames)
262 : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC))
263 , mSampleRateHz(cfg.base.sampleRateHz)
264 , mFrameSize(util::countChannels(cfg.base.channelMask) * sizeof(int16_t))
265 , mInitialFrames(initialFrames)
266 , mFrames(initialFrames) {}
267
getLatencyMsandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink268 static int getLatencyMs(const AudioConfig &) {
269 return 1;
270 }
271
getPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink272 Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
273 const AutoMutex lock(mFrameCountersMutex);
274
275 nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
276 const uint64_t nowFrames = getPresentationFramesLocked(nowNs);
277 auto presentedFrames = nowFrames - mMissedFrames;
278 if (presentedFrames > mReceivedFrames) {
279 // There is another underrun that is not yet accounted for in mMissedFrames
280 auto delta = presentedFrames - mReceivedFrames;
281 presentedFrames -= delta;
282 // The last frame was presented some time ago, reflect that in the result
283 nowNs -= delta * 1000000000 / mSampleRateHz;
284 }
285 mFrames = presentedFrames + mInitialFrames;
286
287 frames = mFrames;
288 ts = util::nsecs2TimeSpec(nowNs);
289 return Result::OK;
290 }
291
getPresentationFramesLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink292 uint64_t getPresentationFramesLocked(const nsecs_t nowNs) const {
293 return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000;
294 }
295
calcAvailableFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink296 size_t calcAvailableFramesNowLocked() {
297 const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
298 auto presentationFrames = getPresentationFramesLocked(nowNs);
299 if (mReceivedFrames + mMissedFrames < presentationFrames) {
300 // There has been an underrun
301 mMissedFrames = presentationFrames - mReceivedFrames;
302 }
303 size_t pendingFrames = mReceivedFrames + mMissedFrames - presentationFrames;
304 return sizeof(mWriteBuffer) / mFrameSize - pendingFrames;
305 }
306
calcWaitFramesNowLockedandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink307 size_t calcWaitFramesNowLocked(const size_t requestedFrames) {
308 const size_t availableFrames = calcAvailableFramesNowLocked();
309 return (requestedFrames > availableFrames)
310 ? (requestedFrames - availableFrames) : 0;
311 }
312
writeandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink313 size_t write(float volume, size_t bytesToWrite, IReader &reader) override {
314 (void)volume;
315 const AutoMutex lock(mFrameCountersMutex);
316
317 const size_t waitFrames = calcWaitFramesNowLocked(bytesToWrite / mFrameSize);
318 const auto blockUntil =
319 std::chrono::high_resolution_clock::now() +
320 + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz);
321 std::this_thread::sleep_until(blockUntil);
322
323 while (bytesToWrite > 0) {
324 size_t chunkSize =
325 std::min(bytesToWrite, sizeof(mWriteBuffer)) / mFrameSize * mFrameSize;
326 chunkSize = reader(mWriteBuffer, chunkSize);
327 if (chunkSize > 0) {
328 mReceivedFrames += chunkSize / mFrameSize;
329 bytesToWrite -= chunkSize;
330 } else {
331 break; // reader failed
332 }
333 }
334
335 return 0;
336 }
337
createandroid::hardware::audio::CPP_VERSION::implementation::__anonb8135f430111::NullSink338 static std::unique_ptr<NullSink> create(const AudioConfig &cfg,
339 size_t readerBufferSizeHint,
340 uint64_t initialFrames) {
341 (void)readerBufferSizeHint;
342 return std::make_unique<NullSink>(cfg, initialFrames);
343 }
344
345 private:
346 const nsecs_t mStartNs;
347 const unsigned mSampleRateHz;
348 const unsigned mFrameSize;
349 const uint64_t mInitialFrames;
350 uint64_t mFrames GUARDED_BY(mFrameCountersMutex);
351 uint64_t mMissedFrames GUARDED_BY(mFrameCountersMutex) = 0;
352 uint64_t mReceivedFrames GUARDED_BY(mFrameCountersMutex) = 0;
353 char mWriteBuffer[1024];
354 mutable Mutex mFrameCountersMutex;
355 };
356
357 } // namespace
358
359 std::unique_ptr<DevicePortSink>
create(size_t readerBufferSizeHint,const DeviceAddress & address,const AudioConfig & cfg,const hidl_vec<AudioInOutFlag> & flags,uint64_t initialFrames)360 DevicePortSink::create(size_t readerBufferSizeHint,
361 const DeviceAddress &address,
362 const AudioConfig &cfg,
363 const hidl_vec<AudioInOutFlag> &flags,
364 uint64_t initialFrames) {
365 (void)flags;
366
367 if (xsd::stringToAudioFormat(cfg.base.format) != xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT) {
368 ALOGE("%s:%d, unexpected format: '%s'", __func__, __LINE__, cfg.base.format.c_str());
369 return FAILURE(nullptr);
370 }
371
372 if (GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false)) {
373 goto nullsink;
374 }
375
376 switch (xsd::stringToAudioDevice(address.deviceType)) {
377 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
378 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
379 {
380 auto sinkptr = TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice,
381 cfg, readerBufferSizeHint, initialFrames);
382 if (sinkptr != nullptr) {
383 return sinkptr;
384 } else {
385 ALOGW("%s:%d failed to create alsa sink for '%s'; creating NullSink instead.",
386 __func__, __LINE__, address.deviceType.c_str());
387 }
388 }
389 break;
390
391 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
392 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
393 ALOGW("%s:%d creating NullSink for '%s'.", __func__, __LINE__, address.deviceType.c_str());
394 break;
395
396 default:
397 ALOGW("%s:%d unsupported device: '%s', creating NullSink", __func__, __LINE__, address.deviceType.c_str());
398 break;
399 }
400
401 nullsink:
402 return NullSink::create(cfg, readerBufferSizeHint, initialFrames);
403 }
404
getLatencyMs(const DeviceAddress & address,const AudioConfig & cfg)405 int DevicePortSink::getLatencyMs(const DeviceAddress &address, const AudioConfig &cfg) {
406 switch (xsd::stringToAudioDevice(address.deviceType)) {
407 default:
408 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
409 return FAILURE(-1);
410
411 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
412 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
413 return TinyalsaSink::getLatencyMs(cfg);
414
415 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
416 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
417 return NullSink::getLatencyMs(cfg);
418 }
419 }
420
validateDeviceAddress(const DeviceAddress & address)421 bool DevicePortSink::validateDeviceAddress(const DeviceAddress& address) {
422 switch (xsd::stringToAudioDevice(address.deviceType)) {
423 default:
424 ALOGW("%s:%d unsupported device: '%s'", __func__, __LINE__, address.deviceType.c_str());
425 return FAILURE(false);
426
427 case xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
428 case xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
429 case xsd::AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
430 case xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS:
431 break;
432 }
433
434 return true;
435 }
436
437 } // namespace implementation
438 } // namespace CPP_VERSION
439 } // namespace audio
440 } // namespace hardware
441 } // namespace android
442