blob: 5af0d914b8e241e0e7244816a707e7208a3ae1b6 [file] [log] [blame]
Shraddha Basantwani6bb69632023-04-25 15:26:38 +05301/*
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#define LOG_TAG "AHAL_StreamRemoteSubmix"
18#include <android-base/logging.h>
19
20#include <cmath>
21
22#include "core-impl/StreamRemoteSubmix.h"
23
24using aidl::android::hardware::audio::common::SinkMetadata;
25using aidl::android::hardware::audio::common::SourceMetadata;
26using aidl::android::media::audio::common::AudioOffloadInfo;
27using aidl::android::media::audio::common::MicrophoneDynamicInfo;
28using aidl::android::media::audio::common::MicrophoneInfo;
29
30namespace aidl::android::hardware::audio::core {
31
32StreamRemoteSubmix::StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context)
33 : StreamCommonImpl(metadata, std::move(context)),
34 mPortId(context.getPortId()),
35 mIsInput(isInput(metadata)) {
36 mStreamConfig.frameSize = context.getFrameSize();
37 mStreamConfig.format = context.getFormat();
38 mStreamConfig.channelLayout = context.getChannelLayout();
39 mStreamConfig.sampleRate = context.getSampleRate();
40}
41
42std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
43std::map<int32_t, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::sSubmixRoutes;
44
45::android::status_t StreamRemoteSubmix::init() {
46 {
47 std::lock_guard guard(sSubmixRoutesLock);
48 if (sSubmixRoutes.find(mPortId) != sSubmixRoutes.end()) {
49 mCurrentRoute = sSubmixRoutes[mPortId];
50 }
51 }
52 // If route is not available for this port, add it.
53 if (mCurrentRoute == nullptr) {
54 // Initialize the pipe.
55 mCurrentRoute = std::make_shared<SubmixRoute>();
56 if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) {
57 LOG(ERROR) << __func__ << ": create pipe failed";
Mikhail Naganov49712b52023-06-27 16:39:33 -070058 return ::android::NO_INIT;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053059 }
60 {
61 std::lock_guard guard(sSubmixRoutesLock);
62 sSubmixRoutes.emplace(mPortId, mCurrentRoute);
63 }
64 } else {
65 if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
66 LOG(ERROR) << __func__ << ": invalid stream config";
Mikhail Naganov49712b52023-06-27 16:39:33 -070067 return ::android::NO_INIT;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053068 }
69 sp<MonoPipe> sink = mCurrentRoute->getSink();
70 if (sink == nullptr) {
71 LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
Mikhail Naganov49712b52023-06-27 16:39:33 -070072 return ::android::NO_INIT;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053073 }
74 // If the sink has been shutdown or pipe recreation is forced, delete the pipe and
75 // recreate it.
76 if (sink->isShutdown()) {
77 LOG(DEBUG) << __func__ << ": Non-nullptr shut down sink when opening stream";
78 if (::android::OK != mCurrentRoute->resetPipe()) {
79 LOG(ERROR) << __func__ << ": reset pipe failed";
Mikhail Naganov49712b52023-06-27 16:39:33 -070080 return ::android::NO_INIT;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053081 }
82 }
83 }
84
85 mCurrentRoute->openStream(mIsInput);
Mikhail Naganov49712b52023-06-27 16:39:33 -070086 return ::android::OK;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053087}
88
89::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
90 usleep(1000);
91 return ::android::OK;
92}
93
94::android::status_t StreamRemoteSubmix::flush() {
95 usleep(1000);
96 return ::android::OK;
97}
98
99::android::status_t StreamRemoteSubmix::pause() {
100 usleep(1000);
101 return ::android::OK;
102}
103
Mikhail Naganov49712b52023-06-27 16:39:33 -0700104::android::status_t StreamRemoteSubmix::standby() {
105 mCurrentRoute->standby(mIsInput);
106 return ::android::OK;
107}
108
109::android::status_t StreamRemoteSubmix::start() {
110 mCurrentRoute->exitStandby(mIsInput);
111 return ::android::OK;
112}
113
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530114ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
115 if (!mIsInput) {
116 std::shared_ptr<SubmixRoute> route = nullptr;
117 {
118 std::lock_guard guard(sSubmixRoutesLock);
119 if (sSubmixRoutes.find(mPortId) != sSubmixRoutes.end()) {
120 route = sSubmixRoutes[mPortId];
121 }
122 }
123 if (route != nullptr) {
124 sp<MonoPipe> sink = route->getSink();
125 if (sink == nullptr) {
126 ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
127 }
128 LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
129
130 sink->shutdown(true);
131 } else {
132 LOG(DEBUG) << __func__ << ": stream already closed.";
133 ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
134 }
135 }
136 return ndk::ScopedAStatus::ok();
137}
138
139// Remove references to the specified input and output streams. When the device no longer
140// references input and output streams destroy the associated pipe.
141void StreamRemoteSubmix::shutdown() {
142 mCurrentRoute->closeStream(mIsInput);
143 // If all stream instances are closed, we can remove route information for this port.
144 if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
145 mCurrentRoute->releasePipe();
146 LOG(DEBUG) << __func__ << ": pipe destroyed";
147
148 std::lock_guard guard(sSubmixRoutesLock);
149 sSubmixRoutes.erase(mPortId);
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530150 }
Mikhail Naganov49712b52023-06-27 16:39:33 -0700151 mCurrentRoute.reset();
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530152}
153
154::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
155 size_t* actualFrameCount, int32_t* latencyMs) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530156 *latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate;
157 LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
158
159 sp<MonoPipe> sink = mCurrentRoute->getSink();
160 if (sink != nullptr) {
161 if (sink->isShutdown()) {
162 sink.clear();
163 LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer.";
164 // the pipe has already been shutdown, this buffer will be lost but we must simulate
165 // timing so we don't drain the output faster than realtime
166 const size_t delayUs = static_cast<size_t>(
167 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
168 usleep(delayUs);
169
170 *actualFrameCount = frameCount;
171 return ::android::OK;
172 }
173 } else {
174 LOG(ERROR) << __func__ << ": transfer without a pipe!";
175 return ::android::UNEXPECTED_NULL;
176 }
177
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530178 return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
179 : outWrite(buffer, frameCount, actualFrameCount));
180}
181
182// Calculate the maximum size of the pipe buffer in frames for the specified stream.
183size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
184 auto pipeConfig = mCurrentRoute->mPipeConfig;
185 const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
186 return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
187}
188
189::android::status_t StreamRemoteSubmix::outWrite(void* buffer, size_t frameCount,
190 size_t* actualFrameCount) {
191 sp<MonoPipe> sink = mCurrentRoute->getSink();
192 if (sink != nullptr) {
193 if (sink->isShutdown()) {
194 sink.clear();
195 LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
196 // the pipe has already been shutdown, this buffer will be lost but we must
197 // simulate timing so we don't drain the output faster than realtime
198 const size_t delayUs = static_cast<size_t>(
199 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
200 usleep(delayUs);
201 *actualFrameCount = frameCount;
202 return ::android::OK;
203 }
204 } else {
205 LOG(FATAL) << __func__ << ": without a pipe!";
206 return ::android::UNKNOWN_ERROR;
207 }
208
209 const size_t availableToWrite = sink->availableToWrite();
210 // NOTE: sink has been checked above and sink and source life cycles are synchronized
211 sp<MonoPipeReader> source = mCurrentRoute->getSource();
212 // If the write to the sink should be blocked, flush enough frames from the pipe to make space
213 // to write the most recent data.
214 if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
215 static uint8_t flushBuffer[64];
216 const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
217 size_t framesToFlushFromSource = frameCount - availableToWrite;
218 LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
219 << " frames from the pipe to avoid blocking";
220 while (framesToFlushFromSource) {
221 const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
222 framesToFlushFromSource -= flushSize;
223 // read does not block
224 source->read(flushBuffer, flushSize);
225 }
226 }
227
228 ssize_t writtenFrames = sink->write(buffer, frameCount);
229 if (writtenFrames < 0) {
230 if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
231 LOG(ERROR) << __func__ << ": write to pipe returned NEGOTIATE";
232 sink.clear();
233 *actualFrameCount = 0;
234 return ::android::UNKNOWN_ERROR;
235 } else {
236 // write() returned UNDERRUN or WOULD_BLOCK, retry
237 LOG(ERROR) << __func__ << ": write to pipe returned unexpected " << writtenFrames;
238 writtenFrames = sink->write(buffer, frameCount);
239 }
240 }
241 sink.clear();
242
243 if (writtenFrames < 0) {
244 LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
245 *actualFrameCount = 0;
246 return ::android::UNKNOWN_ERROR;
247 }
248 LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames";
249 *actualFrameCount = writtenFrames;
250 return ::android::OK;
251}
252
253::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount,
254 size_t* actualFrameCount) {
255 // about to read from audio source
256 sp<MonoPipeReader> source = mCurrentRoute->getSource();
257 if (source == nullptr) {
258 int readErrorCount = mCurrentRoute->notifyReadError();
259 if (readErrorCount < kMaxReadErrorLogs) {
260 LOG(ERROR)
261 << __func__
262 << ": no audio pipe yet we're trying to read! (not all errors will be logged)";
263 } else {
264 LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
265 }
266 const size_t delayUs = static_cast<size_t>(
267 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
268 usleep(delayUs);
269 memset(buffer, 0, mStreamConfig.frameSize * frameCount);
270 *actualFrameCount = frameCount;
271 return ::android::OK;
272 }
273
274 // read the data from the pipe
275 int attempts = 0;
276 const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
277 char* buff = (char*)buffer;
278 size_t remainingFrames = frameCount;
279
280 while ((remainingFrames > 0) && (attempts < kMaxReadFailureAttempts)) {
281 LOG(VERBOSE) << __func__ << ": frames available to read " << source->availableToRead();
282
283 ssize_t framesRead = source->read(buff, remainingFrames);
284
285 LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
286
287 if (framesRead > 0) {
288 remainingFrames -= framesRead;
289 buff += framesRead * mStreamConfig.frameSize;
290 LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
291 << " frames, remaining=" << remainingFrames;
292 } else {
293 attempts++;
294 LOG(WARNING) << __func__ << ": read returned " << framesRead
295 << " , read failure attempts = " << attempts;
296 usleep(delayUs);
297 }
298 }
299 // done using the source
300 source.clear();
301
302 if (remainingFrames > 0) {
303 const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize;
304 LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames;
305 memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0,
306 remainingBytes);
307 }
308
309 long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount);
310 *actualFrameCount = frameCount;
311
312 // compute how much we need to sleep after reading the data by comparing the wall clock with
313 // the projected time at which we should return.
314 // wall clock after reading from the pipe
315 auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
316
317 // readCounterFrames contains the number of frames that have been read since the beginning of
318 // recording (including this call): it's converted to usec and compared to how long we've been
319 // recording for, which gives us how long we must wait to sync the projected recording time, and
320 // the observed recording time.
321 static constexpr float kScaleFactor = .8f;
322 const size_t projectedVsObservedOffsetUs =
323 kScaleFactor * (static_cast<size_t>(std::roundf((readCounterFrames * MICROS_PER_SECOND /
324 mStreamConfig.sampleRate) -
325 recordDurationUs.count())));
326
327 LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
328 << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
329 if (projectedVsObservedOffsetUs > 0) {
330 usleep(projectedVsObservedOffsetUs);
331 }
332 return ::android::OK;
333}
334
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530335StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
336 StreamContext&& context,
337 const std::vector<MicrophoneInfo>& microphones)
338 : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
339
340ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
341 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
342 LOG(DEBUG) << __func__ << ": not supported";
343 *_aidl_return = std::vector<MicrophoneDynamicInfo>();
344 return ndk::ScopedAStatus::ok();
345}
346
347StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
348 StreamContext&& context,
349 const std::optional<AudioOffloadInfo>& offloadInfo)
350 : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
351
352} // namespace aidl::android::hardware::audio::core