Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b732895 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio r_submix: Suggest configuration from the peer

When there is a pipe established for a remote submix
device, suggest the configuration of the peer when opening
the other end.

Refactor SubmixRoute management to move it out from
StreamRemoteSubmix so that ModuleRemoteSubmix could also
use it.

Bug: 294962274
Test: atest audiorouting_tests
Change-Id: Ib31a662e7b65b92c614dc441a01160cae3485f3a
parent a92039ac
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -29,6 +29,10 @@ class ModuleRemoteSubmix : public Module {
    // IModule interfaces
    ndk::ScopedAStatus getMicMute(bool* _aidl_return) 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
    ndk::ScopedAStatus createInputStream(
+0 −8
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

#pragma once

#include <mutex>
#include <vector>

#include "core-impl/Stream.h"
@@ -56,13 +55,6 @@ class StreamRemoteSubmix : public StreamCommonImpl {
    r_submix::AudioConfig mStreamConfig;
    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
    static constexpr int kMaxReadErrorLogs = 5;
    // The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior
+59 −1
Original line number Diff line number Diff line
@@ -27,14 +27,36 @@

using aidl::android::hardware::audio::common::SinkMetadata;
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::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
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;

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) {
    LOG(DEBUG) << __func__ << ": is not supported";
    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);
}

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(
        StreamContext&& context, const SinkMetadata& sinkMetadata,
        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) {
    // 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.
    std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(audioPort->id);
    if (routes.empty()) {
@@ -87,6 +144,7 @@ ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* au
        RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort));
    }
    audioPort->profiles = mixPort.profiles;
    LOG(DEBUG) << __func__ << ": populated from the mix port as: " << audioPort->toString();
    return ndk::ScopedAStatus::ok();
}

+6 −32
Original line number Diff line number Diff line
@@ -43,27 +43,11 @@ StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& m
    mStreamConfig.sampleRate = context->getSampleRate();
}

std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::sSubmixRoutes;

::android::status_t StreamRemoteSubmix::init() {
    {
        std::lock_guard guard(sSubmixRoutesLock);
        auto routeItr = sSubmixRoutes.find(mDeviceAddress);
        if (routeItr != sSubmixRoutes.end()) {
            mCurrentRoute = routeItr->second;
        }
        // If route is not available for this port, add it.
    mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig);
    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)) {
        LOG(ERROR) << __func__ << ": invalid stream config";
        return ::android::NO_INIT;
@@ -80,7 +64,6 @@ std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::s
            return ::android::NO_INIT;
        }
    }

    mCurrentRoute->openStream(mIsInput);
    return ::android::OK;
}
@@ -114,14 +97,7 @@ std::map<AudioDeviceAddress, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::s

ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
    if (!mIsInput) {
        std::shared_ptr<SubmixRoute> route = nullptr;
        {
            std::lock_guard guard(sSubmixRoutesLock);
            auto routeItr = sSubmixRoutes.find(mDeviceAddress);
            if (routeItr != sSubmixRoutes.end()) {
                route = routeItr->second;
            }
        }
        std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(mDeviceAddress);
        if (route != nullptr) {
            sp<MonoPipe> sink = route->getSink();
            if (sink == nullptr) {
@@ -148,9 +124,7 @@ void StreamRemoteSubmix::shutdown() {
    if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
        mCurrentRoute->releasePipe();
        LOG(DEBUG) << __func__ << ": pipe destroyed";

        std::lock_guard guard(sSubmixRoutesLock);
        sSubmixRoutes.erase(mDeviceAddress);
        SubmixRoute::removeRoute(mDeviceAddress);
    }
    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.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
    auto pipeConfig = mCurrentRoute->mPipeConfig;
    auto pipeConfig = mCurrentRoute->getPipeConfig();
    const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
    return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
}
+48 −10
Original line number Diff line number Diff line
@@ -23,9 +23,49 @@
#include "SubmixRoute.h"

using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioDeviceAddress;

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.
bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) {
    // 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*
// match, true otherwise.
bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) {
    std::lock_guard guard(mLock);
    if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
        LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
                   << streamConfig.channelLayout.toString()
@@ -162,17 +203,14 @@ void SubmixRoute::closeStream(bool isInput) {
        LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index;
        return ::android::BAD_INDEX;
    }
    LOG(VERBOSE) << __func__ << ": created pipe";

    mPipeConfig = streamConfig;
    mPipeConfig.frameCount = sink->maxFrames();

    LOG(VERBOSE) << __func__ << ": Pipe frame size : " << mPipeConfig.frameSize
                 << ", pipe frames : " << mPipeConfig.frameCount;
    LOG(VERBOSE) << __func__ << ": Pipe frame size : " << streamConfig.frameSize
                 << ", pipe frames : " << sink->maxFrames();

    // Save references to the source and sink.
    {
        std::lock_guard guard(mLock);
        mPipeConfig = streamConfig;
        mPipeConfig.frameCount = sink->maxFrames();
        mSink = std::move(sink);
        mSource = std::move(source);
    }
@@ -181,15 +219,15 @@ void SubmixRoute::closeStream(bool isInput) {
}

// Release references to the sink and source.
void SubmixRoute::releasePipe() {
AudioConfig SubmixRoute::releasePipe() {
    std::lock_guard guard(mLock);
    mSink.clear();
    mSource.clear();
    return mPipeConfig;
}

::android::status_t SubmixRoute::resetPipe() {
    releasePipe();
    return createPipe(mPipeConfig);
    return createPipe(releasePipe());
}

void SubmixRoute::standby(bool isInput) {
Loading