• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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