• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <cmath>
18 #include <limits>
19 
20 #define LOG_TAG "AHAL_StreamAlsa"
21 #include <android-base/logging.h>
22 
23 #include <Utils.h>
24 #include <audio_utils/clock.h>
25 #include <error/expected_utils.h>
26 #include <media/AidlConversionCppNdk.h>
27 
28 #include "core-impl/StreamAlsa.h"
29 
30 using aidl::android::hardware::audio::common::getChannelCount;
31 
32 namespace aidl::android::hardware::audio::core {
33 
StreamAlsa(StreamContext * context,const Metadata & metadata,int readWriteRetries)34 StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int readWriteRetries)
35     : StreamCommonImpl(context, metadata),
36       mBufferSizeFrames(getContext().getBufferSizeInFrames()),
37       mFrameSizeBytes(getContext().getFrameSize()),
38       mSampleRate(getContext().getSampleRate()),
39       mIsInput(isInput(metadata)),
40       mConfig(alsa::getPcmConfig(getContext(), mIsInput)),
41       mReadWriteRetries(readWriteRetries) {}
42 
~StreamAlsa()43 StreamAlsa::~StreamAlsa() {
44     cleanupWorker();
45 }
46 
getPipeFormat() const47 ::android::NBAIO_Format StreamAlsa::getPipeFormat() const {
48     const audio_format_t audioFormat = VALUE_OR_FATAL(
49             aidl2legacy_AudioFormatDescription_audio_format_t(getContext().getFormat()));
50     const int channelCount = getChannelCount(getContext().getChannelLayout());
51     return ::android::Format_from_SR_C(getContext().getSampleRate(), channelCount, audioFormat);
52 }
53 
makeSink(bool writeCanBlock)54 ::android::sp<::android::MonoPipe> StreamAlsa::makeSink(bool writeCanBlock) {
55     const ::android::NBAIO_Format format = getPipeFormat();
56     auto sink = ::android::sp<::android::MonoPipe>::make(mBufferSizeFrames, format, writeCanBlock);
57     const ::android::NBAIO_Format offers[1] = {format};
58     size_t numCounterOffers = 0;
59     ssize_t index = sink->negotiate(offers, 1, nullptr, numCounterOffers);
60     LOG_IF(FATAL, index != 0) << __func__ << ": Negotiation for the sink failed, index = " << index;
61     return sink;
62 }
63 
makeSource(::android::MonoPipe * pipe)64 ::android::sp<::android::MonoPipeReader> StreamAlsa::makeSource(::android::MonoPipe* pipe) {
65     const ::android::NBAIO_Format format = getPipeFormat();
66     const ::android::NBAIO_Format offers[1] = {format};
67     auto source = ::android::sp<::android::MonoPipeReader>::make(pipe);
68     size_t numCounterOffers = 0;
69     ssize_t index = source->negotiate(offers, 1, nullptr, numCounterOffers);
70     LOG_IF(FATAL, index != 0) << __func__
71                               << ": Negotiation for the source failed, index = " << index;
72     return source;
73 }
74 
init(DriverCallbackInterface *)75 ::android::status_t StreamAlsa::init(DriverCallbackInterface* /*callback*/) {
76     return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
77 }
78 
drain(StreamDescriptor::DrainMode)79 ::android::status_t StreamAlsa::drain(StreamDescriptor::DrainMode) {
80     if (!mIsInput) {
81         static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
82         const size_t delayUs = static_cast<size_t>(
83                 std::roundf(mBufferSizeFrames * kMicrosPerSecond / mSampleRate));
84         usleep(delayUs);
85     }
86     return ::android::OK;
87 }
88 
flush()89 ::android::status_t StreamAlsa::flush() {
90     return ::android::OK;
91 }
92 
pause()93 ::android::status_t StreamAlsa::pause() {
94     return ::android::OK;
95 }
96 
standby()97 ::android::status_t StreamAlsa::standby() {
98     teardownIo();
99     return ::android::OK;
100 }
101 
start()102 ::android::status_t StreamAlsa::start() {
103     if (!mAlsaDeviceProxies.empty()) {
104         // This is a resume after a pause.
105         return ::android::OK;
106     }
107     decltype(mAlsaDeviceProxies) alsaDeviceProxies;
108     decltype(mSources) sources;
109     decltype(mSinks) sinks;
110     for (const auto& device : getDeviceProfiles()) {
111         if ((device.direction == PCM_OUT && mIsInput) ||
112             (device.direction == PCM_IN && !mIsInput)) {
113             continue;
114         }
115         alsa::DeviceProxy proxy;
116         if (device.isExternal) {
117             // Always ask alsa configure as required since the configuration should be supported
118             // by the connected device. That is guaranteed by `setAudioPortConfig` and
119             // `setAudioPatch`.
120             proxy = alsa::openProxyForExternalDevice(
121                     device, const_cast<struct pcm_config*>(&mConfig.value()),
122                     true /*require_exact_match*/);
123         } else {
124             proxy = alsa::openProxyForAttachedDevice(
125                     device, const_cast<struct pcm_config*>(&mConfig.value()), mBufferSizeFrames);
126         }
127         if (proxy.get() == nullptr) {
128             return ::android::NO_INIT;
129         }
130         alsaDeviceProxies.push_back(std::move(proxy));
131         auto sink = makeSink(mIsInput);  // Do not block the writer when it is on our thread.
132         if (sink != nullptr) {
133             sinks.push_back(sink);
134         } else {
135             return ::android::NO_INIT;
136         }
137         if (auto source = makeSource(sink.get()); source != nullptr) {
138             sources.push_back(source);
139         } else {
140             return ::android::NO_INIT;
141         }
142     }
143     if (alsaDeviceProxies.empty()) {
144         return ::android::NO_INIT;
145     }
146     mAlsaDeviceProxies = std::move(alsaDeviceProxies);
147     mSources = std::move(sources);
148     mSinks = std::move(sinks);
149     mIoThreadIsRunning = true;
150     for (size_t i = 0; i < mAlsaDeviceProxies.size(); ++i) {
151         mIoThreads.emplace_back(mIsInput ? &StreamAlsa::inputIoThread : &StreamAlsa::outputIoThread,
152                                 this, i);
153     }
154     return ::android::OK;
155 }
156 
transfer(void * buffer,size_t frameCount,size_t * actualFrameCount,int32_t * latencyMs)157 ::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
158                                          int32_t* latencyMs) {
159     if (mAlsaDeviceProxies.empty()) {
160         LOG(FATAL) << __func__ << ": no opened devices";
161         return ::android::NO_INIT;
162     }
163     const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
164     unsigned maxLatency = 0;
165     if (mIsInput) {
166         const size_t i = 0;  // For the input case, only support a single device.
167         LOG(VERBOSE) << __func__ << ": reading from sink " << i;
168         ssize_t framesRead = mSources[i]->read(buffer, frameCount);
169         LOG_IF(FATAL, framesRead < 0) << "Error reading from the pipe: " << framesRead;
170         if (ssize_t framesMissing = static_cast<ssize_t>(frameCount) - framesRead;
171             framesMissing > 0) {
172             LOG(WARNING) << __func__ << ": incomplete data received, inserting " << framesMissing
173                          << " frames of silence";
174             memset(static_cast<char*>(buffer) + framesRead * mFrameSizeBytes, 0,
175                    framesMissing * mFrameSizeBytes);
176         }
177         maxLatency = proxy_get_latency(mAlsaDeviceProxies[i].get());
178     } else {
179         alsa::applyGain(buffer, mGain, bytesToTransfer, mConfig.value().format, mConfig->channels);
180         for (size_t i = 0; i < mAlsaDeviceProxies.size(); ++i) {
181             LOG(VERBOSE) << __func__ << ": writing into sink " << i;
182             ssize_t framesWritten = mSinks[i]->write(buffer, frameCount);
183             LOG_IF(FATAL, framesWritten < 0) << "Error writing into the pipe: " << framesWritten;
184             if (ssize_t framesLost = static_cast<ssize_t>(frameCount) - framesWritten;
185                 framesLost > 0) {
186                 LOG(WARNING) << __func__ << ": sink " << i << " incomplete data sent, dropping "
187                              << framesLost << " frames";
188             }
189             maxLatency = std::max(maxLatency, proxy_get_latency(mAlsaDeviceProxies[i].get()));
190         }
191     }
192     *actualFrameCount = frameCount;
193     maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
194     *latencyMs = maxLatency;
195     return ::android::OK;
196 }
197 
refinePosition(StreamDescriptor::Position * position)198 ::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
199     if (mAlsaDeviceProxies.empty()) {
200         LOG(WARNING) << __func__ << ": no opened devices";
201         return ::android::NO_INIT;
202     }
203     // Since the proxy can only count transferred frames since its creation,
204     // we override its counter value with ours and let it to correct for buffered frames.
205     alsa::resetTransferredFrames(mAlsaDeviceProxies[0], position->frames);
206     if (mIsInput) {
207         if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
208                                                  &position->timeNs);
209             ret != 0) {
210             LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
211             return ::android::INVALID_OPERATION;
212         }
213     } else {
214         uint64_t hwFrames;
215         struct timespec timestamp;
216         if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
217                                                       &timestamp);
218             ret == 0) {
219             if (hwFrames > std::numeric_limits<int64_t>::max()) {
220                 hwFrames -= std::numeric_limits<int64_t>::max();
221             }
222             position->frames = static_cast<int64_t>(hwFrames);
223             position->timeNs = audio_utils_ns_from_timespec(&timestamp);
224         } else {
225             LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
226             return ::android::INVALID_OPERATION;
227         }
228     }
229     return ::android::OK;
230 }
231 
shutdown()232 void StreamAlsa::shutdown() {
233     teardownIo();
234 }
235 
setGain(float gain)236 ndk::ScopedAStatus StreamAlsa::setGain(float gain) {
237     mGain = gain;
238     return ndk::ScopedAStatus::ok();
239 }
240 
inputIoThread(size_t idx)241 void StreamAlsa::inputIoThread(size_t idx) {
242 #if defined(__ANDROID__)
243     setWorkerThreadPriority(pthread_gettid_np(pthread_self()));
244     const std::string threadName = (std::string("in_") + std::to_string(idx)).substr(0, 15);
245     pthread_setname_np(pthread_self(), threadName.c_str());
246 #endif
247     const size_t bufferSize = mBufferSizeFrames * mFrameSizeBytes;
248     std::vector<char> buffer(bufferSize);
249     while (mIoThreadIsRunning) {
250         if (int ret = proxy_read_with_retries(mAlsaDeviceProxies[idx].get(), &buffer[0], bufferSize,
251                                               mReadWriteRetries);
252             ret == 0) {
253             size_t bufferFramesWritten = 0;
254             while (bufferFramesWritten < mBufferSizeFrames) {
255                 if (!mIoThreadIsRunning) return;
256                 ssize_t framesWrittenOrError =
257                         mSinks[idx]->write(&buffer[0], mBufferSizeFrames - bufferFramesWritten);
258                 if (framesWrittenOrError >= 0) {
259                     bufferFramesWritten += framesWrittenOrError;
260                 } else {
261                     LOG(WARNING) << __func__ << "[" << idx
262                                  << "]: Error while writing into the pipe: "
263                                  << framesWrittenOrError;
264                 }
265             }
266         } else {
267             // Errors when the stream is being stopped are expected.
268             LOG_IF(WARNING, mIoThreadIsRunning)
269                     << __func__ << "[" << idx << "]: Error reading from ALSA: " << ret;
270         }
271     }
272 }
273 
outputIoThread(size_t idx)274 void StreamAlsa::outputIoThread(size_t idx) {
275 #if defined(__ANDROID__)
276     setWorkerThreadPriority(pthread_gettid_np(pthread_self()));
277     const std::string threadName = (std::string("out_") + std::to_string(idx)).substr(0, 15);
278     pthread_setname_np(pthread_self(), threadName.c_str());
279 #endif
280     const size_t bufferSize = mBufferSizeFrames * mFrameSizeBytes;
281     std::vector<char> buffer(bufferSize);
282     while (mIoThreadIsRunning) {
283         ssize_t framesReadOrError = mSources[idx]->read(&buffer[0], mBufferSizeFrames);
284         if (framesReadOrError > 0) {
285             int ret = proxy_write_with_retries(mAlsaDeviceProxies[idx].get(), &buffer[0],
286                                                framesReadOrError * mFrameSizeBytes,
287                                                mReadWriteRetries);
288             // Errors when the stream is being stopped are expected.
289             LOG_IF(WARNING, ret != 0 && mIoThreadIsRunning)
290                     << __func__ << "[" << idx << "]: Error writing into ALSA: " << ret;
291         } else if (framesReadOrError == 0) {
292             // MonoPipeReader does not have a blocking read, while use of std::condition_variable
293             // requires use of a mutex. For now, just do a 1ms sleep. Consider using a different
294             // pipe / ring buffer mechanism.
295             if (mIoThreadIsRunning) usleep(1000);
296         } else {
297             LOG(WARNING) << __func__ << "[" << idx
298                          << "]: Error while reading from the pipe: " << framesReadOrError;
299         }
300     }
301 }
302 
teardownIo()303 void StreamAlsa::teardownIo() {
304     mIoThreadIsRunning = false;
305     if (mIsInput) {
306         LOG(DEBUG) << __func__ << ": shutting down pipes";
307         for (auto& sink : mSinks) {
308             sink->shutdown(true);
309         }
310     }
311     LOG(DEBUG) << __func__ << ": stopping PCM streams";
312     for (const auto& proxy : mAlsaDeviceProxies) {
313         proxy_stop(proxy.get());
314     }
315     LOG(DEBUG) << __func__ << ": joining threads";
316     for (auto& thread : mIoThreads) {
317         if (thread.joinable()) thread.join();
318     }
319     mIoThreads.clear();
320     LOG(DEBUG) << __func__ << ": closing PCM devices";
321     mAlsaDeviceProxies.clear();
322     mSources.clear();
323     mSinks.clear();
324 }
325 
326 }  // namespace aidl::android::hardware::audio::core
327