audio r_submix: Suggest configuration from the peer am: 3b732895a8 am: de6a93d47d

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2886671

Change-Id: If90ca7bc18bec69a447f1497d9530579aafabd1f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mikhail Naganov
2023-12-27 04:36:28 +00:00
committed by Automerger Merge Worker
6 changed files with 145 additions and 54 deletions

View File

@@ -29,6 +29,10 @@ class ModuleRemoteSubmix : public Module {
// IModule interfaces // IModule interfaces
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override; ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override; ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus setAudioPortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& in_requested,
::aidl::android::media::audio::common::AudioPortConfig* out_suggested,
bool* _aidl_return) override;
// Module interfaces // Module interfaces
ndk::ScopedAStatus createInputStream( ndk::ScopedAStatus createInputStream(

View File

@@ -16,7 +16,6 @@
#pragma once #pragma once
#include <mutex>
#include <vector> #include <vector>
#include "core-impl/Stream.h" #include "core-impl/Stream.h"
@@ -56,13 +55,6 @@ class StreamRemoteSubmix : public StreamCommonImpl {
r_submix::AudioConfig mStreamConfig; r_submix::AudioConfig mStreamConfig;
std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr; std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr;
// Mutex lock to protect vector of submix routes, each of these submix routes have their mutex
// locks and none of the mutex locks should be taken together.
static std::mutex sSubmixRoutesLock;
static std::map<::aidl::android::media::audio::common::AudioDeviceAddress,
std::shared_ptr<r_submix::SubmixRoute>>
sSubmixRoutes GUARDED_BY(sSubmixRoutesLock);
// limit for number of read error log entries to avoid spamming the logs // limit for number of read error log entries to avoid spamming the logs
static constexpr int kMaxReadErrorLogs = 5; static constexpr int kMaxReadErrorLogs = 5;
// The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior // The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior

View File

@@ -27,14 +27,36 @@
using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata; using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioFormatType; using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo; using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort; using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig; using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioProfile;
using aidl::android::media::audio::common::Int;
using aidl::android::media::audio::common::MicrophoneInfo; using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core { namespace aidl::android::hardware::audio::core {
namespace {
std::optional<r_submix::AudioConfig> getRemoteEndConfig(const AudioPort& audioPort) {
const auto& deviceAddress = audioPort.ext.get<AudioPortExt::device>().device.address;
const bool isInput = audioPort.flags.getTag() == AudioIoFlags::input;
if (auto submixRoute = r_submix::SubmixRoute::findRoute(deviceAddress);
submixRoute != nullptr) {
if ((isInput && submixRoute->isStreamOutOpen()) ||
(!isInput && submixRoute->isStreamInOpen())) {
return submixRoute->getPipeConfig();
}
}
return {};
}
} // namespace
ndk::ScopedAStatus ModuleRemoteSubmix::getMicMute(bool* _aidl_return __unused) { ndk::ScopedAStatus ModuleRemoteSubmix::getMicMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported"; LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
@@ -45,6 +67,26 @@ ndk::ScopedAStatus ModuleRemoteSubmix::setMicMute(bool in_mute __unused) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
} }
ndk::ScopedAStatus ModuleRemoteSubmix::setAudioPortConfig(const AudioPortConfig& in_requested,
AudioPortConfig* out_suggested,
bool* _aidl_return) {
auto fillConfig = [this](const AudioPort& port, AudioPortConfig* config) {
if (port.ext.getTag() == AudioPortExt::device) {
if (auto pipeConfig = getRemoteEndConfig(port); pipeConfig.has_value()) {
LOG(DEBUG) << "setAudioPortConfig: suggesting port config from the remote end.";
config->format = pipeConfig->format;
config->channelMask = pipeConfig->channelLayout;
config->sampleRate = Int{.value = pipeConfig->sampleRate};
config->flags = port.flags;
config->ext = port.ext;
return true;
}
}
return generateDefaultPortConfig(port, config);
};
return Module::setAudioPortConfigImpl(in_requested, fillConfig, out_suggested, _aidl_return);
}
ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream( ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream(
StreamContext&& context, const SinkMetadata& sinkMetadata, StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) { const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
@@ -68,7 +110,22 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream(
} }
ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort, int32_t) {
// Find the corresponding mix port and copy its profiles. if (audioPort->ext.getTag() != AudioPortExt::device) {
LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
// If there is already a pipe with a stream for the port address, provide its configuration as
// the only option. Otherwise, find the corresponding mix port and copy its profiles.
if (auto pipeConfig = getRemoteEndConfig(*audioPort); pipeConfig.has_value()) {
audioPort->profiles.clear();
audioPort->profiles.push_back(AudioProfile{
.format = pipeConfig->format,
.channelMasks = std::vector<AudioChannelLayout>({pipeConfig->channelLayout}),
.sampleRates = std::vector<int>({pipeConfig->sampleRate})});
LOG(DEBUG) << __func__ << ": populated from remote end as: " << audioPort->toString();
return ndk::ScopedAStatus::ok();
}
// At this moment, the port has the same ID as the template port, see connectExternalDevice. // At this moment, the port has the same ID as the template port, see connectExternalDevice.
std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(audioPort->id); std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(audioPort->id);
if (routes.empty()) { if (routes.empty()) {
@@ -87,6 +144,7 @@ ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* au
RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort)); RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort));
} }
audioPort->profiles = mixPort.profiles; audioPort->profiles = mixPort.profiles;
LOG(DEBUG) << __func__ << ": populated from the mix port as: " << audioPort->toString();
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }

