1 /*
2 * Copyright (C) 2025 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_OffloadStream"
18 #include <android-base/logging.h>
19 #include <audio_utils/clock.h>
20 #include <error/Result.h>
21 #include <utils/SystemClock.h>
22
23 #include "ApeHeader.h"
24 #include "core-impl/StreamOffloadStub.h"
25
26 using aidl::android::hardware::audio::common::SourceMetadata;
27 using aidl::android::media::audio::common::AudioDevice;
28 using aidl::android::media::audio::common::AudioOffloadInfo;
29 using aidl::android::media::audio::common::MicrophoneInfo;
30
31 namespace aidl::android::hardware::audio::core {
32
33 namespace offload {
34
init()35 std::string DspSimulatorLogic::init() {
36 return "";
37 }
38
cycle()39 DspSimulatorLogic::Status DspSimulatorLogic::cycle() {
40 std::vector<std::pair<int64_t, bool>> clipNotifies;
41 // Simulate playback.
42 const int64_t timeBeginNs = ::android::uptimeNanos();
43 usleep(1000);
44 const int64_t clipFramesPlayed =
45 (::android::uptimeNanos() - timeBeginNs) * mSharedState.sampleRate / NANOS_PER_SECOND;
46 const int64_t bufferFramesConsumed = clipFramesPlayed / 2; // assume 1:2 compression ratio
47 int64_t bufferFramesLeft = 0, bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
48 {
49 std::lock_guard l(mSharedState.lock);
50 mSharedState.bufferFramesLeft =
51 mSharedState.bufferFramesLeft > bufferFramesConsumed
52 ? mSharedState.bufferFramesLeft - bufferFramesConsumed
53 : 0;
54 int64_t framesPlayed = clipFramesPlayed;
55 while (framesPlayed > 0 && !mSharedState.clipFramesLeft.empty()) {
56 LOG(VERBOSE) << __func__ << ": clips: "
57 << ::android::internal::ToString(mSharedState.clipFramesLeft);
58 const bool hasNextClip = mSharedState.clipFramesLeft.size() > 1;
59 if (mSharedState.clipFramesLeft[0] > framesPlayed) {
60 mSharedState.clipFramesLeft[0] -= framesPlayed;
61 framesPlayed = 0;
62 if (mSharedState.clipFramesLeft[0] <= mSharedState.earlyNotifyFrames) {
63 clipNotifies.emplace_back(mSharedState.clipFramesLeft[0], hasNextClip);
64 }
65 } else {
66 clipNotifies.emplace_back(0 /*clipFramesLeft*/, hasNextClip);
67 framesPlayed -= mSharedState.clipFramesLeft[0];
68 mSharedState.clipFramesLeft.erase(mSharedState.clipFramesLeft.begin());
69 if (!hasNextClip) {
70 // Since it's a simulation, the buffer consumption rate it not real,
71 // thus 'bufferFramesLeft' might still have something, need to erase it.
72 mSharedState.bufferFramesLeft = 0;
73 }
74 }
75 }
76 bufferFramesLeft = mSharedState.bufferFramesLeft;
77 bufferNotifyFrames = mSharedState.bufferNotifyFrames;
78 if (bufferFramesLeft <= bufferNotifyFrames) {
79 // Suppress further notifications.
80 mSharedState.bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
81 }
82 }
83 if (bufferFramesLeft <= bufferNotifyFrames) {
84 LOG(DEBUG) << __func__ << ": sending onBufferStateChange: " << bufferFramesLeft;
85 mSharedState.callback->onBufferStateChange(bufferFramesLeft);
86 }
87 for (const auto& notify : clipNotifies) {
88 LOG(DEBUG) << __func__ << ": sending onClipStateChange: " << notify.first << ", "
89 << notify.second;
90 mSharedState.callback->onClipStateChange(notify.first, notify.second);
91 }
92 return Status::CONTINUE;
93 }
94
95 } // namespace offload
96
97 using offload::DspSimulatorState;
98
DriverOffloadStubImpl(const StreamContext & context)99 DriverOffloadStubImpl::DriverOffloadStubImpl(const StreamContext& context)
100 : DriverStubImpl(context, 0 /*asyncSleepTimeUs*/),
101 mBufferNotifyFrames(static_cast<int64_t>(context.getBufferSizeInFrames()) / 2),
102 mState{context.getFormat().encoding, context.getSampleRate(),
103 250 /*earlyNotifyMs*/ * context.getSampleRate() / MILLIS_PER_SECOND},
104 mDspWorker(mState) {
105 LOG_IF(FATAL, !mIsAsynchronous) << "The steam must be used in asynchronous mode";
106 }
107
init(DriverCallbackInterface * callback)108 ::android::status_t DriverOffloadStubImpl::init(DriverCallbackInterface* callback) {
109 RETURN_STATUS_IF_ERROR(DriverStubImpl::init(callback));
110 if (!StreamOffloadStub::getSupportedEncodings().count(mState.formatEncoding)) {
111 LOG(ERROR) << __func__ << ": encoded format \"" << mState.formatEncoding
112 << "\" is not supported";
113 return ::android::NO_INIT;
114 }
115 mState.callback = callback;
116 return ::android::OK;
117 }
118
drain(StreamDescriptor::DrainMode drainMode)119 ::android::status_t DriverOffloadStubImpl::drain(StreamDescriptor::DrainMode drainMode) {
120 RETURN_STATUS_IF_ERROR(DriverStubImpl::drain(drainMode));
121 std::lock_guard l(mState.lock);
122 if (!mState.clipFramesLeft.empty()) {
123 // Cut playback of the current clip.
124 mState.clipFramesLeft[0] = std::min(mState.earlyNotifyFrames * 2, mState.clipFramesLeft[0]);
125 if (drainMode == StreamDescriptor::DrainMode::DRAIN_ALL) {
126 // Make sure there are no clips after the current one.
127 mState.clipFramesLeft.resize(1);
128 }
129 }
130 mState.bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
131 return ::android::OK;
132 }
133
flush()134 ::android::status_t DriverOffloadStubImpl::flush() {
135 RETURN_STATUS_IF_ERROR(DriverStubImpl::flush());
136 mDspWorker.pause();
137 {
138 std::lock_guard l(mState.lock);
139 mState.clipFramesLeft.clear();
140 mState.bufferFramesLeft = 0;
141 mState.bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
142 }
143 return ::android::OK;
144 }
145
pause()146 ::android::status_t DriverOffloadStubImpl::pause() {
147 RETURN_STATUS_IF_ERROR(DriverStubImpl::pause());
148 mDspWorker.pause();
149 {
150 std::lock_guard l(mState.lock);
151 mState.bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
152 }
153 return ::android::OK;
154 }
155
start()156 ::android::status_t DriverOffloadStubImpl::start() {
157 RETURN_STATUS_IF_ERROR(DriverStubImpl::start());
158 RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
159 bool hasClips; // Can be start after paused draining.
160 {
161 std::lock_guard l(mState.lock);
162 hasClips = !mState.clipFramesLeft.empty();
163 LOG(DEBUG) << __func__
164 << ": clipFramesLeft: " << ::android::internal::ToString(mState.clipFramesLeft);
165 mState.bufferNotifyFrames = DspSimulatorState::kSkipBufferNotifyFrames;
166 }
167 if (hasClips) {
168 mDspWorker.resume();
169 }
170 return ::android::OK;
171 }
172
transfer(void * buffer,size_t frameCount,size_t * actualFrameCount,int32_t * latencyMs)173 ::android::status_t DriverOffloadStubImpl::transfer(void* buffer, size_t frameCount,
174 size_t* actualFrameCount, int32_t* latencyMs) {
175 RETURN_STATUS_IF_ERROR(
176 DriverStubImpl::transfer(buffer, frameCount, actualFrameCount, latencyMs));
177 RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
178 // Scan the buffer for clip headers.
179 *actualFrameCount = frameCount;
180 while (buffer != nullptr && frameCount > 0) {
181 ApeHeader* apeHeader = nullptr;
182 void* prevBuffer = buffer;
183 buffer = findApeHeader(prevBuffer, frameCount * mFrameSizeBytes, &apeHeader);
184 if (buffer != nullptr && apeHeader != nullptr) {
185 // Frame count does not include the size of the header data.
186 const size_t headerSizeFrames =
187 (static_cast<uint8_t*>(buffer) - static_cast<uint8_t*>(prevBuffer)) /
188 mFrameSizeBytes;
189 frameCount -= headerSizeFrames;
190 *actualFrameCount = frameCount;
191 // Stage the clip duration into the DSP worker's queue.
192 const int64_t clipDurationFrames = getApeClipDurationFrames(apeHeader);
193 const int32_t clipSampleRate = apeHeader->sampleRate;
194 LOG(DEBUG) << __func__ << ": found APE clip of " << clipDurationFrames << " frames, "
195 << "sample rate: " << clipSampleRate;
196 if (clipSampleRate == mState.sampleRate) {
197 std::lock_guard l(mState.lock);
198 mState.clipFramesLeft.push_back(clipDurationFrames);
199 } else {
200 LOG(ERROR) << __func__ << ": clip sample rate " << clipSampleRate
201 << " does not match stream sample rate " << mState.sampleRate;
202 }
203 } else {
204 frameCount = 0;
205 }
206 }
207 {
208 std::lock_guard l(mState.lock);
209 mState.bufferFramesLeft = *actualFrameCount;
210 mState.bufferNotifyFrames = mBufferNotifyFrames;
211 }
212 mDspWorker.resume();
213 return ::android::OK;
214 }
215
shutdown()216 void DriverOffloadStubImpl::shutdown() {
217 LOG(DEBUG) << __func__ << ": stopping the DSP simulator worker";
218 mDspWorker.stop();
219 DriverStubImpl::shutdown();
220 }
221
startWorkerIfNeeded()222 ::android::status_t DriverOffloadStubImpl::startWorkerIfNeeded() {
223 if (!mDspWorkerStarted) {
224 // This is an "audio service thread," must have elevated priority.
225 if (!mDspWorker.start("dsp_sim", ANDROID_PRIORITY_URGENT_AUDIO)) {
226 return ::android::NO_INIT;
227 }
228 mDspWorkerStarted = true;
229 }
230 return ::android::OK;
231 }
232
233 // static
getSupportedEncodings()234 const std::set<std::string>& StreamOffloadStub::getSupportedEncodings() {
235 static const std::set<std::string> kSupportedEncodings = {
236 "audio/x-ape",
237 };
238 return kSupportedEncodings;
239 }
240
StreamOffloadStub(StreamContext * context,const Metadata & metadata)241 StreamOffloadStub::StreamOffloadStub(StreamContext* context, const Metadata& metadata)
242 : StreamCommonImpl(context, metadata), DriverOffloadStubImpl(getContext()) {}
243
~StreamOffloadStub()244 StreamOffloadStub::~StreamOffloadStub() {
245 cleanupWorker();
246 }
247
StreamOutOffloadStub(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo)248 StreamOutOffloadStub::StreamOutOffloadStub(StreamContext&& context,
249 const SourceMetadata& sourceMetadata,
250 const std::optional<AudioOffloadInfo>& offloadInfo)
251 : StreamOut(std::move(context), offloadInfo),
252 StreamOffloadStub(&mContextInstance, sourceMetadata) {}
253
254 } // namespace aidl::android::hardware::audio::core
255