• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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