View File

@@ -43,26 +43,10 @@ StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& m
mStreamConfig.sampleRate = context->getSampleRate(); mStreamConfig.sampleRate = context->getSampleRate();
} }
std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::sSubmixRoutes;
::android::status_t StreamRemoteSubmix::init() { ::android::status_t StreamRemoteSubmix::init() {
{ mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig);
std::lock_guard guard(sSubmixRoutesLock); if (mCurrentRoute == nullptr) {
auto routeItr = sSubmixRoutes.find(mDeviceAddress); return ::android::NO_INIT;
if (routeItr != sSubmixRoutes.end()) {
mCurrentRoute = routeItr->second;
}
// If route is not available for this port, add it.
if (mCurrentRoute == nullptr) {
// Initialize the pipe.
mCurrentRoute = std::make_shared<SubmixRoute>();
if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) {
LOG(ERROR) << __func__ << ": create pipe failed";
return ::android::NO_INIT;
}
sSubmixRoutes.emplace(mDeviceAddress, mCurrentRoute);
}
} }
if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) { if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
LOG(ERROR) << __func__ << ": invalid stream config"; LOG(ERROR) << __func__ << ": invalid stream config";
@@ -80,7 +64,6 @@ std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::s
return ::android::NO_INIT; return ::android::NO_INIT;
} }
} }
mCurrentRoute->openStream(mIsInput); mCurrentRoute->openStream(mIsInput);
return ::android::OK; return ::android::OK;
} }
@@ -114,14 +97,7 @@ std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::s
ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() { ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
if (!mIsInput) { if (!mIsInput) {
std::shared_ptr<SubmixRoute> route = nullptr; std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(mDeviceAddress);
{
std::lock_guard guard(sSubmixRoutesLock);
auto routeItr = sSubmixRoutes.find(mDeviceAddress);
if (routeItr != sSubmixRoutes.end()) {
route = routeItr->second;
}
}
if (route != nullptr) { if (route != nullptr) {
sp<MonoPipe> sink = route->getSink(); sp<MonoPipe> sink = route->getSink();
if (sink == nullptr) { if (sink == nullptr) {
@@ -148,9 +124,7 @@ void StreamRemoteSubmix::shutdown() {
if (!mCurrentRoute->hasAtleastOneStreamOpen()) { if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
mCurrentRoute->releasePipe(); mCurrentRoute->releasePipe();
LOG(DEBUG) << __func__ << ": pipe destroyed"; LOG(DEBUG) << __func__ << ": pipe destroyed";
SubmixRoute::removeRoute(mDeviceAddress);
std::lock_guard guard(sSubmixRoutesLock);
sSubmixRoutes.erase(mDeviceAddress);
} }
mCurrentRoute.reset(); mCurrentRoute.reset();
} }
@@ -201,7 +175,7 @@ long StreamRemoteSubmix::getDelayInUsForFrameCount(size_t frameCount) {
// Calculate the maximum size of the pipe buffer in frames for the specified stream. // Calculate the maximum size of the pipe buffer in frames for the specified stream.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
auto pipeConfig = mCurrentRoute->mPipeConfig; auto pipeConfig = mCurrentRoute->getPipeConfig();
const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize); const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize; return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
} }

View File

@@ -23,9 +23,49 @@
#include "SubmixRoute.h" #include "SubmixRoute.h"
using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioDeviceAddress;
namespace aidl::android::hardware::audio::core::r_submix { namespace aidl::android::hardware::audio::core::r_submix {
// static
SubmixRoute::RoutesMonitor SubmixRoute::getRoutes() {
static std::mutex submixRoutesLock;
static RoutesMap submixRoutes;
return RoutesMonitor(submixRoutesLock, submixRoutes);
}
// static
std::shared_ptr<SubmixRoute> SubmixRoute::findOrCreateRoute(const AudioDeviceAddress& deviceAddress,
const AudioConfig& pipeConfig) {
auto routes = getRoutes();
auto routeItr = routes->find(deviceAddress);
if (routeItr != routes->end()) {
return routeItr->second;
}
auto route = std::make_shared<SubmixRoute>();
if (::android::OK != route->createPipe(pipeConfig)) {
LOG(ERROR) << __func__ << ": create pipe failed";
return nullptr;
}
routes->emplace(deviceAddress, route);
return route;
}
// static
std::shared_ptr<SubmixRoute> SubmixRoute::findRoute(const AudioDeviceAddress& deviceAddress) {
auto routes = getRoutes();
auto routeItr = routes->find(deviceAddress);
if (routeItr != routes->end()) {
return routeItr->second;
}
return nullptr;
}
// static
void SubmixRoute::removeRoute(const AudioDeviceAddress& deviceAddress) {
getRoutes()->erase(deviceAddress);
}
// Verify a submix input or output stream can be opened. // Verify a submix input or output stream can be opened.
bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) { bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) {
// If the stream is already open, don't open it again. // If the stream is already open, don't open it again.
@@ -44,6 +84,7 @@ bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamCon
// Compare this stream config with existing pipe config, returning false if they do *not* // Compare this stream config with existing pipe config, returning false if they do *not*
// match, true otherwise. // match, true otherwise.
bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) { bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) {
std::lock_guard guard(mLock);
if (streamConfig.channelLayout != mPipeConfig.channelLayout) { if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = " LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
<< streamConfig.channelLayout.toString() << streamConfig.channelLayout.toString()
@@ -162,17 +203,14 @@ void SubmixRoute::closeStream(bool isInput) {
LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index; LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index;
return ::android::BAD_INDEX; return ::android::BAD_INDEX;
} }
LOG(VERBOSE) << __func__ << ": created pipe"; LOG(VERBOSE) << __func__ << ": Pipe frame size : " << streamConfig.frameSize
<< ", pipe frames : " << sink->maxFrames();
mPipeConfig = streamConfig;
mPipeConfig.frameCount = sink->maxFrames();
LOG(VERBOSE) << __func__ << ": Pipe frame size : " << mPipeConfig.frameSize
<< ", pipe frames : " << mPipeConfig.frameCount;
// Save references to the source and sink. // Save references to the source and sink.
{ {
std::lock_guard guard(mLock); std::lock_guard guard(mLock);
mPipeConfig = streamConfig;
mPipeConfig.frameCount = sink->maxFrames();
mSink = std::move(sink); mSink = std::move(sink);
mSource = std::move(source); mSource = std::move(source);
} }
@@ -181,15 +219,15 @@ void SubmixRoute::closeStream(bool isInput) {
} }
// Release references to the sink and source. // Release references to the sink and source.
void SubmixRoute::releasePipe() { AudioConfig SubmixRoute::releasePipe() {
std::lock_guard guard(mLock); std::lock_guard guard(mLock);
mSink.clear(); mSink.clear();
mSource.clear(); mSource.clear();
return mPipeConfig;
} }
::android::status_t SubmixRoute::resetPipe() { ::android::status_t SubmixRoute::resetPipe() {
releasePipe(); return createPipe(releasePipe());
return createPipe(mPipeConfig);
} }
void SubmixRoute::standby(bool isInput) { void SubmixRoute::standby(bool isInput) {

View File

@@ -25,6 +25,7 @@
#include <media/nbaio/MonoPipeReader.h> #include <media/nbaio/MonoPipeReader.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h> #include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceAddress.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h> #include <aidl/android/media/audio/common/AudioFormatDescription.h>
using aidl::android::media::audio::common::AudioChannelLayout; using aidl::android::media::audio::common::AudioChannelLayout;
@@ -60,7 +61,13 @@ struct AudioConfig {
class SubmixRoute { class SubmixRoute {
public: public:
AudioConfig mPipeConfig; static std::shared_ptr<SubmixRoute> findOrCreateRoute(
const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress,
const AudioConfig& pipeConfig);
static std::shared_ptr<SubmixRoute> findRoute(
const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress);
static void removeRoute(
const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress);
bool isStreamInOpen() { bool isStreamInOpen() {
std::lock_guard guard(mLock); std::lock_guard guard(mLock);
@@ -90,6 +97,10 @@ class SubmixRoute {
std::lock_guard guard(mLock); std::lock_guard guard(mLock);
return mSource; return mSource;
} }
AudioConfig getPipeConfig() {
std::lock_guard guard(mLock);
return mPipeConfig;
}
bool isStreamConfigValid(bool isInput, const AudioConfig& streamConfig); bool isStreamConfigValid(bool isInput, const AudioConfig& streamConfig);
void closeStream(bool isInput); void closeStream(bool isInput);
@@ -98,17 +109,31 @@ class SubmixRoute {
bool hasAtleastOneStreamOpen(); bool hasAtleastOneStreamOpen();
int notifyReadError(); int notifyReadError();
void openStream(bool isInput); void openStream(bool isInput);
void releasePipe(); AudioConfig releasePipe();
::android::status_t resetPipe(); ::android::status_t resetPipe();
bool shouldBlockWrite(); bool shouldBlockWrite();
void standby(bool isInput); void standby(bool isInput);
long updateReadCounterFrames(size_t frameCount); long updateReadCounterFrames(size_t frameCount);
private: private:
using RoutesMap = std::map<::aidl::android::media::audio::common::AudioDeviceAddress,
std::shared_ptr<r_submix::SubmixRoute>>;
class RoutesMonitor {
public:
RoutesMonitor(std::mutex& mutex, RoutesMap& routes) : mLock(mutex), mRoutes(routes) {}
RoutesMap* operator->() { return &mRoutes; }
private:
std::lock_guard<std::mutex> mLock;
RoutesMap& mRoutes;
};
static RoutesMonitor getRoutes();
bool isStreamConfigCompatible(const AudioConfig& streamConfig); bool isStreamConfigCompatible(const AudioConfig& streamConfig);
std::mutex mLock; std::mutex mLock;
AudioConfig mPipeConfig GUARDED_BY(mLock);
bool mStreamInOpen GUARDED_BY(mLock) = false; bool mStreamInOpen GUARDED_BY(mLock) = false;
int mInputRefCount GUARDED_BY(mLock) = 0; int mInputRefCount GUARDED_BY(mLock) = 0;
bool mStreamInStandby GUARDED_BY(mLock) = true; bool mStreamInStandby GUARDED_BY(mLock) = true;