blob: 6d5185b3463745bfdcc8082dde1de6cbe06ba2f1 [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
Mikhail Naganov704aec42023-07-13 11:08:29 -0700182::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
183 sp<MonoPipeReader> source = mCurrentRoute->getSource();
184 if (source == nullptr) {
185 return ::android::NO_INIT;
186 }
187 const ssize_t framesInPipe = source->availableToRead();
188 if (framesInPipe < 0) {
189 return ::android::INVALID_OPERATION;
190 }
191 if (mIsInput) {
192 position->frames += framesInPipe;
193 } else {
194 if (position->frames > framesInPipe) {
195 position->frames -= framesInPipe;
196 } else {
197 position->frames = 0;
198 }
199 }
200 return ::android::OK;
201}
202
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530203// Calculate the maximum size of the pipe buffer in frames for the specified stream.
204size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
205 auto pipeConfig = mCurrentRoute->mPipeConfig;
206 const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
207 return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
208}
209
210::android::status_t StreamRemoteSubmix::outWrite(void* buffer, size_t frameCount,
211 size_t* actualFrameCount) {
212 sp<MonoPipe> sink = mCurrentRoute->getSink();
213 if (sink != nullptr) {
214 if (sink->isShutdown()) {
215 sink.clear();
216 LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
217 // the pipe has already been shutdown, this buffer will be lost but we must
218 // simulate timing so we don't drain the output faster than realtime
219 const size_t delayUs = static_cast<size_t>(
220 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
221 usleep(delayUs);
222 *actualFrameCount = frameCount;
223 return ::android::OK;
224 }
225 } else {
226 LOG(FATAL) << __func__ << ": without a pipe!";
227 return ::android::UNKNOWN_ERROR;
228 }
229
230 const size_t availableToWrite = sink->availableToWrite();
231 // NOTE: sink has been checked above and sink and source life cycles are synchronized
232 sp<MonoPipeReader> source = mCurrentRoute->getSource();
233 // If the write to the sink should be blocked, flush enough frames from the pipe to make space
234 // to write the most recent data.
235 if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
236 static uint8_t flushBuffer[64];
237 const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
238 size_t framesToFlushFromSource = frameCount - availableToWrite;
239 LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
240 << " frames from the pipe to avoid blocking";
241 while (framesToFlushFromSource) {
242 const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
243 framesToFlushFromSource -= flushSize;
244 // read does not block
245 source->read(flushBuffer, flushSize);
246 }
247 }
248
249 ssize_t writtenFrames = sink->write(buffer, frameCount);
250 if (writtenFrames < 0) {
251 if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
252 LOG(ERROR) << __func__ << ": write to pipe returned NEGOTIATE";
253 sink.clear();
254 *actualFrameCount = 0;
255 return ::android::UNKNOWN_ERROR;
256 } else {
257 // write() returned UNDERRUN or WOULD_BLOCK, retry
258 LOG(ERROR) << __func__ << ": write to pipe returned unexpected " << writtenFrames;
259 writtenFrames = sink->write(buffer, frameCount);
260 }
261 }
262 sink.clear();
263
264 if (writtenFrames < 0) {
265 LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
266 *actualFrameCount = 0;
267 return ::android::UNKNOWN_ERROR;
268 }
269 LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames";
270 *actualFrameCount = writtenFrames;
271 return ::android::OK;
272}
273
274::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount,
275 size_t* actualFrameCount) {
276 // about to read from audio source
277 sp<MonoPipeReader> source = mCurrentRoute->getSource();
278 if (source == nullptr) {
279 int readErrorCount = mCurrentRoute->notifyReadError();
280 if (readErrorCount < kMaxReadErrorLogs) {
281 LOG(ERROR)
282 << __func__
283 << ": no audio pipe yet we're trying to read! (not all errors will be logged)";
284 } else {
285 LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
286 }
287 const size_t delayUs = static_cast<size_t>(
288 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
289 usleep(delayUs);
290 memset(buffer, 0, mStreamConfig.frameSize * frameCount);
291 *actualFrameCount = frameCount;
292 return ::android::OK;
293 }
294
295 // read the data from the pipe
296 int attempts = 0;
297 const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
298 char* buff = (char*)buffer;
299 size_t remainingFrames = frameCount;
300
301 while ((remainingFrames > 0) && (attempts < kMaxReadFailureAttempts)) {
302 LOG(VERBOSE) << __func__ << ": frames available to read " << source->availableToRead();
303
304 ssize_t framesRead = source->read(buff, remainingFrames);
305
306 LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
307
308 if (framesRead > 0) {
309 remainingFrames -= framesRead;
310 buff += framesRead * mStreamConfig.frameSize;
311 LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
312 << " frames, remaining=" << remainingFrames;
313 } else {
314 attempts++;
315 LOG(WARNING) << __func__ << ": read returned " << framesRead
316 << " , read failure attempts = " << attempts;
317 usleep(delayUs);
318 }
319 }
320 // done using the source
321 source.clear();
322
323 if (remainingFrames > 0) {
324 const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize;
325 LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames;
326 memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0,
327 remainingBytes);
328 }
329
330 long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount);
331 *actualFrameCount = frameCount;
332
333 // compute how much we need to sleep after reading the data by comparing the wall clock with
334 // the projected time at which we should return.
335 // wall clock after reading from the pipe
336 auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
337
338 // readCounterFrames contains the number of frames that have been read since the beginning of
339 // recording (including this call): it's converted to usec and compared to how long we've been
340 // recording for, which gives us how long we must wait to sync the projected recording time, and
341 // the observed recording time.
342 static constexpr float kScaleFactor = .8f;
343 const size_t projectedVsObservedOffsetUs =
344 kScaleFactor * (static_cast<size_t>(std::roundf((readCounterFrames * MICROS_PER_SECOND /
345 mStreamConfig.sampleRate) -
346 recordDurationUs.count())));
347
348 LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
349 << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
350 if (projectedVsObservedOffsetUs > 0) {
351 usleep(projectedVsObservedOffsetUs);
352 }
353 return ::android::OK;
354}
355
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530356StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
357 StreamContext&& context,
358 const std::vector<MicrophoneInfo>& microphones)
359 : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
360
361ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
362 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
363 LOG(DEBUG) << __func__ << ": not supported";
364 *_aidl_return = std::vector<MicrophoneDynamicInfo>();
365 return ndk::ScopedAStatus::ok();
366}
367
368StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
369 StreamContext&& context,
370 const std::optional<AudioOffloadInfo>& offloadInfo)
371 : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
372
373} // namespace aidl::android::hardware::audio::core