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 ×tamp);
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(×tamp);
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