blob: 7d706c27ceda74ae6c84537ec168dd676a28faf9 [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_SubmixRoute"
18#include <android-base/logging.h>
19#include <media/AidlConversionCppNdk.h>
20
21#include <Utils.h>
22
23#include "SubmixRoute.h"
24
25using aidl::android::hardware::audio::common::getChannelCount;
Mikhail Naganov3b732892023-12-20 16:05:01 -080026using aidl::android::media::audio::common::AudioDeviceAddress;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053027
28namespace aidl::android::hardware::audio::core::r_submix {
29
Mikhail Naganov3b732892023-12-20 16:05:01 -080030// static
31SubmixRoute::RoutesMonitor SubmixRoute::getRoutes() {
32 static std::mutex submixRoutesLock;
33 static RoutesMap submixRoutes;
34 return RoutesMonitor(submixRoutesLock, submixRoutes);
35}
36
37// static
38std::shared_ptr<SubmixRoute> SubmixRoute::findOrCreateRoute(const AudioDeviceAddress& deviceAddress,
39 const AudioConfig& pipeConfig) {
40 auto routes = getRoutes();
41 auto routeItr = routes->find(deviceAddress);
42 if (routeItr != routes->end()) {
43 return routeItr->second;
44 }
45 auto route = std::make_shared<SubmixRoute>();
46 if (::android::OK != route->createPipe(pipeConfig)) {
47 LOG(ERROR) << __func__ << ": create pipe failed";
48 return nullptr;
49 }
50 routes->emplace(deviceAddress, route);
51 return route;
52}
53
54// static
55std::shared_ptr<SubmixRoute> SubmixRoute::findRoute(const AudioDeviceAddress& deviceAddress) {
56 auto routes = getRoutes();
57 auto routeItr = routes->find(deviceAddress);
58 if (routeItr != routes->end()) {
59 return routeItr->second;
60 }
61 return nullptr;
62}
63
64// static
65void SubmixRoute::removeRoute(const AudioDeviceAddress& deviceAddress) {
66 getRoutes()->erase(deviceAddress);
67}
68
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053069// Verify a submix input or output stream can be opened.
Shraddha Basantwani7770c152023-07-19 16:43:50 +053070bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053071 // If the stream is already open, don't open it again.
72 // ENABLE_LEGACY_INPUT_OPEN is default behaviour
73 if (!isInput && isStreamOutOpen()) {
74 LOG(ERROR) << __func__ << ": output stream already open.";
75 return false;
76 }
77 // If either stream is open, verify the existing pipe config matches the stream config.
78 if (hasAtleastOneStreamOpen() && !isStreamConfigCompatible(streamConfig)) {
79 return false;
80 }
81 return true;
82}
83
84// Compare this stream config with existing pipe config, returning false if they do *not*
85// match, true otherwise.
Shraddha Basantwani7770c152023-07-19 16:43:50 +053086bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) {
Mikhail Naganov3b732892023-12-20 16:05:01 -080087 std::lock_guard guard(mLock);
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053088 if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
89 LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
90 << streamConfig.channelLayout.toString()
91 << " pipe config channels = " << mPipeConfig.channelLayout.toString();
92 return false;
93 }
94 if (streamConfig.sampleRate != mPipeConfig.sampleRate) {
95 LOG(ERROR) << __func__
96 << ": sample rate mismatch, stream sample rate = " << streamConfig.sampleRate
97 << " pipe config sample rate = " << mPipeConfig.sampleRate;
98 return false;
99 }
100 if (streamConfig.format != mPipeConfig.format) {
101 LOG(ERROR) << __func__
102 << ": format mismatch, stream format = " << streamConfig.format.toString()
103 << " pipe config format = " << mPipeConfig.format.toString();
104 return false;
105 }
106 return true;
107}
108
109bool SubmixRoute::hasAtleastOneStreamOpen() {
110 std::lock_guard guard(mLock);
111 return (mStreamInOpen || mStreamOutOpen);
112}
113
114// We DO NOT block if:
115// - no peer input stream is present
116// - the peer input is in standby AFTER having been active.
117// We DO block if:
118// - the input was never activated to avoid discarding first frames in the pipe in case capture
119// start was delayed
120bool SubmixRoute::shouldBlockWrite() {
121 std::lock_guard guard(mLock);
122 return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0)));
123}
124
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530125long SubmixRoute::updateReadCounterFrames(size_t frameCount) {
126 std::lock_guard guard(mLock);
127 mReadCounterFrames += frameCount;
128 return mReadCounterFrames;
129}
130
131void SubmixRoute::openStream(bool isInput) {
132 std::lock_guard guard(mLock);
133 if (isInput) {
134 if (mStreamInOpen) {
135 mInputRefCount++;
136 } else {
137 mInputRefCount = 1;
138 mStreamInOpen = true;
139 }
140 mStreamInStandby = true;
141 mReadCounterFrames = 0;
Mikhail Naganov7b234d42023-12-05 11:28:56 -0800142 if (mSink != nullptr) {
143 mSink->shutdown(false);
144 }
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530145 } else {
146 mStreamOutOpen = true;
147 }
148}
149
150void SubmixRoute::closeStream(bool isInput) {
151 std::lock_guard guard(mLock);
152 if (isInput) {
Mikhail Naganov7b234d42023-12-05 11:28:56 -0800153 if (--mInputRefCount == 0) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530154 mStreamInOpen = false;
155 if (mSink != nullptr) {
156 mSink->shutdown(true);
157 }
158 }
159 } else {
160 mStreamOutOpen = false;
161 }
162}
163
164// If SubmixRoute doesn't exist for a port, create a pipe for the submix audio device of size
165// buffer_size_frames and store config of the submix audio device.
Shraddha Basantwani7770c152023-07-19 16:43:50 +0530166::android::status_t SubmixRoute::createPipe(const AudioConfig& streamConfig) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530167 const int channelCount = getChannelCount(streamConfig.channelLayout);
168 const audio_format_t audioFormat = VALUE_OR_RETURN_STATUS(
169 aidl2legacy_AudioFormatDescription_audio_format_t(streamConfig.format));
170 const ::android::NBAIO_Format format =
171 ::android::Format_from_SR_C(streamConfig.sampleRate, channelCount, audioFormat);
172 const ::android::NBAIO_Format offers[1] = {format};
173 size_t numCounterOffers = 0;
174
175 const size_t pipeSizeInFrames =
176 r_submix::kDefaultPipeSizeInFrames *
177 ((float)streamConfig.sampleRate / r_submix::kDefaultSampleRateHz);
178 LOG(VERBOSE) << __func__ << ": creating pipe, rate : " << streamConfig.sampleRate
179 << ", pipe size : " << pipeSizeInFrames;
180
181 // Create a MonoPipe with optional blocking set to true.
182 sp<MonoPipe> sink = sp<MonoPipe>::make(pipeSizeInFrames, format, true /*writeCanBlock*/);
183 if (sink == nullptr) {
184 LOG(FATAL) << __func__ << ": sink is null";
185 return ::android::UNEXPECTED_NULL;
186 }
187
188 // Negotiation between the source and sink cannot fail as the device open operation
189 // creates both ends of the pipe using the same audio format.
190 ssize_t index = sink->negotiate(offers, 1, nullptr, numCounterOffers);
191 if (index != 0) {
192 LOG(FATAL) << __func__ << ": Negotiation for the sink failed, index = " << index;
193 return ::android::BAD_INDEX;
194 }
195 sp<MonoPipeReader> source = sp<MonoPipeReader>::make(sink.get());
196 if (source == nullptr) {
197 LOG(FATAL) << __func__ << ": source is null";
198 return ::android::UNEXPECTED_NULL;
199 }
200 numCounterOffers = 0;
201 index = source->negotiate(offers, 1, nullptr, numCounterOffers);
202 if (index != 0) {
203 LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index;
204 return ::android::BAD_INDEX;
205 }
Mikhail Naganov3b732892023-12-20 16:05:01 -0800206 LOG(VERBOSE) << __func__ << ": Pipe frame size : " << streamConfig.frameSize
207 << ", pipe frames : " << sink->maxFrames();
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530208
209 // Save references to the source and sink.
210 {
211 std::lock_guard guard(mLock);
Mikhail Naganov3b732892023-12-20 16:05:01 -0800212 mPipeConfig = streamConfig;
213 mPipeConfig.frameCount = sink->maxFrames();
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530214 mSink = std::move(sink);
215 mSource = std::move(source);
216 }
217
218 return ::android::OK;
219}
220
221// Release references to the sink and source.
Mikhail Naganov3b732892023-12-20 16:05:01 -0800222AudioConfig SubmixRoute::releasePipe() {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530223 std::lock_guard guard(mLock);
224 mSink.clear();
225 mSource.clear();
Mikhail Naganov3b732892023-12-20 16:05:01 -0800226 return mPipeConfig;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530227}
228
229::android::status_t SubmixRoute::resetPipe() {
Mikhail Naganov3b732892023-12-20 16:05:01 -0800230 return createPipe(releasePipe());
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530231}
232
233void SubmixRoute::standby(bool isInput) {
234 std::lock_guard guard(mLock);
235
236 if (isInput) {
237 mStreamInStandby = true;
Shraddha Basantwani7770c152023-07-19 16:43:50 +0530238 } else if (!mStreamOutStandby) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530239 mStreamOutStandby = true;
Shraddha Basantwani7770c152023-07-19 16:43:50 +0530240 mStreamOutStandbyTransition = true;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530241 }
242}
243
244void SubmixRoute::exitStandby(bool isInput) {
245 std::lock_guard guard(mLock);
246
247 if (isInput) {
248 if (mStreamInStandby || mStreamOutStandbyTransition) {
249 mStreamInStandby = false;
250 mStreamOutStandbyTransition = false;
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530251 mReadCounterFrames = 0;
252 }
253 } else {
254 if (mStreamOutStandby) {
255 mStreamOutStandby = false;
256 mStreamOutStandbyTransition = true;
257 }
258 }
259}
260
261} // namespace aidl::android::hardware::audio::core::r_submix