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

Commit 111e0cea authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Add support for compressed offload

- Add compressed offload mix port into default implementation.
- Require AudioOffloadInfo to be passed to IModule.openOutputStream
  for compressed offload port configs.
- Update VTS to handle compressed offload.

Bug: 205884982
Test: atest VtsHalAudioCoreTargetTest
Merged-In: I118b2c04bff12b64a7cac4dc2c88217a6a270046
Change-Id: I118b2c04bff12b64a7cac4dc2c88217a6a270046
(cherry picked from commit 975ea3ae)
parent 16db9b7c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -282,6 +282,8 @@ interface IModule {
     * @throws EX_ILLEGAL_ARGUMENT In the following cases:
     *                             - If the port config can not be found by the ID.
     *                             - If the port config is not of an output mix port.
     *                             - If the offload info is not provided for an offload
     *                               port configuration.
     * @throws EX_ILLEGAL_STATE In the following cases:
     *                          - If the port config already has a stream opened on it.
     *                          - If the limit on the open stream count for the port has
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ cc_library_static {
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libstagefright_foundation",
        "android.media.audio.common.types-V1-ndk",
        "android.hardware.audio.core-V1-ndk",
    ],
@@ -37,6 +38,7 @@ cc_binary {
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libstagefright_foundation",
        "android.media.audio.common.types-V1-ndk",
        "android.hardware.audio.core-V1-ndk",
    ],
+40 −9
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <media/stagefright/foundation/MediaDefs.h>

#include "core-impl/Configuration.h"

@@ -42,16 +43,30 @@ using aidl::android::media::audio::common::PcmType;

namespace aidl::android::hardware::audio::core::internal {

static void fillProfile(AudioProfile* profile, const std::vector<int32_t>& channelLayouts,
                        const std::vector<int32_t>& sampleRates) {
    for (auto layout : channelLayouts) {
        profile->channelMasks.push_back(
                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
    }
    profile->sampleRates.insert(profile->sampleRates.end(), sampleRates.begin(), sampleRates.end());
}

static AudioProfile createProfile(PcmType pcmType, const std::vector<int32_t>& channelLayouts,
                                  const std::vector<int32_t>& sampleRates) {
    AudioProfile profile;
    profile.format.type = AudioFormatType::PCM;
    profile.format.pcm = pcmType;
    for (auto layout : channelLayouts) {
        profile.channelMasks.push_back(
                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
    fillProfile(&profile, channelLayouts, sampleRates);
    return profile;
}
    profile.sampleRates.insert(profile.sampleRates.end(), sampleRates.begin(), sampleRates.end());

static AudioProfile createProfile(const std::string& encodingType,
                                  const std::vector<int32_t>& channelLayouts,
                                  const std::vector<int32_t>& sampleRates) {
    AudioProfile profile;
    profile.format.encoding = encodingType;
    fillProfile(&profile, channelLayouts, sampleRates);
    return profile;
}

@@ -125,6 +140,8 @@ static AudioRoute createRoute(const std::vector<int32_t>& sources, int32_t sink)
//  * "primary output", PRIMARY, 1 max open, 1 max active stream
//    - profile PCM 16-bit; MONO, STEREO; 44100, 48000
//    - profile PCM 24-bit; MONO, STEREO; 44100, 48000
//  * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
//    - profile MP3; MONO, STEREO; 44100, 48000
//  * "loopback output", stream count unlimited
//    - profile PCM 24-bit; STEREO; 48000
//  * "primary input", 2 max open, 2 max active streams
@@ -136,8 +153,8 @@ static AudioRoute createRoute(const std::vector<int32_t>& sources, int32_t sink)
//    - profile PCM 24-bit; STEREO; 48000
//
// Routes:
//  "primary out" -> "Null"
//  "primary out" -> "USB Out"
//  "primary out", "compressed offload" -> "Null"
//  "primary out", "compressed offload" -> "USB Out"
//  "loopback out" -> "Loopback Out"
//  "Zero", "USB In" -> "primary input"
//  "Loopback In" -> "loopback input"
@@ -183,6 +200,18 @@ Configuration& getNullPrimaryConfiguration() {
                                      standardPcmAudioProfiles.end());
        c.ports.push_back(primaryOutMix);

        AudioPort compressedOffloadOutMix =
                createPort(c.nextPortId++, "compressed offload",
                           1 << static_cast<int32_t>(AudioOutputFlags::DIRECT) |
                                   1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD) |
                                   1 << static_cast<int32_t>(AudioOutputFlags::NON_BLOCKING),
                           false, createPortMixExt(1, 1));
        compressedOffloadOutMix.profiles.push_back(
                createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
                              {44100, 48000}));
        c.ports.push_back(compressedOffloadOutMix);

        AudioPort loopOutDevice = createPort(c.nextPortId++, "Loopback Out", 0, false,
                                             createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0));
        loopOutDevice.profiles.push_back(
@@ -244,8 +273,10 @@ Configuration& getNullPrimaryConfiguration() {
        c.ports.push_back(usbInDevice);
        c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;

        c.routes.push_back(createRoute({primaryOutMix.id}, nullOutDevice.id));
        c.routes.push_back(createRoute({primaryOutMix.id}, usbOutDevice.id));
        c.routes.push_back(
                createRoute({primaryOutMix.id, compressedOffloadOutMix.id}, nullOutDevice.id));
        c.routes.push_back(
                createRoute({primaryOutMix.id, compressedOffloadOutMix.id}, usbOutDevice.id));
        c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id));
        c.routes.push_back(createRoute({zeroInDevice.id, usbInDevice.id}, primaryInMix.id));
        c.routes.push_back(createRoute({loopInDevice.id}, loopInMix.id));
+8 −0
Original line number Diff line number Diff line
@@ -405,6 +405,14 @@ ndk::ScopedAStatus Module::openOutputStream(int32_t in_portConfigId,
                   << " does not correspond to an output mix port";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (portConfigIt->flags.has_value() &&
        ((portConfigIt->flags.value().get<AudioIoFlags::Tag::output>() &
          1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0) &&
        !in_offloadInfo.has_value()) {
        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
                   << " has COMPRESS_OFFLOAD flag set, requires offload info";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (mStreams.count(in_portConfigId) != 0) {
        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
                   << " already has a stream opened on it";
+49 −56
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */

#include <algorithm>
#include <chrono>

#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
@@ -22,19 +23,43 @@
#include "ModuleConfig.h"

using namespace android;
using namespace std::chrono_literals;

using aidl::android::hardware::audio::core::IModule;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioEncapsulationMode;
using aidl::android::media::audio::common::AudioFormatDescription;
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::AudioOutputFlags;
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::AudioUsage;
using aidl::android::media::audio::common::Int;

// static
std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
        const AudioPortConfig& portConfig) {
    if (portConfig.flags.has_value() &&
        portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
        (portConfig.flags.value().get<AudioIoFlags::Tag::output>() &
         1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0) {
        AudioOffloadInfo offloadInfo;
        offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
        offloadInfo.base.channelMask = portConfig.channelMask.value();
        offloadInfo.base.format = portConfig.format.value();
        offloadInfo.bitRatePerSecond = 256;                                // Arbitrary value.
        offloadInfo.durationUs = std::chrono::microseconds(1min).count();  // Arbitrary value.
        offloadInfo.usage = AudioUsage::MEDIA;
        offloadInfo.encapsulationMode = AudioEncapsulationMode::NONE;
        return offloadInfo;
    }
    return {};
}

template <typename T>
auto findById(const std::vector<T>& v, int32_t id) {
    return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
@@ -264,10 +289,10 @@ std::string ModuleConfig::toString() const {
    return result;
}

static std::vector<AudioPortConfig> combineAudioConfigs(const AudioPort& port,
                                                        const AudioProfile& profile) {
    std::vector<AudioPortConfig> configs;
    configs.reserve(profile.channelMasks.size() * profile.sampleRates.size());
static size_t combineAudioConfigs(const AudioPort& port, const AudioProfile& profile,
                                  std::vector<AudioPortConfig>* result) {
    const size_t newConfigCount = profile.channelMasks.size() * profile.sampleRates.size();
    result->reserve(result->capacity() + newConfigCount);
    for (auto channelMask : profile.channelMasks) {
        for (auto sampleRate : profile.sampleRates) {
            AudioPortConfig config{};
@@ -277,66 +302,32 @@ static std::vector<AudioPortConfig> combineAudioConfigs(const AudioPort& port,
            config.sampleRate = sr;
            config.channelMask = channelMask;
            config.format = profile.format;
            config.flags = port.flags;
            config.ext = port.ext;
            configs.push_back(config);
            result->push_back(std::move(config));
        }
    }
    return configs;
    return newConfigCount;
}

std::vector<AudioPortConfig> ModuleConfig::generateInputAudioMixPortConfigs(
        const std::vector<AudioPort>& ports, bool singleProfile) const {
    std::vector<AudioPortConfig> result;
    for (const auto& mixPort : ports) {
        if (getAttachedSourceDevicesPortsForMixPort(mixPort).empty()) {
            continue;  // no attached devices
        }
        for (const auto& profile : mixPort.profiles) {
            if (profile.format.type == AudioFormatType::DEFAULT || profile.sampleRates.empty() ||
                profile.channelMasks.empty()) {
                continue;  // dynamic profile
            }
            auto configs = combineAudioConfigs(mixPort, profile);
            for (auto& config : configs) {
                config.flags = mixPort.flags;
                result.push_back(config);
                if (singleProfile) return result;
            }
        }
    }
    return result;
}

static std::tuple<AudioIoFlags, bool> generateOutFlags(const AudioPort& mixPort) {
    static const AudioIoFlags offloadFlags = AudioIoFlags::make<AudioIoFlags::Tag::output>(
            (1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD)) |
            (1 << static_cast<int>(AudioOutputFlags::DIRECT)));
    const bool isOffload = (mixPort.flags.get<AudioIoFlags::Tag::output>() &
                            (1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD))) != 0;
    return {isOffload ? offloadFlags : mixPort.flags, isOffload};
static bool isDynamicProfile(const AudioProfile& profile) {
    return (profile.format.type == AudioFormatType::DEFAULT && profile.format.encoding.empty()) ||
           profile.sampleRates.empty() || profile.channelMasks.empty();
}

std::vector<AudioPortConfig> ModuleConfig::generateOutputAudioMixPortConfigs(
        const std::vector<AudioPort>& ports, bool singleProfile) const {
std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
        const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
    std::vector<AudioPortConfig> result;
    for (const auto& mixPort : ports) {
        if (getAttachedSinkDevicesPortsForMixPort(mixPort).empty()) {
            continue;  // no attached devices
        if (getAttachedDevicesPortsForMixPort(isInput, mixPort).empty()) {
            continue;
        }
        auto [flags, isOffload] = generateOutFlags(mixPort);
        (void)isOffload;
        for (const auto& profile : mixPort.profiles) {
            if (profile.format.type == AudioFormatType::DEFAULT) continue;
            auto configs = combineAudioConfigs(mixPort, profile);
            for (auto& config : configs) {
                // Some combinations of flags declared in the config file require special
                // treatment.
                // if (isOffload) {
                //     config.offloadInfo.info(generateOffloadInfo(config.base));
                // }
                config.flags = flags;
                result.push_back(config);
                if (singleProfile) return result;
            if (isDynamicProfile(profile)) continue;
            combineAudioConfigs(mixPort, profile, &result);
            if (singleProfile && !result.empty()) {
                result.resize(1);
                return result;
            }
        }
    }
@@ -349,9 +340,11 @@ std::vector<AudioPortConfig> ModuleConfig::generateAudioDevicePortConfigs(
    for (const auto& devicePort : ports) {
        const size_t resultSizeBefore = result.size();
        for (const auto& profile : devicePort.profiles) {
            auto configs = combineAudioConfigs(devicePort, profile);
            result.insert(result.end(), configs.begin(), configs.end());
            if (singleProfile && !result.empty()) return result;
            combineAudioConfigs(devicePort, profile, &result);
            if (singleProfile && !result.empty()) {
                result.resize(1);
                return result;
            }
        }
        if (resultSizeBefore == result.size()) {
            std::copy_if(mInitialConfigs.begin(), mInitialConfigs.end(), std::back_inserter(result),
Loading