blob: 9cc6fb84248ccfbac4e5cd7f36dc9ded37287c1e [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";
58 return mStatus;
59 }
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";
67 return mStatus;
68 }
69 sp<MonoPipe> sink = mCurrentRoute->getSink();
70 if (sink == nullptr) {
71 LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
72 return mStatus;
73 }
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";
80 return mStatus;
81 }
82 }
83 }
84
85 mCurrentRoute->openStream(mIsInput);
86 mStatus = ::android::OK;
87 return mStatus;
88}
89
90::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
91 usleep(1000);
92 return ::android::OK;
93}
94
95::android::status_t StreamRemoteSubmix::flush() {
96 usleep(1000);
97 return ::android::OK;
98}
99
100::android::status_t StreamRemoteSubmix::pause() {
101 usleep(1000);
102 return ::android::OK;
103}
104
105ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
106 if (!mIsInput) {
107 std::shared_ptr<SubmixRoute> route = nullptr;
108 {
109 std::lock_guard guard(sSubmixRoutesLock);
110 if (sSubmixRoutes.find(mPortId) != sSubmixRoutes.end()) {
111 route = sSubmixRoutes[mPortId];
112 }
113 }
114 if (route != nullptr) {
115 sp<MonoPipe> sink = route->getSink();
116 if (sink == nullptr) {
117 ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
118 }
119 LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
120
121 sink->shutdown(true);
122 } else {
123 LOG(DEBUG) << __func__ << ": stream already closed.";
124 ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
125 }
126 }
127 return ndk::ScopedAStatus::ok();
128}
129
130// Remove references to the specified input and output streams. When the device no longer
131// references input and output streams destroy the associated pipe.
132void StreamRemoteSubmix::shutdown() {
133 mCurrentRoute->closeStream(mIsInput);
134 // If all stream instances are closed, we can remove route information for this port.
135 if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
136 mCurrentRoute->releasePipe();
137 LOG(DEBUG) << __func__ << ": pipe destroyed";
138
139 std::lock_guard guard(sSubmixRoutesLock);
140 sSubmixRoutes.erase(mPortId);
141 mStatus = ::android::NO_INIT;
142 }
143}
144
145::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
146 size_t* actualFrameCount, int32_t* latencyMs) {
147 if (mStatus != ::android::OK) {
148 LOG(ERROR) << __func__ << ": failed, not configured";
149 return ::android::NO_INIT;
150 }
151
152 *latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate;
153 LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
154
155 sp<MonoPipe> sink = mCurrentRoute->getSink();
156 if (sink != nullptr) {
157 if (sink->isShutdown()) {
158 sink.clear();
159 LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer.";
160 // the pipe has already been shutdown, this buffer will be lost but we must simulate
161 // timing so we don't drain the output faster than realtime
162 const size_t delayUs = static_cast<size_t>(
163 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
164 usleep(delayUs);
165
166 *actualFrameCount = frameCount;
167 return ::android::OK;
168 }
169 } else {
170 LOG(ERROR) << __func__ << ": transfer without a pipe!";
171 return ::android::UNEXPECTED_NULL;
172 }
173
174 mCurrentRoute->exitStandby(mIsInput);
175 return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
176 : outWrite(buffer, frameCount, actualFrameCount));
177}
178
179// Calculate the maximum size of the pipe buffer in frames for the specified stream.
180size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
181 auto pipeConfig = mCurrentRoute->mPipeConfig;
182 const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
183 return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
184}
185
186::android::status_t StreamRemoteSubmix::outWrite(void* buffer, size_t frameCount,
187 size_t* actualFrameCount) {
188 sp<MonoPipe> sink = mCurrentRoute->getSink();
189 if (sink != nullptr) {
190 if (sink->isShutdown()) {
191 sink.clear();
192 LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
193 // the pipe has already been shutdown, this buffer will be lost but we must
194 // simulate timing so we don't drain the output faster than realtime
195 const size_t delayUs = static_cast<size_t>(
196 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
197 usleep(delayUs);
198 *actualFrameCount = frameCount;
199 return ::android::OK;
200 }
201 } else {
202 LOG(FATAL) << __func__ << ": without a pipe!";
203 return ::android::UNKNOWN_ERROR;
204 }
205
206 const size_t availableToWrite = sink->availableToWrite();
207 // NOTE: sink has been checked above and sink and source life cycles are synchronized
208 sp<MonoPipeReader> source = mCurrentRoute->getSource();
209 // If the write to the sink should be blocked, flush enough frames from the pipe to make space
210 // to write the most recent data.
211 if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
212 static uint8_t flushBuffer[64];
213 const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
214 size_t framesToFlushFromSource = frameCount - availableToWrite;
215 LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
216 << " frames from the pipe to avoid blocking";
217 while (framesToFlushFromSource) {
218 const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
219 framesToFlushFromSource -= flushSize;
220 // read does not block
221 source->read(flushBuffer, flushSize);
222 }
223 }
224
225 ssize_t writtenFrames = sink->write(buffer, frameCount);
226 if (writtenFrames < 0) {
227 if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
228 LOG(ERROR) << __func__ << ": write to pipe returned NEGOTIATE";
229 sink.clear();
230 *actualFrameCount = 0;
231 return ::android::UNKNOWN_ERROR;
232 } else {
233 // write() returned UNDERRUN or WOULD_BLOCK, retry
234 LOG(ERROR) << __func__ << ": write to pipe returned unexpected " << writtenFrames;
235 writtenFrames = sink->write(buffer, frameCount);
236 }
237 }
238 sink.clear();
239
240 if (writtenFrames < 0) {
241 LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
242 *actualFrameCount = 0;
243 return ::android::UNKNOWN_ERROR;
244 }
245 LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames";
246 *actualFrameCount = writtenFrames;
247 return ::android::OK;
248}
249
250::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount,
251 size_t* actualFrameCount) {
252 // about to read from audio source
253 sp<MonoPipeReader> source = mCurrentRoute->getSource();
254 if (source == nullptr) {
255 int readErrorCount = mCurrentRoute->notifyReadError();
256 if (readErrorCount < kMaxReadErrorLogs) {
257 LOG(ERROR)
258 << __func__
259 << ": no audio pipe yet we're trying to read! (not all errors will be logged)";
260 } else {
261 LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
262 }
263 const size_t delayUs = static_cast<size_t>(
264 std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
265 usleep(delayUs);
266 memset(buffer, 0, mStreamConfig.frameSize * frameCount);
267 *actualFrameCount = frameCount;
268 return ::android::OK;
269 }
270
271 // read the data from the pipe
272 int attempts = 0;
273 const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
274 char* buff = (char*)buffer;
275 size_t remainingFrames = frameCount;
276
277 while ((remainingFrames > 0) && (attempts < kMaxReadFailureAttempts)) {
278 LOG(VERBOSE) << __func__ << ": frames available to read " << source->availableToRead();
279
280 ssize_t framesRead = source->read(buff, remainingFrames);
281
282 LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
283
284 if (framesRead > 0) {
285 remainingFrames -= framesRead;
286 buff += framesRead * mStreamConfig.frameSize;
287 LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
288 << " frames, remaining=" << remainingFrames;
289 } else {
290 attempts++;
291 LOG(WARNING) << __func__ << ": read returned " << framesRead
292 << " , read failure attempts = " << attempts;
293 usleep(delayUs);
294 }
295 }
296 // done using the source
297 source.clear();
298
299 if (remainingFrames > 0) {
300 const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize;
301 LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames;
302 memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0,
303 remainingBytes);
304 }
305
306 long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount);
307 *actualFrameCount = frameCount;
308
309 // compute how much we need to sleep after reading the data by comparing the wall clock with
310 // the projected time at which we should return.
311 // wall clock after reading from the pipe
312 auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
313
314 // readCounterFrames contains the number of frames that have been read since the beginning of
315 // recording (including this call): it's converted to usec and compared to how long we've been
316 // recording for, which gives us how long we must wait to sync the projected recording time, and
317 // the observed recording time.
318 static constexpr float kScaleFactor = .8f;
319 const size_t projectedVsObservedOffsetUs =
320 kScaleFactor * (static_cast<size_t>(std::roundf((readCounterFrames * MICROS_PER_SECOND /
321 mStreamConfig.sampleRate) -
322 recordDurationUs.count())));
323
324 LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
325 << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
326 if (projectedVsObservedOffsetUs > 0) {
327 usleep(projectedVsObservedOffsetUs);
328 }
329 return ::android::OK;
330}
331
332::android::status_t StreamRemoteSubmix::standby() {
333 mCurrentRoute->standby(mIsInput);
334 return ::android::OK;
335}
336
337StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
338 StreamContext&& context,
339 const std::vector<MicrophoneInfo>& microphones)
340 : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
341
342ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
343 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
344 LOG(DEBUG) << __func__ << ": not supported";
345 *_aidl_return = std::vector<MicrophoneDynamicInfo>();
346 return ndk::ScopedAStatus::ok();
347}
348
349StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
350 StreamContext&& context,
351 const std::optional<AudioOffloadInfo>& offloadInfo)
352 : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
353
354} // namespace aidl::android::hardware::audio::core