sysbta: Sync with r29
* Currently not working on my MTK devices. Investigation ongoing.
This commit is contained in:
parent
9fb62e9fad
commit
ddcc0a5fed
73
bluetooth/audio/hal/A2dpBits.h
Normal file
73
bluetooth/audio/hal/A2dpBits.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
class A2dpBits {
|
||||
const uint8_t* cdata_;
|
||||
uint8_t* data_;
|
||||
|
||||
public:
|
||||
A2dpBits(const std::vector<uint8_t>& vector) : cdata_(vector.data()) {}
|
||||
|
||||
A2dpBits(std::vector<uint8_t>& vector)
|
||||
: cdata_(vector.data()), data_(vector.data()) {}
|
||||
|
||||
struct Range {
|
||||
const int first, len;
|
||||
constexpr Range(int first, int last)
|
||||
: first(first), len(last - first + 1) {}
|
||||
constexpr Range(int index) : first(index), len(1) {}
|
||||
};
|
||||
|
||||
constexpr bool get(int bit) const {
|
||||
return (cdata_[bit >> 3] >> (7 - (bit & 7))) & 1;
|
||||
}
|
||||
|
||||
constexpr unsigned get(const Range& range) const {
|
||||
unsigned v(0);
|
||||
for (int i = 0; i < range.len; i++)
|
||||
v |= get(range.first + i) << ((range.len - 1) - i);
|
||||
return v;
|
||||
}
|
||||
|
||||
constexpr void set(int bit, int value = 1) {
|
||||
uint8_t m = 1 << (7 - (bit & 7));
|
||||
if (value)
|
||||
data_[bit >> 3] |= m;
|
||||
else
|
||||
data_[bit >> 3] &= ~m;
|
||||
}
|
||||
|
||||
constexpr void set(const Range& range, int value) {
|
||||
for (int i = 0; i < range.len; i++)
|
||||
set(range.first + i, (value >> ((range.len - 1) - i)) & 1);
|
||||
}
|
||||
|
||||
constexpr int find_active_bit(const Range& range) const {
|
||||
unsigned v = get(range);
|
||||
int i = 0;
|
||||
for (; i < range.len && ((v >> i) & 1) == 0; i++)
|
||||
;
|
||||
return i < range.len && (v ^ (1 << i)) == 0
|
||||
? range.first + (range.len - 1) - i
|
||||
: -1;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
|
@ -22,6 +22,10 @@
|
|||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "A2dpOffloadCodecAac.h"
|
||||
#include "A2dpOffloadCodecFactory.h"
|
||||
#include "A2dpOffloadCodecSbc.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
|
@ -48,19 +52,44 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession(
|
|||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::a2dpConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
||||
session_type_, audio_config.get<AudioConfiguration::a2dpConfig>())) {
|
||||
if (audio_config.getTag() == AudioConfiguration::Tag::a2dp) {
|
||||
auto a2dp_config = audio_config.get<AudioConfiguration::Tag::a2dp>();
|
||||
A2dpStatus a2dp_status = A2dpStatus::NOT_SUPPORTED_CODEC_TYPE;
|
||||
|
||||
if (a2dp_config.codecId ==
|
||||
A2dpOffloadCodecSbc::GetInstance()->GetCodecId()) {
|
||||
SbcParameters sbc_parameters;
|
||||
a2dp_status = A2dpOffloadCodecSbc::GetInstance()->ParseConfiguration(
|
||||
a2dp_config.configuration, &sbc_parameters);
|
||||
|
||||
} else if (a2dp_config.codecId ==
|
||||
A2dpOffloadCodecAac::GetInstance()->GetCodecId()) {
|
||||
AacParameters aac_parameters;
|
||||
a2dp_status = A2dpOffloadCodecAac::GetInstance()->ParseConfiguration(
|
||||
a2dp_config.configuration, &aac_parameters);
|
||||
}
|
||||
if (a2dp_status != A2dpStatus::OK) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
} else if (audio_config.getTag() == AudioConfiguration::Tag::a2dpConfig) {
|
||||
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
||||
session_type_,
|
||||
audio_config.get<AudioConfiguration::a2dpConfig>())) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
@ -73,6 +102,36 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady(
|
|||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration(
|
||||
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) {
|
||||
auto codec = A2dpOffloadCodecFactory::GetInstance()->GetCodec(codec_id);
|
||||
if (!codec) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " - CodecId=" << codec_id.toString() << " is not found";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
*_aidl_return = codec->ParseConfiguration(configuration, codec_parameters);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpOffloadAudioProvider::getA2dpConfiguration(
|
||||
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||
const A2dpConfigurationHint& hint,
|
||||
std::optional<audio::A2dpConfiguration>* _aidl_return) {
|
||||
*_aidl_return = std::nullopt;
|
||||
A2dpConfiguration avdtp_configuration;
|
||||
|
||||
if (A2dpOffloadCodecFactory::GetInstance()->GetConfiguration(
|
||||
remote_a2dp_capabilities, hint, &avdtp_configuration))
|
||||
*_aidl_return =
|
||||
std::make_optional<A2dpConfiguration>(std::move(avdtp_configuration));
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
|
|
|
@ -34,7 +34,16 @@ class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
|
|||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
DataMQDesc* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus parseA2dpConfiguration(
|
||||
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus getA2dpConfiguration(
|
||||
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||
const A2dpConfigurationHint& hint,
|
||||
std::optional<audio::A2dpConfiguration>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
|
|
47
bluetooth/audio/hal/A2dpOffloadCodec.h
Normal file
47
bluetooth/audio/hal/A2dpOffloadCodec.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/A2dpStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/ChannelMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecParameters.h>
|
||||
|
||||
#include "BluetoothAudioProviderFactory.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
class A2dpOffloadCodec {
|
||||
protected:
|
||||
A2dpOffloadCodec(const CodecInfo& info) : info(info) {}
|
||||
virtual ~A2dpOffloadCodec() {}
|
||||
|
||||
public:
|
||||
const CodecInfo& info;
|
||||
|
||||
const CodecId& GetCodecId() const { return info.id; }
|
||||
|
||||
virtual A2dpStatus ParseConfiguration(
|
||||
const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters) const = 0;
|
||||
|
||||
virtual bool BuildConfiguration(
|
||||
const std::vector<uint8_t>& remote_capabilities,
|
||||
const std::optional<CodecParameters>& hint,
|
||||
std::vector<uint8_t>* configuration) const = 0;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
378
bluetooth/audio/hal/A2dpOffloadCodecAac.cpp
Normal file
378
bluetooth/audio/hal/A2dpOffloadCodecAac.cpp
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "A2dpOffloadCodecAac.h"
|
||||
|
||||
#include "A2dpBits.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
/**
|
||||
* AAC Local Capabilities
|
||||
*/
|
||||
|
||||
enum : bool {
|
||||
kEnableObjectTypeMpeg2AacLc = true,
|
||||
kEnableObjectTypeMpeg4AacLc = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableSamplingFrequency44100 = true,
|
||||
kEnableSamplingFrequency48000 = true,
|
||||
kEnableSamplingFrequency88200 = false,
|
||||
kEnableSamplingFrequency96000 = false,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableChannels1 = true,
|
||||
kEnableChannels2 = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableVbrSupported = true,
|
||||
};
|
||||
|
||||
enum : int {
|
||||
kBitdepth = 24,
|
||||
};
|
||||
|
||||
/**
|
||||
* AAC Signaling format [A2DP - 4.5]
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
constexpr A2dpBits::Range kObjectType ( 0, 6 );
|
||||
constexpr A2dpBits::Range kDrcEnable ( 7 );
|
||||
constexpr A2dpBits::Range kSamplingFrequency ( 8, 19 );
|
||||
constexpr A2dpBits::Range kChannels ( 20, 23 );
|
||||
constexpr A2dpBits::Range kVbrSupported ( 24 );
|
||||
constexpr A2dpBits::Range kBitrate ( 25, 47 );
|
||||
constexpr size_t kCapabilitiesSize = 48/8;
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum {
|
||||
kObjectTypeMpeg2AacLc = kObjectType.first,
|
||||
kObjectTypeMpeg4AacLc,
|
||||
kObjectTypeMpeg4AacLtp,
|
||||
kObjectTypeMpeg4AacScalable,
|
||||
kObjectTypeMpeg4AacHeV1,
|
||||
kObjectTypeMpeg4AacHeV2,
|
||||
kObjectTypeMpeg4AacEldV2
|
||||
};
|
||||
|
||||
enum {
|
||||
kSamplingFrequency8000 = kSamplingFrequency.first,
|
||||
kSamplingFrequency11025,
|
||||
kSamplingFrequency12000,
|
||||
kSamplingFrequency16000,
|
||||
kSamplingFrequency22050,
|
||||
kSamplingFrequency24000,
|
||||
kSamplingFrequency32000,
|
||||
kSamplingFrequency44100,
|
||||
kSamplingFrequency48000,
|
||||
kSamplingFrequency64000,
|
||||
kSamplingFrequency88200,
|
||||
kSamplingFrequency96000
|
||||
};
|
||||
|
||||
enum { kChannels1 = kChannels.first, kChannels2, kChannels51, kChannels71 };
|
||||
|
||||
/**
|
||||
* AAC Conversion functions
|
||||
*/
|
||||
|
||||
static AacParameters::ObjectType GetObjectTypeEnum(int object_type) {
|
||||
switch (object_type) {
|
||||
case kObjectTypeMpeg2AacLc:
|
||||
return AacParameters::ObjectType::MPEG2_AAC_LC;
|
||||
case kObjectTypeMpeg4AacLc:
|
||||
default:
|
||||
return AacParameters::ObjectType::MPEG4_AAC_LC;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetSamplingFrequencyBit(int32_t sampling_frequency) {
|
||||
switch (sampling_frequency) {
|
||||
case 8000:
|
||||
return kSamplingFrequency8000;
|
||||
case 11025:
|
||||
return kSamplingFrequency11025;
|
||||
case 12000:
|
||||
return kSamplingFrequency12000;
|
||||
case 16000:
|
||||
return kSamplingFrequency16000;
|
||||
case 22050:
|
||||
return kSamplingFrequency22050;
|
||||
case 24000:
|
||||
return kSamplingFrequency24000;
|
||||
case 32000:
|
||||
return kSamplingFrequency32000;
|
||||
case 44100:
|
||||
return kSamplingFrequency44100;
|
||||
case 48000:
|
||||
return kSamplingFrequency48000;
|
||||
case 64000:
|
||||
return kSamplingFrequency64000;
|
||||
case 88200:
|
||||
return kSamplingFrequency88200;
|
||||
case 96000:
|
||||
return kSamplingFrequency96000;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetSamplingFrequencyValue(int sampling_frequency) {
|
||||
switch (sampling_frequency) {
|
||||
case kSamplingFrequency8000:
|
||||
return 8000;
|
||||
case kSamplingFrequency11025:
|
||||
return 11025;
|
||||
case kSamplingFrequency12000:
|
||||
return 12000;
|
||||
case kSamplingFrequency16000:
|
||||
return 16000;
|
||||
case kSamplingFrequency22050:
|
||||
return 22050;
|
||||
case kSamplingFrequency24000:
|
||||
return 24000;
|
||||
case kSamplingFrequency32000:
|
||||
return 32000;
|
||||
case kSamplingFrequency44100:
|
||||
return 44100;
|
||||
case kSamplingFrequency48000:
|
||||
return 48000;
|
||||
case kSamplingFrequency64000:
|
||||
return 64000;
|
||||
case kSamplingFrequency88200:
|
||||
return 88200;
|
||||
case kSamplingFrequency96000:
|
||||
return 96000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetChannelsBit(ChannelMode channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case ChannelMode::MONO:
|
||||
return kChannels1;
|
||||
case ChannelMode::STEREO:
|
||||
return kChannels2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static ChannelMode GetChannelModeEnum(int channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case kChannels1:
|
||||
return ChannelMode::MONO;
|
||||
case kChannels2:
|
||||
return ChannelMode::STEREO;
|
||||
default:
|
||||
return ChannelMode::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AAC Class implementation
|
||||
*/
|
||||
|
||||
const A2dpOffloadCodecAac* A2dpOffloadCodecAac::GetInstance() {
|
||||
static A2dpOffloadCodecAac instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
A2dpOffloadCodecAac::A2dpOffloadCodecAac()
|
||||
: A2dpOffloadCodec(info_),
|
||||
info_({.id = CodecId(CodecId::A2dp::AAC), .name = "AAC"}) {
|
||||
info_.transport.set<CodecInfo::Transport::Tag::a2dp>();
|
||||
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
/* --- Setup Capabilities --- */
|
||||
|
||||
a2dp_info.capabilities.resize(kCapabilitiesSize);
|
||||
std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0);
|
||||
|
||||
auto capabilities = A2dpBits(a2dp_info.capabilities);
|
||||
|
||||
capabilities.set(kObjectTypeMpeg2AacLc, kEnableObjectTypeMpeg2AacLc);
|
||||
capabilities.set(kObjectTypeMpeg4AacLc, kEnableObjectTypeMpeg4AacLc);
|
||||
|
||||
capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100);
|
||||
capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000);
|
||||
capabilities.set(kSamplingFrequency88200, kEnableSamplingFrequency88200);
|
||||
capabilities.set(kSamplingFrequency96000, kEnableSamplingFrequency96000);
|
||||
|
||||
capabilities.set(kChannels1, kEnableChannels1);
|
||||
capabilities.set(kChannels2, kEnableChannels2);
|
||||
|
||||
capabilities.set(kVbrSupported, kEnableVbrSupported);
|
||||
|
||||
/* --- Setup Sampling Frequencies --- */
|
||||
|
||||
auto& sampling_frequency = a2dp_info.samplingFrequencyHz;
|
||||
|
||||
for (auto v : {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
||||
64000, 88200, 96000})
|
||||
if (capabilities.get(GetSamplingFrequencyBit(int32_t(v))))
|
||||
sampling_frequency.push_back(v);
|
||||
|
||||
/* --- Setup Channel Modes --- */
|
||||
|
||||
auto& channel_modes = a2dp_info.channelMode;
|
||||
|
||||
for (auto v : {ChannelMode::MONO, ChannelMode::STEREO})
|
||||
if (capabilities.get(GetChannelsBit(v))) channel_modes.push_back(v);
|
||||
|
||||
/* --- Setup Bitdepth --- */
|
||||
|
||||
a2dp_info.bitdepth.push_back(kBitdepth);
|
||||
}
|
||||
|
||||
A2dpStatus A2dpOffloadCodecAac::ParseConfiguration(
|
||||
const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters, AacParameters* aac_parameters) const {
|
||||
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
if (configuration.size() != a2dp_info.capabilities.size())
|
||||
return A2dpStatus::BAD_LENGTH;
|
||||
|
||||
auto config = A2dpBits(configuration);
|
||||
auto lcaps = A2dpBits(a2dp_info.capabilities);
|
||||
|
||||
/* --- Check Object Type --- */
|
||||
|
||||
int object_type = config.find_active_bit(kObjectType);
|
||||
if (object_type < 0) return A2dpStatus::INVALID_OBJECT_TYPE;
|
||||
if (!lcaps.get(object_type)) return A2dpStatus::NOT_SUPPORTED_OBJECT_TYPE;
|
||||
|
||||
/* --- Check Sampling Frequency --- */
|
||||
|
||||
int sampling_frequency = config.find_active_bit(kSamplingFrequency);
|
||||
if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY;
|
||||
if (!lcaps.get(sampling_frequency))
|
||||
return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY;
|
||||
|
||||
/* --- Check Channels --- */
|
||||
|
||||
int channels = config.find_active_bit(kChannels);
|
||||
if (channels < 0) return A2dpStatus::INVALID_CHANNELS;
|
||||
if (!lcaps.get(channels)) return A2dpStatus::NOT_SUPPORTED_CHANNELS;
|
||||
|
||||
/* --- Check Bitrate --- */
|
||||
|
||||
bool vbr = config.get(kVbrSupported);
|
||||
if (vbr && !lcaps.get(kVbrSupported)) return A2dpStatus::NOT_SUPPORTED_VBR;
|
||||
|
||||
int bitrate = config.get(kBitrate);
|
||||
if (vbr && lcaps.get(kBitrate) && bitrate > lcaps.get(kBitrate))
|
||||
return A2dpStatus::NOT_SUPPORTED_BIT_RATE;
|
||||
|
||||
/* --- Return --- */
|
||||
|
||||
codec_parameters->channelMode = GetChannelModeEnum(channels);
|
||||
codec_parameters->samplingFrequencyHz =
|
||||
GetSamplingFrequencyValue(sampling_frequency);
|
||||
codec_parameters->bitdepth = kBitdepth;
|
||||
|
||||
codec_parameters->minBitrate = vbr ? 0 : bitrate;
|
||||
codec_parameters->maxBitrate = bitrate;
|
||||
|
||||
if (aac_parameters)
|
||||
aac_parameters->object_type = GetObjectTypeEnum(object_type);
|
||||
|
||||
return A2dpStatus::OK;
|
||||
}
|
||||
|
||||
bool A2dpOffloadCodecAac::BuildConfiguration(
|
||||
const std::vector<uint8_t>& remote_capabilities,
|
||||
const std::optional<CodecParameters>& hint,
|
||||
std::vector<uint8_t>* configuration) const {
|
||||
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false;
|
||||
|
||||
auto lcaps = A2dpBits(a2dp_info.capabilities);
|
||||
auto rcaps = A2dpBits(remote_capabilities);
|
||||
|
||||
configuration->resize(a2dp_info.capabilities.size());
|
||||
std::fill(begin(*configuration), end(*configuration), 0);
|
||||
auto config = A2dpBits(*configuration);
|
||||
|
||||
/* --- Select Object Type --- */
|
||||
|
||||
if (lcaps.get(kObjectTypeMpeg2AacLc) && rcaps.get(kObjectTypeMpeg2AacLc))
|
||||
config.set(kObjectTypeMpeg2AacLc);
|
||||
else if (lcaps.get(kObjectTypeMpeg4AacLc) && rcaps.get(kObjectTypeMpeg4AacLc))
|
||||
config.set(kObjectTypeMpeg4AacLc);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Sampling Frequency --- */
|
||||
|
||||
auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1;
|
||||
|
||||
if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint))
|
||||
config.set(sf_hint);
|
||||
else if (lcaps.get(kSamplingFrequency96000) &&
|
||||
rcaps.get(kSamplingFrequency96000))
|
||||
config.set(kSamplingFrequency96000);
|
||||
else if (lcaps.get(kSamplingFrequency88200) &&
|
||||
rcaps.get(kSamplingFrequency88200))
|
||||
config.set(kSamplingFrequency88200);
|
||||
else if (lcaps.get(kSamplingFrequency48000) &&
|
||||
rcaps.get(kSamplingFrequency48000))
|
||||
config.set(kSamplingFrequency48000);
|
||||
else if (lcaps.get(kSamplingFrequency44100) &&
|
||||
rcaps.get(kSamplingFrequency44100))
|
||||
config.set(kSamplingFrequency44100);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Channels --- */
|
||||
|
||||
auto ch_hint = hint ? GetChannelsBit(hint->channelMode) : -1;
|
||||
|
||||
if (ch_hint >= 0 && lcaps.get(ch_hint) && rcaps.get(ch_hint))
|
||||
config.set(ch_hint);
|
||||
else if (lcaps.get(kChannels2) && rcaps.get(kChannels2))
|
||||
config.set(kChannels2);
|
||||
else if (lcaps.get(kChannels1) && rcaps.get(kChannels1))
|
||||
config.set(kChannels1);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Bitrate --- */
|
||||
|
||||
if (!hint || hint->minBitrate == 0)
|
||||
config.set(kVbrSupported,
|
||||
lcaps.get(kVbrSupported) && rcaps.get(kVbrSupported));
|
||||
|
||||
int32_t bitrate = lcaps.get(kBitrate);
|
||||
if (hint && hint->maxBitrate > 0 && bitrate)
|
||||
bitrate = std::min(hint->maxBitrate, bitrate);
|
||||
else if (hint && hint->maxBitrate > 0)
|
||||
bitrate = hint->maxBitrate;
|
||||
config.set(kBitrate, bitrate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
57
bluetooth/audio/hal/A2dpOffloadCodecAac.h
Normal file
57
bluetooth/audio/hal/A2dpOffloadCodecAac.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "A2dpOffloadCodec.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
struct AacParameters : public CodecParameters {
|
||||
enum class ObjectType { MPEG2_AAC_LC, MPEG4_AAC_LC };
|
||||
|
||||
ObjectType object_type;
|
||||
};
|
||||
|
||||
class A2dpOffloadCodecAac : public A2dpOffloadCodec {
|
||||
CodecInfo info_;
|
||||
|
||||
A2dpOffloadCodecAac();
|
||||
|
||||
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters,
|
||||
AacParameters* aac_parameters) const;
|
||||
|
||||
public:
|
||||
static const A2dpOffloadCodecAac* GetInstance();
|
||||
|
||||
A2dpStatus ParseConfiguration(
|
||||
const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters) const override {
|
||||
return ParseConfiguration(configuration, codec_parameters, nullptr);
|
||||
}
|
||||
|
||||
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||
AacParameters* aac_parameters) const {
|
||||
return ParseConfiguration(configuration, aac_parameters, aac_parameters);
|
||||
}
|
||||
|
||||
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
|
||||
const std::optional<CodecParameters>& hint,
|
||||
std::vector<uint8_t>* configuration) const override;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
100
bluetooth/audio/hal/A2dpOffloadCodecFactory.cpp
Normal file
100
bluetooth/audio/hal/A2dpOffloadCodecFactory.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "A2dpOffloadCodecFactory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "A2dpOffloadCodecAac.h"
|
||||
#include "A2dpOffloadCodecSbc.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
/**
|
||||
* Local Capabilities Configuration
|
||||
*/
|
||||
|
||||
enum : bool {
|
||||
kEnableAac = true,
|
||||
kEnableSbc = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Class implementation
|
||||
*/
|
||||
|
||||
const A2dpOffloadCodecFactory* A2dpOffloadCodecFactory::GetInstance() {
|
||||
static A2dpOffloadCodecFactory instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
A2dpOffloadCodecFactory::A2dpOffloadCodecFactory()
|
||||
: name("Offload"), codecs(ranked_codecs_) {
|
||||
ranked_codecs_.reserve(kEnableAac + kEnableSbc);
|
||||
|
||||
if (kEnableAac) ranked_codecs_.push_back(A2dpOffloadCodecAac::GetInstance());
|
||||
if (kEnableSbc) ranked_codecs_.push_back(A2dpOffloadCodecSbc::GetInstance());
|
||||
}
|
||||
|
||||
const A2dpOffloadCodec* A2dpOffloadCodecFactory::GetCodec(CodecId id) const {
|
||||
auto codec = std::find_if(begin(ranked_codecs_), end(ranked_codecs_),
|
||||
[&](auto c) { return id == c->info.id; });
|
||||
|
||||
return codec != end(ranked_codecs_) ? *codec : nullptr;
|
||||
}
|
||||
|
||||
bool A2dpOffloadCodecFactory::GetConfiguration(
|
||||
const std::vector<A2dpRemoteCapabilities>& remote_capabilities,
|
||||
const A2dpConfigurationHint& hint, A2dpConfiguration* configuration) const {
|
||||
decltype(ranked_codecs_) codecs;
|
||||
|
||||
codecs.reserve(ranked_codecs_.size());
|
||||
|
||||
auto hinted_codec =
|
||||
std::find_if(begin(ranked_codecs_), end(ranked_codecs_),
|
||||
[&](auto c) { return hint.codecId == c->info.id; });
|
||||
|
||||
if (hinted_codec != end(ranked_codecs_)) codecs.push_back(*hinted_codec);
|
||||
|
||||
std::copy_if(begin(ranked_codecs_), end(ranked_codecs_),
|
||||
std::back_inserter(codecs),
|
||||
[&](auto c) { return c != *hinted_codec; });
|
||||
|
||||
for (auto codec : codecs) {
|
||||
auto rc =
|
||||
std::find_if(begin(remote_capabilities), end(remote_capabilities),
|
||||
[&](auto& rc__) { return codec->info.id == rc__.id; });
|
||||
|
||||
if ((rc == end(remote_capabilities)) ||
|
||||
!codec->BuildConfiguration(rc->capabilities, hint.codecParameters,
|
||||
&configuration->configuration))
|
||||
continue;
|
||||
|
||||
configuration->id = codec->info.id;
|
||||
A2dpStatus status = codec->ParseConfiguration(configuration->configuration,
|
||||
&configuration->parameters);
|
||||
assert(status == A2dpStatus::OK);
|
||||
|
||||
configuration->remoteSeid = rc->seid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
41
bluetooth/audio/hal/A2dpOffloadCodecFactory.h
Normal file
41
bluetooth/audio/hal/A2dpOffloadCodecFactory.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "A2dpOffloadCodec.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
class A2dpOffloadCodecFactory {
|
||||
std::vector<const A2dpOffloadCodec*> ranked_codecs_;
|
||||
|
||||
A2dpOffloadCodecFactory();
|
||||
|
||||
public:
|
||||
const std::string name;
|
||||
const std::vector<const A2dpOffloadCodec*>& codecs;
|
||||
|
||||
static const A2dpOffloadCodecFactory* GetInstance();
|
||||
|
||||
const A2dpOffloadCodec* GetCodec(CodecId id) const;
|
||||
|
||||
bool GetConfiguration(const std::vector<A2dpRemoteCapabilities>&,
|
||||
const A2dpConfigurationHint& hint,
|
||||
A2dpConfiguration* configuration) const;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
510
bluetooth/audio/hal/A2dpOffloadCodecSbc.cpp
Normal file
510
bluetooth/audio/hal/A2dpOffloadCodecSbc.cpp
Normal file
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "A2dpOffloadCodecSbc.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "A2dpBits.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
/**
|
||||
* SBC Local Capabilities
|
||||
*/
|
||||
|
||||
enum : bool {
|
||||
kEnableSamplingFrequency44100 = true,
|
||||
kEnableSamplingFrequency48000 = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableChannelModeMono = true,
|
||||
kEnableChannelModeDualChannel = true,
|
||||
kEnableChannelModeStereo = true,
|
||||
kEnableChannelModeJointStereo = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableBlockLength4 = true,
|
||||
kEnableBlockLength8 = true,
|
||||
kEnableBlockLength12 = true,
|
||||
kEnableBlockLength16 = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableSubbands4 = true,
|
||||
kEnableSubbands8 = true,
|
||||
};
|
||||
|
||||
enum : bool {
|
||||
kEnableAllocationMethodSnr = true,
|
||||
kEnableAllocationMethodLoudness = true,
|
||||
};
|
||||
|
||||
enum : uint8_t {
|
||||
kDefaultMinimumBitpool = 2,
|
||||
kDefaultMaximumBitpool = 250,
|
||||
};
|
||||
|
||||
enum : int {
|
||||
kBitdepth = 16,
|
||||
};
|
||||
|
||||
/**
|
||||
* SBC Signaling format [A2DP - 4.3]
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
constexpr A2dpBits::Range kSamplingFrequency ( 0, 3 );
|
||||
constexpr A2dpBits::Range kChannelMode ( 4, 7 );
|
||||
constexpr A2dpBits::Range kBlockLength ( 8, 11 );
|
||||
constexpr A2dpBits::Range kSubbands ( 12, 13 );
|
||||
constexpr A2dpBits::Range kAllocationMethod ( 14, 15 );
|
||||
constexpr A2dpBits::Range kMinimumBitpool ( 16, 23 );
|
||||
constexpr A2dpBits::Range kMaximumBitpool ( 24, 31 );
|
||||
constexpr size_t kCapabilitiesSize = 32/8;
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum {
|
||||
kSamplingFrequency16000 = kSamplingFrequency.first,
|
||||
kSamplingFrequency32000,
|
||||
kSamplingFrequency44100,
|
||||
kSamplingFrequency48000
|
||||
};
|
||||
|
||||
enum {
|
||||
kChannelModeMono = kChannelMode.first,
|
||||
kChannelModeDualChannel,
|
||||
kChannelModeStereo,
|
||||
kChannelModeJointStereo
|
||||
};
|
||||
|
||||
enum {
|
||||
kBlockLength4 = kBlockLength.first,
|
||||
kBlockLength8,
|
||||
kBlockLength12,
|
||||
kBlockLength16
|
||||
};
|
||||
|
||||
enum { kSubbands8 = kSubbands.first, kSubbands4 };
|
||||
|
||||
enum {
|
||||
kAllocationMethodSnr = kAllocationMethod.first,
|
||||
kAllocationMethodLoudness
|
||||
};
|
||||
|
||||
/**
|
||||
* SBC Conversion functions
|
||||
*/
|
||||
|
||||
static int GetSamplingFrequencyBit(int32_t sampling_frequency) {
|
||||
switch (sampling_frequency) {
|
||||
case 16000:
|
||||
return kSamplingFrequency16000;
|
||||
case 32000:
|
||||
return kSamplingFrequency32000;
|
||||
case 44100:
|
||||
return kSamplingFrequency44100;
|
||||
case 48000:
|
||||
return kSamplingFrequency48000;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetSamplingFrequencyValue(int sampling_frequency) {
|
||||
switch (sampling_frequency) {
|
||||
case kSamplingFrequency16000:
|
||||
return 16000;
|
||||
case kSamplingFrequency32000:
|
||||
return 32000;
|
||||
case kSamplingFrequency44100:
|
||||
return 44100;
|
||||
case kSamplingFrequency48000:
|
||||
return 48000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetChannelModeBit(ChannelMode channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case ChannelMode::STEREO:
|
||||
return kChannelModeJointStereo | kChannelModeStereo;
|
||||
case ChannelMode::DUALMONO:
|
||||
return kChannelModeDualChannel;
|
||||
case ChannelMode::MONO:
|
||||
return kChannelModeMono;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static ChannelMode GetChannelModeEnum(int channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case kChannelModeMono:
|
||||
return ChannelMode::MONO;
|
||||
case kChannelModeDualChannel:
|
||||
return ChannelMode::DUALMONO;
|
||||
case kChannelModeStereo:
|
||||
case kChannelModeJointStereo:
|
||||
return ChannelMode::STEREO;
|
||||
default:
|
||||
return ChannelMode::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetBlockLengthValue(int block_length) {
|
||||
switch (block_length) {
|
||||
case kBlockLength4:
|
||||
return 4;
|
||||
case kBlockLength8:
|
||||
return 8;
|
||||
case kBlockLength12:
|
||||
return 12;
|
||||
case kBlockLength16:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetSubbandsValue(int subbands) {
|
||||
switch (subbands) {
|
||||
case kSubbands4:
|
||||
return 4;
|
||||
case kSubbands8:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static SbcParameters::AllocationMethod GetAllocationMethodEnum(
|
||||
int allocation_method) {
|
||||
switch (allocation_method) {
|
||||
case kAllocationMethodSnr:
|
||||
return SbcParameters::AllocationMethod::SNR;
|
||||
case kAllocationMethodLoudness:
|
||||
default:
|
||||
return SbcParameters::AllocationMethod::LOUDNESS;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetSamplingFrequencyValue(const A2dpBits& configuration) {
|
||||
return GetSamplingFrequencyValue(
|
||||
configuration.find_active_bit(kSamplingFrequency));
|
||||
}
|
||||
|
||||
static int32_t GetBlockLengthValue(const A2dpBits& configuration) {
|
||||
return GetBlockLengthValue(configuration.find_active_bit(kBlockLength));
|
||||
}
|
||||
|
||||
static int32_t GetSubbandsValue(const A2dpBits& configuration) {
|
||||
return GetSubbandsValue(configuration.find_active_bit(kSubbands));
|
||||
}
|
||||
|
||||
static int GetFrameSize(const A2dpBits& configuration, int bitpool) {
|
||||
const int kSbcHeaderSize = 4;
|
||||
int subbands = GetSubbandsValue(configuration);
|
||||
int blocks = GetBlockLengthValue(configuration);
|
||||
|
||||
unsigned bits =
|
||||
((4 * subbands) << !configuration.get(kChannelModeMono)) +
|
||||
((blocks * bitpool) << configuration.get(kChannelModeDualChannel)) +
|
||||
((configuration.get(kChannelModeJointStereo) ? subbands : 0));
|
||||
|
||||
return kSbcHeaderSize + ((bits + 7) >> 3);
|
||||
}
|
||||
|
||||
static int GetBitrate(const A2dpBits& configuration, int bitpool) {
|
||||
int sampling_frequency = GetSamplingFrequencyValue(configuration);
|
||||
int subbands = GetSubbandsValue(configuration);
|
||||
int blocks = GetBlockLengthValue(configuration);
|
||||
int bits = 8 * GetFrameSize(configuration, bitpool);
|
||||
|
||||
return (bits * sampling_frequency) / (blocks * subbands);
|
||||
}
|
||||
|
||||
static uint8_t GetBitpool(const A2dpBits& configuration, int bitrate) {
|
||||
int bitpool = 0;
|
||||
|
||||
for (int i = 128; i; i >>= 1)
|
||||
if (bitrate > GetBitrate(configuration, bitpool + i)) {
|
||||
bitpool += i;
|
||||
}
|
||||
|
||||
return std::clamp(bitpool, 2, 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* SBC Class implementation
|
||||
*/
|
||||
|
||||
const A2dpOffloadCodecSbc* A2dpOffloadCodecSbc::GetInstance() {
|
||||
static A2dpOffloadCodecSbc instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
A2dpOffloadCodecSbc::A2dpOffloadCodecSbc()
|
||||
: A2dpOffloadCodec(info_),
|
||||
info_({.id = CodecId(CodecId::A2dp::SBC), .name = "SBC"}) {
|
||||
info_.transport.set<CodecInfo::Transport::Tag::a2dp>();
|
||||
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
/* --- Setup Capabilities --- */
|
||||
|
||||
a2dp_info.capabilities.resize(kCapabilitiesSize);
|
||||
std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0);
|
||||
|
||||
auto capabilities = A2dpBits(a2dp_info.capabilities);
|
||||
|
||||
capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100);
|
||||
capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000);
|
||||
|
||||
capabilities.set(kChannelModeMono, kEnableChannelModeMono);
|
||||
capabilities.set(kChannelModeDualChannel, kEnableChannelModeDualChannel);
|
||||
capabilities.set(kChannelModeStereo, kEnableChannelModeStereo);
|
||||
capabilities.set(kChannelModeJointStereo, kEnableChannelModeJointStereo);
|
||||
|
||||
capabilities.set(kBlockLength4, kEnableBlockLength4);
|
||||
capabilities.set(kBlockLength8, kEnableBlockLength8);
|
||||
capabilities.set(kBlockLength12, kEnableBlockLength12);
|
||||
capabilities.set(kBlockLength16, kEnableBlockLength16);
|
||||
|
||||
capabilities.set(kSubbands4, kEnableSubbands4);
|
||||
capabilities.set(kSubbands8, kEnableSubbands8);
|
||||
|
||||
capabilities.set(kSubbands4, kEnableSubbands4);
|
||||
capabilities.set(kSubbands8, kEnableSubbands8);
|
||||
|
||||
capabilities.set(kAllocationMethodSnr, kEnableAllocationMethodSnr);
|
||||
capabilities.set(kAllocationMethodLoudness, kEnableAllocationMethodLoudness);
|
||||
|
||||
capabilities.set(kMinimumBitpool, kDefaultMinimumBitpool);
|
||||
capabilities.set(kMaximumBitpool, kDefaultMaximumBitpool);
|
||||
|
||||
/* --- Setup Sampling Frequencies --- */
|
||||
|
||||
auto& sampling_frequency = a2dp_info.samplingFrequencyHz;
|
||||
|
||||
for (auto v : {16000, 32000, 44100, 48000})
|
||||
if (capabilities.get(GetSamplingFrequencyBit(int32_t(v))))
|
||||
sampling_frequency.push_back(v);
|
||||
|
||||
/* --- Setup Channel Modes --- */
|
||||
|
||||
auto& channel_modes = a2dp_info.channelMode;
|
||||
|
||||
for (auto v : {ChannelMode::MONO, ChannelMode::DUALMONO, ChannelMode::STEREO})
|
||||
if (capabilities.get(GetChannelModeBit(v))) channel_modes.push_back(v);
|
||||
|
||||
/* --- Setup Bitdepth --- */
|
||||
|
||||
a2dp_info.bitdepth.push_back(kBitdepth);
|
||||
}
|
||||
|
||||
A2dpStatus A2dpOffloadCodecSbc::ParseConfiguration(
|
||||
const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const {
|
||||
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
if (configuration.size() != a2dp_info.capabilities.size())
|
||||
return A2dpStatus::BAD_LENGTH;
|
||||
|
||||
auto config = A2dpBits(configuration);
|
||||
auto lcaps = A2dpBits(a2dp_info.capabilities);
|
||||
|
||||
/* --- Check Sampling Frequency --- */
|
||||
|
||||
int sampling_frequency = config.find_active_bit(kSamplingFrequency);
|
||||
if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY;
|
||||
if (!lcaps.get(sampling_frequency))
|
||||
return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY;
|
||||
|
||||
/* --- Check Channel Mode --- */
|
||||
|
||||
int channel_mode = config.find_active_bit(kChannelMode);
|
||||
if (channel_mode < 0) return A2dpStatus::INVALID_CHANNEL_MODE;
|
||||
if (!lcaps.get(channel_mode)) return A2dpStatus::NOT_SUPPORTED_CHANNEL_MODE;
|
||||
|
||||
/* --- Check Block Length --- */
|
||||
|
||||
int block_length = config.find_active_bit(kBlockLength);
|
||||
if (block_length < 0) return A2dpStatus::INVALID_BLOCK_LENGTH;
|
||||
|
||||
/* --- Check Subbands --- */
|
||||
|
||||
int subbands = config.find_active_bit(kSubbands);
|
||||
if (subbands < 0) return A2dpStatus::INVALID_SUBBANDS;
|
||||
if (!lcaps.get(subbands)) return A2dpStatus::NOT_SUPPORTED_SUBBANDS;
|
||||
|
||||
/* --- Check Allocation Method --- */
|
||||
|
||||
int allocation_method = config.find_active_bit(kAllocationMethod);
|
||||
if (allocation_method < 0) return A2dpStatus::INVALID_ALLOCATION_METHOD;
|
||||
if (!lcaps.get(allocation_method))
|
||||
return A2dpStatus::NOT_SUPPORTED_ALLOCATION_METHOD;
|
||||
|
||||
/* --- Check Bitpool --- */
|
||||
|
||||
uint8_t min_bitpool = config.get(kMinimumBitpool);
|
||||
if (min_bitpool < 2 || min_bitpool > 250)
|
||||
return A2dpStatus::INVALID_MINIMUM_BITPOOL_VALUE;
|
||||
if (min_bitpool < lcaps.get(kMinimumBitpool))
|
||||
return A2dpStatus::NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE;
|
||||
|
||||
uint8_t max_bitpool = config.get(kMaximumBitpool);
|
||||
if (max_bitpool < 2 || max_bitpool > 250)
|
||||
return A2dpStatus::INVALID_MAXIMUM_BITPOOL_VALUE;
|
||||
if (max_bitpool > lcaps.get(kMaximumBitpool))
|
||||
return A2dpStatus::NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE;
|
||||
|
||||
/* --- Return --- */
|
||||
|
||||
codec_parameters->channelMode = GetChannelModeEnum(channel_mode);
|
||||
codec_parameters->samplingFrequencyHz =
|
||||
GetSamplingFrequencyValue(sampling_frequency);
|
||||
codec_parameters->bitdepth = kBitdepth;
|
||||
|
||||
codec_parameters->minBitrate = GetBitrate(config, min_bitpool);
|
||||
codec_parameters->maxBitrate = GetBitrate(config, max_bitpool);
|
||||
|
||||
if (sbc_parameters) {
|
||||
sbc_parameters->block_length = GetBlockLengthValue(block_length);
|
||||
sbc_parameters->subbands = GetSubbandsValue(subbands);
|
||||
sbc_parameters->allocation_method =
|
||||
GetAllocationMethodEnum(allocation_method);
|
||||
sbc_parameters->min_bitpool = min_bitpool;
|
||||
sbc_parameters->max_bitpool = max_bitpool;
|
||||
}
|
||||
|
||||
return A2dpStatus::OK;
|
||||
}
|
||||
|
||||
bool A2dpOffloadCodecSbc::BuildConfiguration(
|
||||
const std::vector<uint8_t>& remote_capabilities,
|
||||
const std::optional<CodecParameters>& hint,
|
||||
std::vector<uint8_t>* configuration) const {
|
||||
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||
|
||||
if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false;
|
||||
|
||||
auto lcaps = A2dpBits(a2dp_info.capabilities);
|
||||
auto rcaps = A2dpBits(remote_capabilities);
|
||||
|
||||
configuration->resize(a2dp_info.capabilities.size());
|
||||
std::fill(begin(*configuration), end(*configuration), 0);
|
||||
auto config = A2dpBits(*configuration);
|
||||
|
||||
/* --- Select Sampling Frequency --- */
|
||||
|
||||
auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1;
|
||||
|
||||
if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint))
|
||||
config.set(sf_hint);
|
||||
else if (lcaps.get(kSamplingFrequency44100) &&
|
||||
rcaps.get(kSamplingFrequency44100))
|
||||
config.set(kSamplingFrequency44100);
|
||||
else if (lcaps.get(kSamplingFrequency48000) &&
|
||||
rcaps.get(kSamplingFrequency48000))
|
||||
config.set(kSamplingFrequency48000);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Channel Mode --- */
|
||||
|
||||
auto cm_hint = hint ? GetChannelModeBit(hint->channelMode) : -1;
|
||||
|
||||
if (cm_hint >= 0 && lcaps.get(cm_hint) && rcaps.get(cm_hint))
|
||||
config.set(cm_hint);
|
||||
else if (lcaps.get(kChannelModeJointStereo) &&
|
||||
rcaps.get(kChannelModeJointStereo))
|
||||
config.set(kChannelModeJointStereo);
|
||||
else if (lcaps.get(kChannelModeStereo) && rcaps.get(kChannelModeStereo))
|
||||
config.set(kChannelModeStereo);
|
||||
else if (lcaps.get(kChannelModeDualChannel) &&
|
||||
rcaps.get(kChannelModeDualChannel))
|
||||
config.set(kChannelModeDualChannel);
|
||||
else if (lcaps.get(kChannelModeMono) && rcaps.get(kChannelModeMono))
|
||||
config.set(kChannelModeMono);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Block Length --- */
|
||||
|
||||
if (lcaps.get(kBlockLength16) && rcaps.get(kBlockLength16))
|
||||
config.set(kBlockLength16);
|
||||
else if (lcaps.get(kBlockLength12) && rcaps.get(kBlockLength12))
|
||||
config.set(kBlockLength12);
|
||||
else if (lcaps.get(kBlockLength8) && rcaps.get(kBlockLength8))
|
||||
config.set(kBlockLength8);
|
||||
else if (lcaps.get(kBlockLength4) && rcaps.get(kBlockLength4))
|
||||
config.set(kBlockLength4);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Subbands --- */
|
||||
|
||||
if (lcaps.get(kSubbands8) && rcaps.get(kSubbands8))
|
||||
config.set(kSubbands8);
|
||||
else if (lcaps.get(kSubbands4) && rcaps.get(kSubbands4))
|
||||
config.set(kSubbands4);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Allocation method --- */
|
||||
|
||||
if (lcaps.get(kAllocationMethodLoudness) &&
|
||||
rcaps.get(kAllocationMethodLoudness))
|
||||
config.set(kAllocationMethodLoudness);
|
||||
else if (lcaps.get(kAllocationMethodSnr) && rcaps.get(kAllocationMethodSnr))
|
||||
config.set(kAllocationMethodSnr);
|
||||
else
|
||||
return false;
|
||||
|
||||
/* --- Select Bitpool --- */
|
||||
|
||||
uint8_t min_bitpool = rcaps.get(kMinimumBitpool);
|
||||
uint8_t max_bitpool = rcaps.get(kMaximumBitpool);
|
||||
|
||||
if (min_bitpool < 2 || min_bitpool > 250 || max_bitpool < 2 ||
|
||||
max_bitpool > 250 || min_bitpool > max_bitpool) {
|
||||
min_bitpool = 2;
|
||||
max_bitpool = 250;
|
||||
}
|
||||
|
||||
min_bitpool = std::max(min_bitpool, uint8_t(lcaps.get(kMinimumBitpool)));
|
||||
max_bitpool = std::max(max_bitpool, uint8_t(lcaps.get(kMaximumBitpool)));
|
||||
|
||||
if (hint) {
|
||||
min_bitpool =
|
||||
std::max(min_bitpool, GetBitpool(*configuration, hint->minBitrate));
|
||||
if (hint->maxBitrate && hint->maxBitrate >= hint->minBitrate)
|
||||
max_bitpool =
|
||||
std::min(max_bitpool, GetBitpool(*configuration, hint->maxBitrate));
|
||||
}
|
||||
|
||||
config.set(kMinimumBitpool, min_bitpool);
|
||||
config.set(kMaximumBitpool, max_bitpool);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
61
bluetooth/audio/hal/A2dpOffloadCodecSbc.h
Normal file
61
bluetooth/audio/hal/A2dpOffloadCodecSbc.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "A2dpOffloadCodec.h"
|
||||
|
||||
namespace aidl::android::hardware::bluetooth::audio {
|
||||
|
||||
struct SbcParameters : public CodecParameters {
|
||||
enum class AllocationMethod { SNR, LOUDNESS };
|
||||
|
||||
AllocationMethod allocation_method;
|
||||
int block_length;
|
||||
int subbands;
|
||||
int min_bitpool;
|
||||
int max_bitpool;
|
||||
};
|
||||
|
||||
class A2dpOffloadCodecSbc : public A2dpOffloadCodec {
|
||||
CodecInfo info_;
|
||||
|
||||
A2dpOffloadCodecSbc();
|
||||
|
||||
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters,
|
||||
SbcParameters* sbc_parameters) const;
|
||||
|
||||
public:
|
||||
static const A2dpOffloadCodecSbc* GetInstance();
|
||||
|
||||
A2dpStatus ParseConfiguration(
|
||||
const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters) const override {
|
||||
return ParseConfiguration(configuration, codec_parameters, nullptr);
|
||||
}
|
||||
|
||||
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||
SbcParameters* sbc_parameters) const {
|
||||
return ParseConfiguration(configuration, sbc_parameters, sbc_parameters);
|
||||
}
|
||||
|
||||
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
|
||||
const std::optional<CodecParameters>& hint,
|
||||
std::vector<uint8_t>* configuration) const override;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::bluetooth::audio
|
|
@ -7,8 +7,13 @@ cc_binary {
|
|||
"BluetoothAudioProvider.cpp",
|
||||
"BluetoothAudioProviderFactory.cpp",
|
||||
"A2dpOffloadAudioProvider.cpp",
|
||||
"A2dpOffloadCodecAac.cpp",
|
||||
"A2dpOffloadCodecFactory.cpp",
|
||||
"A2dpOffloadCodecSbc.cpp",
|
||||
"A2dpSoftwareAudioProvider.cpp",
|
||||
"HearingAidAudioProvider.cpp",
|
||||
"HfpOffloadAudioProvider.cpp",
|
||||
"HfpSoftwareAudioProvider.cpp",
|
||||
"LeAudioOffloadAudioProvider.cpp",
|
||||
"LeAudioSoftwareAudioProvider.cpp",
|
||||
"service_system.cpp",
|
||||
|
@ -16,6 +21,9 @@ cc_binary {
|
|||
header_libs: [
|
||||
"libhardware_headers",
|
||||
],
|
||||
defaults: [
|
||||
"latest_android_hardware_bluetooth_audio_ndk_shared",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
|
@ -25,7 +33,6 @@ cc_binary {
|
|||
"libhidlbase",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"android.hardware.bluetooth.audio-V3-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
],
|
||||
required: [
|
||||
|
|
|
@ -21,15 +21,39 @@
|
|||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "A2dpOffloadCodecFactory.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
struct BluetoothAudioProviderContext {
|
||||
SessionType session_type;
|
||||
};
|
||||
|
||||
static void binderUnlinkedCallbackAidl(void* cookie) {
|
||||
LOG(INFO) << __func__;
|
||||
BluetoothAudioProviderContext* ctx =
|
||||
static_cast<BluetoothAudioProviderContext*>(cookie);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
static void binderDiedCallbackAidl(void* cookie) {
|
||||
LOG(INFO) << __func__;
|
||||
BluetoothAudioProviderContext* ctx =
|
||||
static_cast<BluetoothAudioProviderContext*>(cookie);
|
||||
CHECK_NE(ctx, nullptr);
|
||||
|
||||
BluetoothAudioSessionReport::OnSessionEnded(ctx->session_type);
|
||||
}
|
||||
|
||||
BluetoothAudioProvider::BluetoothAudioProvider() {
|
||||
death_recipient_ = ::ndk::ScopedAIBinder_DeathRecipient(
|
||||
AIBinder_DeathRecipient_new(binderDiedCallbackAidl));
|
||||
AIBinder_DeathRecipient_setOnUnlinked(death_recipient_.get(),
|
||||
binderUnlinkedCallbackAidl);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::startSession(
|
||||
|
@ -39,17 +63,21 @@ ndk::ScopedAStatus BluetoothAudioProvider::startSession(
|
|||
DataMQDesc* _aidl_return) {
|
||||
if (host_if == nullptr) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " Illegal argument";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
latency_modes_ = latencyModes;
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
stack_iface_ = host_if;
|
||||
is_binder_died = false;
|
||||
BluetoothAudioProviderContext* cookie =
|
||||
new BluetoothAudioProviderContext{session_type_};
|
||||
|
||||
AIBinder_linkToDeath(stack_iface_->asBinder().get(), death_recipient_.get(),
|
||||
this);
|
||||
cookie);
|
||||
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
onSessionReady(_aidl_return);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
@ -60,10 +88,8 @@ ndk::ScopedAStatus BluetoothAudioProvider::endSession() {
|
|||
if (stack_iface_ != nullptr) {
|
||||
BluetoothAudioSessionReport::OnSessionEnded(session_type_);
|
||||
|
||||
if (!is_binder_died) {
|
||||
AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
|
||||
death_recipient_.get(), this);
|
||||
}
|
||||
AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
|
||||
death_recipient_.get(), this);
|
||||
} else {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
|
@ -77,10 +103,9 @@ ndk::ScopedAStatus BluetoothAudioProvider::endSession() {
|
|||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::streamStarted(
|
||||
BluetoothAudioStatus status) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status);
|
||||
|
||||
if (stack_iface_ != nullptr) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status);
|
||||
BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
|
||||
status);
|
||||
} else {
|
||||
|
@ -108,8 +133,6 @@ ndk::ScopedAStatus BluetoothAudioProvider::streamSuspended(
|
|||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::updateAudioConfiguration(
|
||||
const AudioConfiguration& audio_config) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
|
||||
if (stack_iface_ == nullptr || audio_config_ == nullptr) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
|
@ -125,13 +148,13 @@ ndk::ScopedAStatus BluetoothAudioProvider::updateAudioConfiguration(
|
|||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
BluetoothAudioSessionReport::ReportAudioConfigChanged(session_type_,
|
||||
*audio_config_);
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " | audio_config=" << audio_config.toString();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
|
||||
bool allowed) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
|
||||
if (stack_iface_ == nullptr) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
|
@ -143,19 +166,143 @@ ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
|
|||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void BluetoothAudioProvider::binderDiedCallbackAidl(void* ptr) {
|
||||
LOG(ERROR) << __func__ << " - BluetoothAudio Service died";
|
||||
auto provider = static_cast<BluetoothAudioProvider*>(ptr);
|
||||
if (provider == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": Null AudioProvider HAL died";
|
||||
return;
|
||||
}
|
||||
provider->is_binder_died = true;
|
||||
provider->endSession();
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::parseA2dpConfiguration(
|
||||
[[maybe_unused]] const CodecId& codec_id,
|
||||
[[maybe_unused]] const std::vector<uint8_t>& configuration,
|
||||
[[maybe_unused]] CodecParameters* codec_parameters,
|
||||
[[maybe_unused]] A2dpStatus* _aidl_return) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " is illegal";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::getA2dpConfiguration(
|
||||
[[maybe_unused]] const std::vector<A2dpRemoteCapabilities>&
|
||||
remote_a2dp_capabilities,
|
||||
[[maybe_unused]] const A2dpConfigurationHint& hint,
|
||||
[[maybe_unused]] std::optional<audio::A2dpConfiguration>* _aidl_return) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " is illegal";
|
||||
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::setCodecPriority(
|
||||
const ::aidl::android::hardware::bluetooth::audio::CodecId& in_codecId,
|
||||
int32_t in_priority) {
|
||||
/* TODO: Implement */
|
||||
(void)in_codecId;
|
||||
(void)in_priority;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseConfiguration(
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSourceAudioCapabilities,
|
||||
const std::vector<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioConfigurationRequirement>& in_requirements,
|
||||
std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
|
||||
_aidl_return) {
|
||||
/* TODO: Implement */
|
||||
(void)in_remoteSinkAudioCapabilities;
|
||||
(void)in_remoteSourceAudioCapabilities;
|
||||
(void)in_requirements;
|
||||
(void)_aidl_return;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseQosConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioAseQosConfigurationRequirement& in_qosRequirement,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioAseQosConfigurationPair* _aidl_return) {
|
||||
/* TODO: Implement */
|
||||
(void)in_qosRequirement;
|
||||
(void)_aidl_return;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseDatapathConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::AudioContext& in_context,
|
||||
const std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
LeAudioConfiguration::StreamMap>& in_streamMap,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDataPathConfigurationPair* _aidl_return) {
|
||||
/* TODO: Implement */
|
||||
(void)in_context;
|
||||
(void)in_streamMap;
|
||||
(void)_aidl_return;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::onSinkAseMetadataChanged(
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
AseState in_state,
|
||||
int32_t cigId, int32_t cisId,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
|
||||
in_metadata) {
|
||||
/* TODO: Implement */
|
||||
(void)in_state;
|
||||
(void)cigId;
|
||||
(void)cisId;
|
||||
(void)in_metadata;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::onSourceAseMetadataChanged(
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
AseState in_state,
|
||||
int32_t cigId, int32_t cisId,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
|
||||
in_metadata) {
|
||||
/* TODO: Implement */
|
||||
(void)in_state;
|
||||
(void)cigId;
|
||||
(void)cisId;
|
||||
(void)in_metadata;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioBroadcastConfiguration(
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
|
||||
const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioBroadcastConfigurationRequirement& in_requirement,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioBroadcastConfigurationSetting* _aidl_return) {
|
||||
/* TODO: Implement */
|
||||
(void)in_remoteSinkAudioCapabilities;
|
||||
(void)in_requirement;
|
||||
(void)_aidl_return;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus
|
||||
BluetoothAudioProvider::getLeAudioBroadcastDatapathConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::AudioContext& in_context,
|
||||
const std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
LeAudioBroadcastConfiguration::BroadcastStreamMap>&
|
||||
in_streamMap,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDataPathConfiguration* _aidl_return) {
|
||||
/* TODO: Implement */
|
||||
(void)in_context;
|
||||
(void)in_streamMap;
|
||||
(void)_aidl_return;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
} // namespace aidl
|
||||
|
|
|
@ -41,20 +41,86 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider {
|
|||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
|
||||
ndk::ScopedAStatus endSession();
|
||||
ndk::ScopedAStatus streamStarted(BluetoothAudioStatus status);
|
||||
ndk::ScopedAStatus streamSuspended(BluetoothAudioStatus status);
|
||||
ndk::ScopedAStatus updateAudioConfiguration(
|
||||
const AudioConfiguration& audio_config);
|
||||
ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed);
|
||||
ndk::ScopedAStatus setCodecPriority(
|
||||
const ::aidl::android::hardware::bluetooth::audio::CodecId& in_codecId,
|
||||
int32_t in_priority) override;
|
||||
ndk::ScopedAStatus getLeAudioAseConfiguration(
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSourceAudioCapabilities,
|
||||
const std::vector<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioConfigurationRequirement>& in_requirements,
|
||||
std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
|
||||
_aidl_return) override;
|
||||
ndk::ScopedAStatus getLeAudioAseQosConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
||||
in_qosRequirement,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioAseQosConfigurationPair* _aidl_return) override;
|
||||
ndk::ScopedAStatus getLeAudioAseDatapathConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::AudioContext&
|
||||
in_context,
|
||||
const std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
LeAudioConfiguration::StreamMap>& in_streamMap,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDataPathConfigurationPair* _aidl_return) override;
|
||||
ndk::ScopedAStatus onSinkAseMetadataChanged(
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
AseState in_state,
|
||||
int32_t cigId, int32_t cisId,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
|
||||
in_metadata) override;
|
||||
ndk::ScopedAStatus onSourceAseMetadataChanged(
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
AseState in_state,
|
||||
int32_t cigId, int32_t cisId,
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
|
||||
in_metadata) override;
|
||||
ndk::ScopedAStatus getLeAudioBroadcastConfiguration(
|
||||
const std::optional<std::vector<std::optional<
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
|
||||
const ::aidl::android::hardware::bluetooth::audio::
|
||||
IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
|
||||
in_requirement,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioBroadcastConfigurationSetting* _aidl_return) override;
|
||||
ndk::ScopedAStatus getLeAudioBroadcastDatapathConfiguration(
|
||||
const ::aidl::android::hardware::bluetooth::audio::AudioContext&
|
||||
in_context,
|
||||
const std::vector<::aidl::android::hardware::bluetooth::audio::
|
||||
LeAudioBroadcastConfiguration::BroadcastStreamMap>&
|
||||
in_streamMap,
|
||||
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
|
||||
LeAudioDataPathConfiguration* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus parseA2dpConfiguration(
|
||||
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
|
||||
CodecParameters* codec_parameters, A2dpStatus* _aidl_return);
|
||||
ndk::ScopedAStatus getA2dpConfiguration(
|
||||
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||
const A2dpConfigurationHint& hint,
|
||||
std::optional<audio::A2dpConfiguration>* _aidl_return);
|
||||
|
||||
virtual bool isValid(const SessionType& sessionType) = 0;
|
||||
|
||||
protected:
|
||||
virtual ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) = 0;
|
||||
static void binderDiedCallbackAidl(void* cookie_ptr);
|
||||
|
||||
::ndk::ScopedAIBinder_DeathRecipient death_recipient_;
|
||||
|
||||
|
@ -62,9 +128,7 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider {
|
|||
std::unique_ptr<AudioConfiguration> audio_config_ = nullptr;
|
||||
SessionType session_type_;
|
||||
std::vector<LatencyMode> latency_modes_;
|
||||
bool is_binder_died = false;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
#include <android-base/logging.h>
|
||||
|
||||
#include "A2dpOffloadAudioProvider.h"
|
||||
#include "A2dpOffloadCodecFactory.h"
|
||||
#include "A2dpSoftwareAudioProvider.h"
|
||||
#include "BluetoothAudioProvider.h"
|
||||
#include "HearingAidAudioProvider.h"
|
||||
#include "HfpOffloadAudioProvider.h"
|
||||
#include "HfpSoftwareAudioProvider.h"
|
||||
#include "LeAudioOffloadAudioProvider.h"
|
||||
#include "LeAudioSoftwareAudioProvider.h"
|
||||
|
||||
|
@ -34,6 +37,9 @@ namespace hardware {
|
|||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static const std::string kLeAudioOffloadProviderName =
|
||||
"LE_AUDIO_OFFLOAD_HARDWARE_OFFLOAD_PROVIDER";
|
||||
|
||||
BluetoothAudioProviderFactory::BluetoothAudioProviderFactory() {}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProviderFactory::openProvider(
|
||||
|
@ -78,6 +84,15 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::openProvider(
|
|||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<A2dpOffloadDecodingAudioProvider>();
|
||||
break;
|
||||
case SessionType::HFP_SOFTWARE_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<HfpSoftwareOutputAudioProvider>();
|
||||
break;
|
||||
case SessionType::HFP_SOFTWARE_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<HfpSoftwareInputAudioProvider>();
|
||||
break;
|
||||
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<HfpOffloadAudioProvider>();
|
||||
break;
|
||||
default:
|
||||
provider = nullptr;
|
||||
break;
|
||||
|
@ -135,8 +150,43 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderCapabilities(
|
|||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo(
|
||||
SessionType session_type, std::optional<ProviderInfo>* _aidl_return) {
|
||||
*_aidl_return = std::nullopt;
|
||||
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type);
|
||||
|
||||
if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
auto& provider_info = _aidl_return->emplace();
|
||||
|
||||
provider_info.name = A2dpOffloadCodecFactory::GetInstance()->name;
|
||||
for (auto codec : A2dpOffloadCodecFactory::GetInstance()->codecs)
|
||||
provider_info.codecInfos.push_back(codec->info);
|
||||
}
|
||||
|
||||
if (session_type ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
session_type ==
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
std::vector<CodecInfo> db_codec_info =
|
||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(session_type);
|
||||
if (!db_codec_info.empty()) {
|
||||
auto& provider_info = _aidl_return->emplace();
|
||||
provider_info.name = kLeAudioOffloadProviderName;
|
||||
provider_info.codecInfos = db_codec_info;
|
||||
*_aidl_return = provider_info;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
} // namespace aidl
|
||||
|
|
|
@ -35,6 +35,10 @@ class BluetoothAudioProviderFactory : public BnBluetoothAudioProviderFactory {
|
|||
ndk::ScopedAStatus getProviderCapabilities(
|
||||
const SessionType session_type,
|
||||
std::vector<AudioCapabilities>* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus getProviderInfo(
|
||||
SessionType in_sessionType,
|
||||
std::optional<ProviderInfo>* _aidl_return) override;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
|
65
bluetooth/audio/hal/HfpOffloadAudioProvider.cpp
Normal file
65
bluetooth/audio/hal/HfpOffloadAudioProvider.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderHfpHW"
|
||||
|
||||
#include "HfpOffloadAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
HfpOffloadAudioProvider::HfpOffloadAudioProvider() {
|
||||
session_type_ = SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH;
|
||||
}
|
||||
|
||||
bool HfpOffloadAudioProvider::isValid(const SessionType& session_type) {
|
||||
return (session_type == session_type_);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HfpOffloadAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::hfpConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
return BluetoothAudioProvider::startSession(host_if, audio_config,
|
||||
latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HfpOffloadAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, nullptr, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
46
bluetooth/audio/hal/HfpOffloadAudioProvider.h
Normal file
46
bluetooth/audio/hal/HfpOffloadAudioProvider.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class HfpOffloadAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
HfpOffloadAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
140
bluetooth/audio/hal/HfpSoftwareAudioProvider.cpp
Normal file
140
bluetooth/audio/hal/HfpSoftwareAudioProvider.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderHfpSW"
|
||||
|
||||
#include "HfpSoftwareAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static constexpr uint32_t kBufferCount = 2; // two frame buffer
|
||||
|
||||
HfpSoftwareOutputAudioProvider::HfpSoftwareOutputAudioProvider()
|
||||
: HfpSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::HFP_SOFTWARE_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
HfpSoftwareInputAudioProvider::HfpSoftwareInputAudioProvider()
|
||||
: HfpSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::HFP_SOFTWARE_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
HfpSoftwareAudioProvider::HfpSoftwareAudioProvider()
|
||||
: BluetoothAudioProvider(), data_mq_(nullptr) {
|
||||
}
|
||||
|
||||
bool HfpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
|
||||
return (sessionType == session_type_);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HfpSoftwareAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const PcmConfiguration& pcm_config =
|
||||
audio_config.get<AudioConfiguration::pcmConfig>();
|
||||
if (!BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(pcm_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
|
||||
<< pcm_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
bool isValidConfig = true;
|
||||
|
||||
if (pcm_config.bitsPerSample != 16) {
|
||||
isValidConfig = false;
|
||||
}
|
||||
|
||||
if (pcm_config.sampleRateHz != 8000 && pcm_config.sampleRateHz != 16000 &&
|
||||
pcm_config.sampleRateHz != 32000) {
|
||||
isValidConfig = false;
|
||||
}
|
||||
|
||||
if (pcm_config.channelMode != ChannelMode::MONO) {
|
||||
isValidConfig = false;
|
||||
}
|
||||
|
||||
if (pcm_config.dataIntervalUs != 7500) {
|
||||
isValidConfig = false;
|
||||
}
|
||||
|
||||
int bytes_per_sample = pcm_config.bitsPerSample / 8;
|
||||
|
||||
uint32_t data_mq_size = kBufferCount * bytes_per_sample *
|
||||
(pcm_config.sampleRateHz / 1000) *
|
||||
pcm_config.dataIntervalUs / 1000;
|
||||
if (!isValidConfig) {
|
||||
LOG(ERROR) << __func__ << "Unexpected audio buffer size: " << data_mq_size
|
||||
<< ", SampleRateHz: " << pcm_config.sampleRateHz
|
||||
<< ", ChannelMode: " << toString(pcm_config.channelMode)
|
||||
<< ", BitsPerSample: "
|
||||
<< static_cast<int>(pcm_config.bitsPerSample)
|
||||
<< ", BytesPerSample: " << bytes_per_sample
|
||||
<< ", DataIntervalUs: " << pcm_config.dataIntervalUs
|
||||
<< ", SessionType: " << toString(session_type_);
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << " - size of audio buffer " << data_mq_size
|
||||
<< " byte(s)";
|
||||
|
||||
std::unique_ptr<DataMQ> temp_data_mq(
|
||||
new DataMQ(data_mq_size, /* EventFlag */ true));
|
||||
if (temp_data_mq == nullptr || !temp_data_mq->isValid()) {
|
||||
ALOGE_IF(!temp_data_mq, "failed to allocate data MQ");
|
||||
ALOGE_IF(temp_data_mq && !temp_data_mq->isValid(), "data MQ is invalid");
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
data_mq_ = std::move(temp_data_mq);
|
||||
|
||||
return BluetoothAudioProvider::startSession(host_if, audio_config,
|
||||
latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HfpSoftwareAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
if (data_mq_ == nullptr || !data_mq_->isValid()) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
*_aidl_return = data_mq_->dupeDesc();
|
||||
auto desc = data_mq_->dupeDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, &desc, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
59
bluetooth/audio/hal/HfpSoftwareAudioProvider.h
Normal file
59
bluetooth/audio/hal/HfpSoftwareAudioProvider.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class HfpSoftwareAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
HfpSoftwareAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
// audio data queue for software encoding
|
||||
std::unique_ptr<DataMQ> data_mq_;
|
||||
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
class HfpSoftwareOutputAudioProvider : public HfpSoftwareAudioProvider {
|
||||
public:
|
||||
HfpSoftwareOutputAudioProvider();
|
||||
};
|
||||
|
||||
class HfpSoftwareInputAudioProvider : public HfpSoftwareAudioProvider {
|
||||
public:
|
||||
HfpSoftwareInputAudioProvider();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
|
@ -28,6 +28,73 @@ namespace hardware {
|
|||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
constexpr uint8_t kLeAudioDirectionSink = 0x01;
|
||||
constexpr uint8_t kLeAudioDirectionSource = 0x02;
|
||||
|
||||
const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t>
|
||||
freq_to_support_bitmask_map = {
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ8000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ11025,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ11025},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ22050,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ22050},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ32000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ88200,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ88200},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ96000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ176400,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ176400},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ192000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ192000},
|
||||
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ384000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ384000},
|
||||
};
|
||||
|
||||
// Helper map from capability's tag to configuration's tag
|
||||
std::map<CodecSpecificCapabilitiesLtv::Tag, CodecSpecificConfigurationLtv::Tag>
|
||||
cap_to_cfg_tag_map = {
|
||||
{CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies,
|
||||
CodecSpecificConfigurationLtv::Tag::samplingFrequency},
|
||||
{CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU,
|
||||
CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU},
|
||||
{CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations,
|
||||
CodecSpecificConfigurationLtv::Tag::frameDuration},
|
||||
{CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts,
|
||||
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation},
|
||||
{CodecSpecificCapabilitiesLtv::Tag::supportedOctetsPerCodecFrame,
|
||||
CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame},
|
||||
};
|
||||
|
||||
const std::map<CodecSpecificConfigurationLtv::FrameDuration, uint32_t>
|
||||
fduration_to_support_fduration_map = {
|
||||
{CodecSpecificConfigurationLtv::FrameDuration::US7500,
|
||||
CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US7500},
|
||||
{CodecSpecificConfigurationLtv::FrameDuration::US10000,
|
||||
CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US10000},
|
||||
};
|
||||
|
||||
std::map<int32_t, CodecSpecificConfigurationLtv::SamplingFrequency>
|
||||
sampling_freq_map = {
|
||||
{16000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
|
||||
{48000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000},
|
||||
{96000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000},
|
||||
};
|
||||
|
||||
std::map<int32_t, CodecSpecificConfigurationLtv::FrameDuration>
|
||||
frame_duration_map = {
|
||||
{7500, CodecSpecificConfigurationLtv::FrameDuration::US7500},
|
||||
{10000, CodecSpecificConfigurationLtv::FrameDuration::US10000},
|
||||
};
|
||||
|
||||
LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
|
||||
: LeAudioOffloadAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
|
@ -55,24 +122,23 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::startSession(
|
|||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
|
||||
if (session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
if (audio_config.getTag() != AudioConfiguration::leAudioBroadcastConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
} else if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& le_audio_config =
|
||||
audio_config.get<AudioConfiguration::leAudioConfig>();
|
||||
if (!BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
|
||||
session_type_, le_audio_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
|
||||
<< le_audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
return BluetoothAudioProvider::startSession(host_if, audio_config,
|
||||
latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
|
||||
|
@ -82,6 +148,644 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
|
|||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::setCodecPriority(
|
||||
const CodecId& in_codecId, int32_t in_priority) {
|
||||
codec_priority_map_[in_codecId] = in_priority;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedValidCodec(CodecId cfg_codec,
|
||||
CodecId req_codec) {
|
||||
auto priority = codec_priority_map_.find(cfg_codec);
|
||||
if (priority != codec_priority_map_.end() && priority->second == -1)
|
||||
return false;
|
||||
return cfg_codec == req_codec;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext(
|
||||
AudioContext setting_context,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
|
||||
// If has no metadata, assume match
|
||||
if (!capabilities.metadata.has_value()) return true;
|
||||
|
||||
for (auto metadata : capabilities.metadata.value()) {
|
||||
if (!metadata.has_value()) continue;
|
||||
if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
|
||||
// Check all pref audio context to see if anything matched
|
||||
auto& context = metadata.value()
|
||||
.get<MetadataLtv::Tag::preferredAudioContexts>()
|
||||
.values;
|
||||
if (setting_context.bitmask & context.bitmask) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq(
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
|
||||
capability_freq) {
|
||||
for (auto [freq, bitmask] : freq_to_support_bitmask_map)
|
||||
if (cfg_freq == freq) return (capability_freq.bitmask & bitmask);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
|
||||
CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
|
||||
CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
|
||||
capability_fduration) {
|
||||
for (auto [fduration, bitmask] : fduration_to_support_fduration_map)
|
||||
if (cfg_fduration == fduration)
|
||||
return (capability_fduration.bitmask & bitmask);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation&
|
||||
/*cfg_channel*/,
|
||||
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
|
||||
/*capability_channel*/) {
|
||||
bool isMatched = true;
|
||||
// TODO: how to match?
|
||||
return isMatched;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU(
|
||||
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu,
|
||||
CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU&
|
||||
capability_frame_sdu) {
|
||||
return cfg_frame_sdu.value <= capability_frame_sdu.value;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedOctetsPerCodecFrame(
|
||||
CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets,
|
||||
CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame&
|
||||
capability_octets) {
|
||||
return cfg_octets.value >= capability_octets.minimum &&
|
||||
cfg_octets.value <= capability_octets.maximum;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
|
||||
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
|
||||
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities) {
|
||||
// Convert all codec_cfg into a map of tags -> correct data
|
||||
std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
|
||||
cfg_tag_map;
|
||||
for (auto codec_cfg_data : codec_cfg)
|
||||
cfg_tag_map[codec_cfg_data.getTag()] = codec_cfg_data;
|
||||
|
||||
for (auto& codec_capability : codec_capabilities) {
|
||||
auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]);
|
||||
// Cannot find tag for the capability:
|
||||
if (cfg == cfg_tag_map.end()) return false;
|
||||
|
||||
// Matching logic for sampling frequency
|
||||
if (codec_capability.getTag() ==
|
||||
CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies) {
|
||||
if (!isMatchedSamplingFreq(
|
||||
cfg->second
|
||||
.get<CodecSpecificConfigurationLtv::Tag::samplingFrequency>(),
|
||||
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedSamplingFrequencies>()))
|
||||
return false;
|
||||
} else if (codec_capability.getTag() ==
|
||||
CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations) {
|
||||
if (!isMatchedFrameDuration(
|
||||
cfg->second
|
||||
.get<CodecSpecificConfigurationLtv::Tag::frameDuration>(),
|
||||
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedFrameDurations>()))
|
||||
return false;
|
||||
} else if (codec_capability.getTag() ==
|
||||
CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts) {
|
||||
if (!isMatchedAudioChannel(
|
||||
cfg->second.get<
|
||||
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>(),
|
||||
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedAudioChannelCounts>()))
|
||||
return false;
|
||||
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedMaxCodecFramesPerSDU) {
|
||||
if (!isMatchedCodecFramesPerSDU(
|
||||
cfg->second.get<
|
||||
CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU>(),
|
||||
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedMaxCodecFramesPerSDU>()))
|
||||
return false;
|
||||
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedOctetsPerCodecFrame) {
|
||||
if (!isMatchedOctetsPerCodecFrame(
|
||||
cfg->second.get<
|
||||
CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(),
|
||||
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
|
||||
supportedOctetsPerCodecFrame>()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
|
||||
LeAudioAseConfiguration setting_cfg,
|
||||
LeAudioAseConfiguration requirement_cfg) {
|
||||
// Check matching for codec configuration <=> requirement ASE codec
|
||||
// Also match if no CodecId requirement
|
||||
if (requirement_cfg.codecId.has_value()) {
|
||||
if (!setting_cfg.codecId.has_value()) return false;
|
||||
if (!isMatchedValidCodec(setting_cfg.codecId.value(),
|
||||
requirement_cfg.codecId.value()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setting_cfg.targetLatency != requirement_cfg.targetLatency) return false;
|
||||
// Ignore PHY requirement
|
||||
|
||||
// Check all codec configuration
|
||||
std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
|
||||
cfg_tag_map;
|
||||
for (auto cfg : setting_cfg.codecConfiguration)
|
||||
cfg_tag_map[cfg.getTag()] = cfg;
|
||||
|
||||
for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
|
||||
// Directly compare CodecSpecificConfigurationLtv
|
||||
auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
|
||||
if (cfg == cfg_tag_map.end()) return false;
|
||||
|
||||
if (cfg->second != requirement_cfg) return false;
|
||||
}
|
||||
// Ignore vendor configuration and metadata requirement
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedBISConfiguration(
|
||||
LeAudioBisConfiguration bis_cfg,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
|
||||
if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) return false;
|
||||
if (!isCapabilitiesMatchedCodecConfiguration(
|
||||
bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration(
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
direction_configurations,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
valid_direction_configurations) {
|
||||
for (auto direction_configuration : direction_configurations) {
|
||||
if (!direction_configuration.has_value()) continue;
|
||||
if (!direction_configuration.value().aseConfiguration.codecId.has_value())
|
||||
continue;
|
||||
if (!isMatchedValidCodec(
|
||||
direction_configuration.value().aseConfiguration.codecId.value(),
|
||||
capabilities.codecId))
|
||||
continue;
|
||||
// Check matching for codec configuration <=> codec capabilities
|
||||
if (!isCapabilitiesMatchedCodecConfiguration(
|
||||
direction_configuration.value().aseConfiguration.codecConfiguration,
|
||||
capabilities.codecSpecificCapabilities))
|
||||
continue;
|
||||
valid_direction_configurations.push_back(direction_configuration);
|
||||
}
|
||||
}
|
||||
|
||||
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
direction_configurations,
|
||||
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
|
||||
requirements,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
valid_direction_configurations) {
|
||||
for (auto direction_configuration : direction_configurations) {
|
||||
if (!requirements.has_value()) {
|
||||
// If there's no requirement, all are valid
|
||||
valid_direction_configurations.push_back(direction_configuration);
|
||||
continue;
|
||||
}
|
||||
if (!direction_configuration.has_value()) continue;
|
||||
|
||||
for (auto& requirement : requirements.value()) {
|
||||
if (!requirement.has_value()) continue;
|
||||
if (!isMatchedAseConfiguration(
|
||||
direction_configuration.value().aseConfiguration,
|
||||
requirement.value().aseConfiguration))
|
||||
continue;
|
||||
// Valid if match any requirement.
|
||||
valid_direction_configurations.push_back(direction_configuration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
|
||||
* capabilities. The new setting will have a filtered list of
|
||||
* AseDirectionConfiguration that matched the capabilities */
|
||||
std::optional<LeAudioAseConfigurationSetting>
|
||||
LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
|
||||
uint8_t direction) {
|
||||
// Try to match context in metadata.
|
||||
if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities))
|
||||
return std::nullopt;
|
||||
|
||||
// Get a list of all matched AseDirectionConfiguration
|
||||
// for the input direction
|
||||
std::vector<std::optional<AseDirectionConfiguration>>*
|
||||
direction_configuration = nullptr;
|
||||
if (direction == kLeAudioDirectionSink) {
|
||||
if (!setting.sinkAseConfiguration.has_value()) return std::nullopt;
|
||||
direction_configuration = &setting.sinkAseConfiguration.value();
|
||||
} else {
|
||||
if (!setting.sourceAseConfiguration.has_value()) return std::nullopt;
|
||||
direction_configuration = &setting.sourceAseConfiguration.value();
|
||||
}
|
||||
std::vector<std::optional<AseDirectionConfiguration>>
|
||||
valid_direction_configuration;
|
||||
filterCapabilitiesAseDirectionConfiguration(
|
||||
*direction_configuration, capabilities, valid_direction_configuration);
|
||||
if (valid_direction_configuration.empty()) return std::nullopt;
|
||||
|
||||
// Create a new LeAudioAseConfigurationSetting and return
|
||||
LeAudioAseConfigurationSetting filtered_setting;
|
||||
filtered_setting.audioContext = setting.audioContext;
|
||||
filtered_setting.packing = setting.packing;
|
||||
if (direction == kLeAudioDirectionSink) {
|
||||
filtered_setting.sinkAseConfiguration = valid_direction_configuration;
|
||||
} else {
|
||||
filtered_setting.sourceAseConfiguration = valid_direction_configuration;
|
||||
}
|
||||
filtered_setting.flags = setting.flags;
|
||||
|
||||
return filtered_setting;
|
||||
}
|
||||
|
||||
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
|
||||
* requirement. The new setting will have a filtered list of
|
||||
* AseDirectionConfiguration that matched the requirement */
|
||||
std::optional<LeAudioAseConfigurationSetting>
|
||||
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
|
||||
requirement) {
|
||||
// Try to match context in metadata.
|
||||
if (setting.audioContext != requirement.audioContext) return std::nullopt;
|
||||
|
||||
// Check requirement for the correct direction
|
||||
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>*
|
||||
direction_requirement;
|
||||
std::vector<std::optional<AseDirectionConfiguration>>*
|
||||
direction_configuration;
|
||||
if (setting.sinkAseConfiguration.has_value()) {
|
||||
direction_configuration = &setting.sinkAseConfiguration.value();
|
||||
direction_requirement = &requirement.sinkAseRequirement;
|
||||
} else {
|
||||
direction_configuration = &setting.sourceAseConfiguration.value();
|
||||
direction_requirement = &requirement.sourceAseRequirement;
|
||||
}
|
||||
|
||||
std::vector<std::optional<AseDirectionConfiguration>>
|
||||
valid_direction_configuration;
|
||||
filterRequirementAseDirectionConfiguration(*direction_configuration,
|
||||
*direction_requirement,
|
||||
valid_direction_configuration);
|
||||
if (valid_direction_configuration.empty()) return std::nullopt;
|
||||
|
||||
// Create a new LeAudioAseConfigurationSetting and return
|
||||
LeAudioAseConfigurationSetting filtered_setting;
|
||||
filtered_setting.audioContext = setting.audioContext;
|
||||
filtered_setting.packing = setting.packing;
|
||||
if (setting.sinkAseConfiguration.has_value())
|
||||
filtered_setting.sinkAseConfiguration = valid_direction_configuration;
|
||||
else
|
||||
filtered_setting.sourceAseConfiguration = valid_direction_configuration;
|
||||
filtered_setting.flags = setting.flags;
|
||||
|
||||
return filtered_setting;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSinkAudioCapabilities,
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSourceAudioCapabilities,
|
||||
const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
|
||||
in_requirements,
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
|
||||
_aidl_return) {
|
||||
// Get all configuration settings
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||
ase_configuration_settings =
|
||||
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
|
||||
|
||||
// Currently won't handle case where both sink and source capabilities
|
||||
// are passed in. Only handle one of them.
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>*
|
||||
in_remoteAudioCapabilities;
|
||||
uint8_t direction = 0;
|
||||
if (in_remoteSinkAudioCapabilities.has_value()) {
|
||||
direction = kLeAudioDirectionSink;
|
||||
in_remoteAudioCapabilities = &in_remoteSinkAudioCapabilities;
|
||||
} else {
|
||||
direction = kLeAudioDirectionSource;
|
||||
in_remoteAudioCapabilities = &in_remoteSourceAudioCapabilities;
|
||||
}
|
||||
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||
capability_matched_ase_configuration_settings;
|
||||
// Matching with remote capabilities
|
||||
for (auto& setting : ase_configuration_settings) {
|
||||
for (auto& capability : in_remoteAudioCapabilities->value()) {
|
||||
if (!capability.has_value()) continue;
|
||||
auto filtered_ase_configuration_setting =
|
||||
getCapabilitiesMatchedAseConfigurationSettings(
|
||||
setting, capability.value(), direction);
|
||||
if (filtered_ase_configuration_setting.has_value()) {
|
||||
capability_matched_ase_configuration_settings.push_back(
|
||||
filtered_ase_configuration_setting.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matching with requirements
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
|
||||
for (auto& setting : capability_matched_ase_configuration_settings) {
|
||||
for (auto& requirement : in_requirements) {
|
||||
auto filtered_ase_configuration_setting =
|
||||
getRequirementMatchedAseConfigurationSettings(setting, requirement);
|
||||
if (filtered_ase_configuration_setting.has_value()) {
|
||||
result.push_back(filtered_ase_configuration_setting.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*_aidl_return = result;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isMatchedQosRequirement(
|
||||
LeAudioAseQosConfiguration setting_qos,
|
||||
AseQosDirectionRequirement requirement_qos) {
|
||||
if (setting_qos.retransmissionNum !=
|
||||
requirement_qos.preferredRetransmissionNum)
|
||||
return false;
|
||||
if (setting_qos.maxTransportLatencyMs > requirement_qos.maxTransportLatencyMs)
|
||||
return false;
|
||||
// Ignore other parameters, as they are not populated in the setting_qos
|
||||
return true;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
|
||||
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
||||
in_qosRequirement,
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return) {
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair result;
|
||||
// Get all configuration settings
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
|
||||
ase_configuration_settings =
|
||||
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
|
||||
|
||||
// Direction QoS matching
|
||||
// Only handle one direction input case
|
||||
uint8_t direction = 0;
|
||||
std::optional<AseQosDirectionRequirement> direction_qos_requirement =
|
||||
std::nullopt;
|
||||
if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
|
||||
direction_qos_requirement = in_qosRequirement.sinkAseQosRequirement.value();
|
||||
direction = kLeAudioDirectionSink;
|
||||
} else if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
|
||||
direction_qos_requirement =
|
||||
in_qosRequirement.sourceAseQosRequirement.value();
|
||||
direction = kLeAudioDirectionSource;
|
||||
}
|
||||
|
||||
for (auto& setting : ase_configuration_settings) {
|
||||
// Context matching
|
||||
if (setting.audioContext != in_qosRequirement.contextType) continue;
|
||||
|
||||
// Match configuration flags
|
||||
// Currently configuration flags are not populated, ignore.
|
||||
|
||||
// Get a list of all matched AseDirectionConfiguration
|
||||
// for the input direction
|
||||
std::vector<std::optional<AseDirectionConfiguration>>*
|
||||
direction_configuration = nullptr;
|
||||
if (direction == kLeAudioDirectionSink) {
|
||||
if (!setting.sinkAseConfiguration.has_value()) continue;
|
||||
direction_configuration = &setting.sinkAseConfiguration.value();
|
||||
} else {
|
||||
if (!setting.sourceAseConfiguration.has_value()) continue;
|
||||
direction_configuration = &setting.sourceAseConfiguration.value();
|
||||
}
|
||||
|
||||
for (auto cfg : *direction_configuration) {
|
||||
if (!cfg.has_value()) continue;
|
||||
// If no requirement, return the first QoS
|
||||
if (!direction_qos_requirement.has_value()) {
|
||||
result.sinkQosConfiguration = cfg.value().qosConfiguration;
|
||||
result.sourceQosConfiguration = cfg.value().qosConfiguration;
|
||||
*_aidl_return = result;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// If has requirement, return the first matched QoS
|
||||
// Try to match the ASE configuration
|
||||
// and QoS with requirement
|
||||
if (!cfg.value().qosConfiguration.has_value()) continue;
|
||||
if (isMatchedAseConfiguration(
|
||||
cfg.value().aseConfiguration,
|
||||
direction_qos_requirement.value().aseConfiguration) &&
|
||||
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
|
||||
direction_qos_requirement.value())) {
|
||||
if (direction == kLeAudioDirectionSink)
|
||||
result.sinkQosConfiguration = cfg.value().qosConfiguration;
|
||||
else
|
||||
result.sourceQosConfiguration = cfg.value().qosConfiguration;
|
||||
*_aidl_return = result;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No match, return empty QoS
|
||||
*_aidl_return = result;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSinkAseMetadataChanged(
|
||||
IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
|
||||
int32_t /*in_cisId*/,
|
||||
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
|
||||
(void)in_state;
|
||||
(void)in_metadata;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSourceAseMetadataChanged(
|
||||
IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
|
||||
int32_t /*in_cisId*/,
|
||||
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
|
||||
(void)in_state;
|
||||
(void)in_metadata;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
};
|
||||
|
||||
void LeAudioOffloadAudioProvider::getBroadcastSettings() {
|
||||
if (!broadcast_settings.empty()) return;
|
||||
|
||||
LOG(INFO) << __func__ << ": Loading broadcast settings from provider info";
|
||||
|
||||
std::vector<CodecInfo> db_codec_info =
|
||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
|
||||
broadcast_settings.clear();
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
|
||||
default_allocation.bitmask =
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
|
||||
|
||||
for (auto& codec_info : db_codec_info) {
|
||||
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
|
||||
continue;
|
||||
auto& transport = codec_info.transport.get<CodecInfo::Transport::leAudio>();
|
||||
LeAudioBroadcastConfigurationSetting setting;
|
||||
// Default setting
|
||||
setting.numBis = 1;
|
||||
setting.phy = {Phy::TWO_M};
|
||||
// Populate BIS configuration info using codec_info
|
||||
LeAudioBisConfiguration bis_cfg;
|
||||
bis_cfg.codecId = codec_info.id;
|
||||
|
||||
CodecSpecificConfigurationLtv::OctetsPerCodecFrame octets;
|
||||
octets.value = transport.bitdepth[0];
|
||||
|
||||
bis_cfg.codecConfiguration = {
|
||||
sampling_freq_map[transport.samplingFrequencyHz[0]], octets,
|
||||
frame_duration_map[transport.frameDurationUs[0]], default_allocation};
|
||||
|
||||
// Add information to structure
|
||||
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
|
||||
sub_bis_cfg.numBis = 1;
|
||||
sub_bis_cfg.bisConfiguration = bis_cfg;
|
||||
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
|
||||
sub_cfg.bisConfigurations = {sub_bis_cfg};
|
||||
setting.subgroupsConfigurations = {sub_cfg};
|
||||
|
||||
broadcast_settings.push_back(setting);
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__
|
||||
<< ": Done loading broadcast settings from provider info";
|
||||
}
|
||||
|
||||
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
|
||||
* capabilities. The new setting will have a filtered list of
|
||||
* AseDirectionConfiguration that matched the capabilities */
|
||||
std::optional<LeAudioBroadcastConfigurationSetting>
|
||||
LeAudioOffloadAudioProvider::
|
||||
getCapabilitiesMatchedBroadcastConfigurationSettings(
|
||||
LeAudioBroadcastConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities&
|
||||
capabilities) {
|
||||
std::vector<IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration>
|
||||
filter_subgroup;
|
||||
for (auto& sub_cfg : setting.subgroupsConfigurations) {
|
||||
std::vector<IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration>
|
||||
filtered_bis_cfg;
|
||||
for (auto& bis_cfg : sub_cfg.bisConfigurations)
|
||||
if (isMatchedBISConfiguration(bis_cfg.bisConfiguration, capabilities)) {
|
||||
filtered_bis_cfg.push_back(bis_cfg);
|
||||
}
|
||||
if (!filtered_bis_cfg.empty()) {
|
||||
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration
|
||||
subgroup_cfg;
|
||||
subgroup_cfg.bisConfigurations = filtered_bis_cfg;
|
||||
filter_subgroup.push_back(subgroup_cfg);
|
||||
}
|
||||
}
|
||||
if (filter_subgroup.empty()) return std::nullopt;
|
||||
|
||||
// Create a new LeAudioAseConfigurationSetting and return
|
||||
LeAudioBroadcastConfigurationSetting filtered_setting(setting);
|
||||
filtered_setting.subgroupsConfigurations = filter_subgroup;
|
||||
|
||||
return filtered_setting;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus
|
||||
LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSinkAudioCapabilities,
|
||||
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
|
||||
in_requirement,
|
||||
LeAudioBroadcastConfigurationSetting* _aidl_return) {
|
||||
getBroadcastSettings();
|
||||
_aidl_return = nullptr;
|
||||
|
||||
// Match and filter capability
|
||||
std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
|
||||
if (!in_remoteSinkAudioCapabilities.has_value()) {
|
||||
LOG(WARNING) << __func__ << ": Empty capability";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
for (auto& setting : broadcast_settings) {
|
||||
for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
|
||||
if (!capability.has_value()) continue;
|
||||
auto filtered_setting =
|
||||
getCapabilitiesMatchedBroadcastConfigurationSettings(
|
||||
setting, capability.value());
|
||||
if (filtered_setting.has_value())
|
||||
filtered_settings.push_back(filtered_setting.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered_settings.empty()) {
|
||||
LOG(WARNING) << __func__ << ": Cannot match any remote capability";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// Match and return the first matched requirement
|
||||
if (in_requirement.subgroupConfigurationRequirements.empty()) {
|
||||
LOG(INFO) << __func__ << ": Empty requirement";
|
||||
*_aidl_return = filtered_settings[0];
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
for (auto& setting : filtered_settings) {
|
||||
// Further filter out bis configuration
|
||||
LeAudioBroadcastConfigurationSetting filtered_setting(setting);
|
||||
filtered_setting.subgroupsConfigurations.clear();
|
||||
for (auto& sub_cfg : setting.subgroupsConfigurations) {
|
||||
bool isMatched = false;
|
||||
for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
|
||||
// Matching number of BIS
|
||||
if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size())
|
||||
continue;
|
||||
// Currently will ignore quality and context hint.
|
||||
isMatched = true;
|
||||
break;
|
||||
}
|
||||
if (isMatched)
|
||||
filtered_setting.subgroupsConfigurations.push_back(sub_cfg);
|
||||
}
|
||||
// Return the first match
|
||||
if (!filtered_setting.subgroupsConfigurations.empty()) {
|
||||
LOG(INFO) << __func__ << ": Matched requirement";
|
||||
*_aidl_return = filtered_setting;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
}
|
||||
|
||||
LOG(WARNING) << __func__ << ": Cannot match any requirement";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
|
|
|
@ -16,7 +16,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
#include "aidl/android/hardware/bluetooth/audio/LeAudioAseConfiguration.h"
|
||||
#include "aidl/android/hardware/bluetooth/audio/MetadataLtv.h"
|
||||
#include "aidl/android/hardware/bluetooth/audio/SessionType.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
|
@ -24,6 +29,19 @@ namespace hardware {
|
|||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using LeAudioAseConfigurationSetting =
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
|
||||
using AseDirectionRequirement = IBluetoothAudioProvider::
|
||||
LeAudioConfigurationRequirement::AseDirectionRequirement;
|
||||
using AseDirectionConfiguration = IBluetoothAudioProvider::
|
||||
LeAudioAseConfigurationSetting::AseDirectionConfiguration;
|
||||
using AseQosDirectionRequirement = IBluetoothAudioProvider::
|
||||
LeAudioAseQosConfigurationRequirement::AseQosDirectionRequirement;
|
||||
using LeAudioAseQosConfiguration =
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfiguration;
|
||||
using LeAudioBroadcastConfigurationSetting =
|
||||
IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting;
|
||||
|
||||
class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
LeAudioOffloadAudioProvider();
|
||||
|
@ -33,11 +51,112 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
|||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
|
||||
ndk::ScopedAStatus setCodecPriority(const CodecId& in_codecId,
|
||||
int32_t in_priority) override;
|
||||
ndk::ScopedAStatus getLeAudioAseConfiguration(
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSinkAudioCapabilities,
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSourceAudioCapabilities,
|
||||
const std::vector<
|
||||
IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
|
||||
in_requirements,
|
||||
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
|
||||
_aidl_return) override;
|
||||
ndk::ScopedAStatus getLeAudioAseQosConfiguration(
|
||||
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
|
||||
in_qosRequirement,
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return)
|
||||
override;
|
||||
ndk::ScopedAStatus onSourceAseMetadataChanged(
|
||||
IBluetoothAudioProvider::AseState in_state, int32_t in_cigId,
|
||||
int32_t in_cisId,
|
||||
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata)
|
||||
override;
|
||||
ndk::ScopedAStatus onSinkAseMetadataChanged(
|
||||
IBluetoothAudioProvider::AseState in_state, int32_t in_cigId,
|
||||
int32_t in_cisId,
|
||||
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata)
|
||||
override;
|
||||
ndk::ScopedAStatus getLeAudioBroadcastConfiguration(
|
||||
const std::optional<std::vector<
|
||||
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
|
||||
in_remoteSinkAudioCapabilities,
|
||||
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
|
||||
in_requirement,
|
||||
LeAudioBroadcastConfigurationSetting* _aidl_return) override;
|
||||
|
||||
private:
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
std::map<CodecId, uint32_t> codec_priority_map_;
|
||||
std::vector<LeAudioBroadcastConfigurationSetting> broadcast_settings;
|
||||
|
||||
// Private matching function definitions
|
||||
bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec);
|
||||
bool isCapabilitiesMatchedContext(
|
||||
AudioContext setting_context,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
|
||||
bool isMatchedSamplingFreq(
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
|
||||
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
|
||||
capability_freq);
|
||||
bool isMatchedFrameDuration(
|
||||
CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
|
||||
CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
|
||||
capability_fduration);
|
||||
bool isMatchedAudioChannel(
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation& cfg_channel,
|
||||
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
|
||||
capability_channel);
|
||||
bool isMatchedCodecFramesPerSDU(
|
||||
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu,
|
||||
CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU&
|
||||
capability_frame_sdu);
|
||||
bool isMatchedOctetsPerCodecFrame(
|
||||
CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets,
|
||||
CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame&
|
||||
capability_octets);
|
||||
bool isCapabilitiesMatchedCodecConfiguration(
|
||||
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
|
||||
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
|
||||
bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg,
|
||||
LeAudioAseConfiguration requirement_cfg);
|
||||
bool isMatchedBISConfiguration(
|
||||
LeAudioBisConfiguration bis_cfg,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
|
||||
void filterCapabilitiesAseDirectionConfiguration(
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
direction_configurations,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
valid_direction_configurations);
|
||||
void filterRequirementAseDirectionConfiguration(
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
direction_configurations,
|
||||
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
|
||||
requirements,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
valid_direction_configurations);
|
||||
std::optional<LeAudioAseConfigurationSetting>
|
||||
getCapabilitiesMatchedAseConfigurationSettings(
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
|
||||
uint8_t direction);
|
||||
std::optional<LeAudioAseConfigurationSetting>
|
||||
getRequirementMatchedAseConfigurationSettings(
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
|
||||
requirement);
|
||||
bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
|
||||
AseQosDirectionRequirement requirement_qos);
|
||||
std::optional<LeAudioBroadcastConfigurationSetting>
|
||||
getCapabilitiesMatchedBroadcastConfigurationSettings(
|
||||
LeAudioBroadcastConfigurationSetting& setting,
|
||||
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
|
||||
void getBroadcastSettings();
|
||||
};
|
||||
|
||||
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<manifest version="1.0" type="framework">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.bluetooth.audio</name>
|
||||
<version>3</version>
|
||||
<version>4</version>
|
||||
<fqname>IBluetoothAudioProviderFactory/sysbta</fqname>
|
||||
</hal>
|
||||
<hal>
|
||||
|
|
|
@ -9,8 +9,11 @@ cc_library_shared {
|
|||
"utils.cc",
|
||||
],
|
||||
header_libs: ["libhardware_headers"],
|
||||
defaults: [
|
||||
"latest_android_hardware_audio_common_ndk_shared",
|
||||
"latest_android_hardware_bluetooth_audio_ndk_shared",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio-V3-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
"libaudioutils",
|
||||
"libbase",
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#define LOG_TAG "BTAudioHw"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <errno.h>
|
||||
#include <hardware/hardware.h>
|
||||
#include <log/log.h>
|
||||
#include <malloc.h>
|
||||
|
@ -39,7 +38,8 @@ static int adev_set_parameters(struct audio_hw_device* dev,
|
|||
|
||||
LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
|
||||
<< "]";
|
||||
if (params.find("A2dpSuspended") == params.end()) {
|
||||
if (params.find("A2dpSuspended") == params.end() &&
|
||||
params.find("LeAudioSuspended") == params.end()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,67 @@ static int adev_get_mic_mute(const struct audio_hw_device* dev, bool* state) {
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_create_audio_patch(struct audio_hw_device* device,
|
||||
unsigned int num_sources,
|
||||
const struct audio_port_config* sources,
|
||||
unsigned int num_sinks,
|
||||
const struct audio_port_config* sinks,
|
||||
audio_patch_handle_t* handle) {
|
||||
if (device == nullptr || sources == nullptr || sinks == nullptr ||
|
||||
handle == nullptr || num_sources != 1 || num_sinks == 0 ||
|
||||
num_sinks > AUDIO_PATCH_PORTS_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
|
||||
if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (sources[0].type == AUDIO_PORT_TYPE_MIX) {
|
||||
for (unsigned int i = 0; i < num_sinks; i++) {
|
||||
if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
|
||||
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
|
||||
if (*handle == AUDIO_PATCH_HANDLE_NONE) {
|
||||
*handle = ++bluetooth_device->next_unique_id;
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << ": device=" << std::hex << sinks[0].ext.device.type
|
||||
<< " handle: " << *handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adev_release_audio_patch(struct audio_hw_device* device,
|
||||
audio_patch_handle_t patch_handle) {
|
||||
if (device == nullptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
LOG(INFO) << __func__ << ": patch_handle=" << patch_handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adev_get_audio_port_v7(struct audio_hw_device* device,
|
||||
struct audio_port_v7* port) {
|
||||
if (device == nullptr || port == nullptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_get_audio_port(struct audio_hw_device* device,
|
||||
struct audio_port* port) {
|
||||
if (device == nullptr || port == nullptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
|
||||
|
||||
static int adev_close(hw_device_t* device) {
|
||||
|
@ -117,7 +178,7 @@ static int adev_open(const hw_module_t* module, const char* name,
|
|||
if (!adev) return -ENOMEM;
|
||||
|
||||
adev->common.tag = HARDWARE_DEVICE_TAG;
|
||||
adev->common.version = AUDIO_DEVICE_API_VERSION_2_0;
|
||||
adev->common.version = AUDIO_DEVICE_API_VERSION_3_2;
|
||||
adev->common.module = (struct hw_module_t*)module;
|
||||
adev->common.close = adev_close;
|
||||
|
||||
|
@ -138,6 +199,10 @@ static int adev_open(const hw_module_t* module, const char* name,
|
|||
adev->dump = adev_dump;
|
||||
adev->set_master_mute = adev_set_master_mute;
|
||||
adev->get_master_mute = adev_get_master_mute;
|
||||
adev->create_audio_patch = adev_create_audio_patch;
|
||||
adev->release_audio_patch = adev_release_audio_patch;
|
||||
adev->get_audio_port_v7 = adev_get_audio_port_v7;
|
||||
adev->get_audio_port = adev_get_audio_port;
|
||||
|
||||
*device = &adev->common;
|
||||
return 0;
|
||||
|
|
|
@ -34,12 +34,17 @@ namespace bluetooth {
|
|||
namespace audio {
|
||||
namespace aidl {
|
||||
|
||||
using ::aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using ::aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
|
||||
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
|
||||
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
|
||||
using ::aidl::android::media::audio::common::AudioContentType;
|
||||
using ::aidl::android::media::audio::common::AudioSource;
|
||||
using ::aidl::android::media::audio::common::AudioUsage;
|
||||
|
||||
using ::android::base::StringPrintf;
|
||||
using ControlResultCallback = std::function<void(
|
||||
|
@ -94,6 +99,20 @@ audio_format_t BitsPerSampleToAudioFormat(uint8_t bits_per_sample,
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> CovertAudioTagFromV7(char* tags_v7) {
|
||||
std::vector<std::string> tags;
|
||||
char tags_copy[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE];
|
||||
strlcpy(tags_copy, tags_v7, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
|
||||
char* tag = strtok(tags_copy, ";");
|
||||
|
||||
while (tag != NULL) {
|
||||
tags.push_back(tag);
|
||||
tag = strtok(NULL, ";");
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
// The maximum time to wait in std::condition_variable::wait_for()
|
||||
constexpr unsigned int kMaxWaitingTimeMs = 4500;
|
||||
|
||||
|
@ -577,7 +596,7 @@ bool BluetoothAudioPortAidl::GetPresentationPosition(
|
|||
}
|
||||
|
||||
void BluetoothAudioPortAidl::UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const {
|
||||
const source_metadata_v7* source_metadata) const {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
|
@ -586,13 +605,25 @@ void BluetoothAudioPortAidl::UpdateSourceMetadata(
|
|||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << ", " << source_metadata->track_count
|
||||
<< " track(s)";
|
||||
if (source_metadata->track_count == 0) return;
|
||||
ssize_t track_count = source_metadata->track_count;
|
||||
if (track_count == 0) return;
|
||||
SourceMetadata hal_source_metadata;
|
||||
hal_source_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
hal_source_metadata.tracks[i].usage =
|
||||
static_cast<AudioUsage>(source_metadata->tracks[i].base.usage);
|
||||
hal_source_metadata.tracks[i].contentType = static_cast<AudioContentType>(
|
||||
source_metadata->tracks[i].base.content_type);
|
||||
hal_source_metadata.tracks[i].tags =
|
||||
std::move(CovertAudioTagFromV7(source_metadata->tracks[i].tags));
|
||||
}
|
||||
|
||||
BluetoothAudioSessionControl::UpdateSourceMetadata(session_type_,
|
||||
*source_metadata);
|
||||
hal_source_metadata);
|
||||
}
|
||||
|
||||
void BluetoothAudioPortAidl::UpdateSinkMetadata(
|
||||
const sink_metadata* sink_metadata) const {
|
||||
const sink_metadata_v7* sink_metadata) const {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
|
@ -601,9 +632,20 @@ void BluetoothAudioPortAidl::UpdateSinkMetadata(
|
|||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << ", " << sink_metadata->track_count
|
||||
<< " track(s)";
|
||||
if (sink_metadata->track_count == 0) return;
|
||||
ssize_t track_count = sink_metadata->track_count;
|
||||
if (track_count == 0) return;
|
||||
SinkMetadata hal_sink_metadata;
|
||||
hal_sink_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
hal_sink_metadata.tracks[i].source =
|
||||
static_cast<AudioSource>(sink_metadata->tracks[i].base.source);
|
||||
hal_sink_metadata.tracks[i].gain = sink_metadata->tracks[i].base.gain;
|
||||
hal_sink_metadata.tracks[i].tags =
|
||||
std::move(CovertAudioTagFromV7(sink_metadata->tracks[i].tags));
|
||||
}
|
||||
|
||||
BluetoothAudioSessionControl::UpdateSinkMetadata(session_type_,
|
||||
*sink_metadata);
|
||||
hal_sink_metadata);
|
||||
}
|
||||
|
||||
BluetoothStreamState BluetoothAudioPortAidl::GetState() const { return state_; }
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
|
||||
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <hardware/audio.h>
|
||||
|
@ -93,12 +95,6 @@ class BluetoothAudioPort {
|
|||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* source has been changed.
|
||||
***/
|
||||
virtual void UpdateSourceMetadata(const source_metadata*) const {};
|
||||
|
||||
/***
|
||||
* Return the current BluetoothStreamState
|
||||
***/
|
||||
|
@ -113,6 +109,8 @@ class BluetoothAudioPort {
|
|||
|
||||
virtual bool IsA2dp() const { return false; }
|
||||
|
||||
virtual bool IsLeAudio() const { return false; }
|
||||
|
||||
virtual bool GetPreferredDataIntervalUs(size_t* interval_us) const {
|
||||
return false;
|
||||
};
|
||||
|
@ -146,14 +144,13 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort {
|
|||
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* byte,
|
||||
timespec* timestamp) const override;
|
||||
|
||||
void UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const override;
|
||||
void UpdateSourceMetadata(const source_metadata_v7* source_metadata) const;
|
||||
|
||||
/***
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* sink has been changed.
|
||||
***/
|
||||
virtual void UpdateSinkMetadata(const sink_metadata* sink_metadata) const;
|
||||
virtual void UpdateSinkMetadata(const sink_metadata_v7* sink_metadata) const;
|
||||
|
||||
BluetoothStreamState GetState() const override;
|
||||
|
||||
|
@ -165,6 +162,20 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort {
|
|||
SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
bool IsLeAudio() const override {
|
||||
return session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::
|
||||
LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -486,7 +486,7 @@ bool BluetoothAudioPortHidl::GetPresentationPosition(
|
|||
return retval;
|
||||
}
|
||||
|
||||
void BluetoothAudioPortHidl::UpdateSourceMetadata(
|
||||
void BluetoothAudioPortHidl::UpdateTracksMetadata(
|
||||
const source_metadata* source_metadata) const {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
|
|
|
@ -55,8 +55,7 @@ class BluetoothAudioPortHidl : public BluetoothAudioPort {
|
|||
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* bytes,
|
||||
timespec* timestamp) const override;
|
||||
|
||||
void UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const override;
|
||||
void UpdateTracksMetadata(const source_metadata* source_metadata) const;
|
||||
|
||||
BluetoothStreamState GetState() const override;
|
||||
|
||||
|
@ -69,6 +68,17 @@ class BluetoothAudioPortHidl : public BluetoothAudioPort {
|
|||
SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH;
|
||||
}
|
||||
|
||||
bool IsLeAudio() const override {
|
||||
return session_type_hidl_ ==
|
||||
SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_hidl_ ==
|
||||
SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH ||
|
||||
session_type_hidl_ ==
|
||||
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_hidl_ ==
|
||||
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <log/log.h>
|
||||
#include <string.h>
|
||||
|
@ -34,6 +33,7 @@
|
|||
#include "utils.h"
|
||||
|
||||
using ::android::base::StringPrintf;
|
||||
using ::android::bluetooth::audio::utils::FrameCount;
|
||||
using ::android::bluetooth::audio::utils::GetAudioParamString;
|
||||
using ::android::bluetooth::audio::utils::ParseAudioParams;
|
||||
|
||||
|
@ -335,7 +335,7 @@ static int out_set_parameters(struct audio_stream* stream,
|
|||
}
|
||||
}
|
||||
|
||||
if (params.find("routing") != params.end()) {
|
||||
if (params.find(AUDIO_PARAMETER_STREAM_ROUTING) != params.end()) {
|
||||
auto routing_param = params.find("routing");
|
||||
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
||||
<< ", stream param '" << routing_param->first.c_str() << "="
|
||||
|
@ -365,8 +365,33 @@ static int out_set_parameters(struct audio_stream* stream,
|
|||
}
|
||||
}
|
||||
|
||||
if (params.find("closing") != params.end()) {
|
||||
if (params["closing"] == "true") {
|
||||
if (params.find("LeAudioSuspended") != params.end() &&
|
||||
out->bluetooth_output_->IsLeAudio()) {
|
||||
LOG(INFO) << __func__ << ": LeAudioSuspended found LEAudio="
|
||||
<< out->bluetooth_output_->IsLeAudio();
|
||||
if (params["LeAudioSuspended"] == "true") {
|
||||
LOG(INFO) << __func__ << ": LeAudioSuspended true, state="
|
||||
<< out->bluetooth_output_->GetState()
|
||||
<< " stream param standby";
|
||||
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
|
||||
LOG(INFO) << __func__ << ": Stream is started, suspending LE Audio";
|
||||
} else if (out->bluetooth_output_->GetState() !=
|
||||
BluetoothStreamState::DISABLED) {
|
||||
LOG(INFO) << __func__ << ": Stream is disabled, suspending LE Audio";
|
||||
}
|
||||
} else {
|
||||
LOG(INFO) << __func__ << ": LeAudioSuspended false, state="
|
||||
<< out->bluetooth_output_->GetState()
|
||||
<< " stream param standby";
|
||||
if (out->bluetooth_output_->GetState() ==
|
||||
BluetoothStreamState::DISABLED) {
|
||||
LOG(INFO) << __func__ << ": Stream is disabled, unsuspending LE Audio";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.find(AUDIO_PARAMETER_KEY_CLOSING) != params.end()) {
|
||||
if (params[AUDIO_PARAMETER_KEY_CLOSING] == "true") {
|
||||
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
||||
<< " stream param closing, disallow any writes?";
|
||||
if (out->bluetooth_output_->GetState() !=
|
||||
|
@ -378,8 +403,8 @@ static int out_set_parameters(struct audio_stream* stream,
|
|||
}
|
||||
}
|
||||
|
||||
if (params.find("exiting") != params.end()) {
|
||||
if (params["exiting"] == "1") {
|
||||
if (params.find(AUDIO_PARAMETER_KEY_EXITING) != params.end()) {
|
||||
if (params[AUDIO_PARAMETER_KEY_EXITING] == "1") {
|
||||
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
||||
<< " stream param exiting";
|
||||
if (out->bluetooth_output_->GetState() !=
|
||||
|
@ -678,21 +703,32 @@ static int out_get_presentation_position(const struct audio_stream_out* stream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void out_update_source_metadata(
|
||||
static void out_update_source_metadata_v7(
|
||||
struct audio_stream_out* stream,
|
||||
const struct source_metadata* source_metadata) {
|
||||
const struct source_metadata_v7* source_metadata_v7) {
|
||||
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
||||
std::unique_lock<std::mutex> lock(out->mutex_);
|
||||
if (source_metadata == nullptr || source_metadata->track_count == 0) {
|
||||
if (source_metadata_v7 == nullptr || source_metadata_v7->track_count == 0) {
|
||||
return;
|
||||
}
|
||||
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
||||
<< ", " << source_metadata->track_count << " track(s)";
|
||||
out->bluetooth_output_->UpdateSourceMetadata(source_metadata);
|
||||
}
|
||||
<< ", " << source_metadata_v7->track_count << " track(s)";
|
||||
if (!out->is_aidl) {
|
||||
struct source_metadata source_metadata;
|
||||
source_metadata.track_count = source_metadata_v7->track_count;
|
||||
struct playback_track_metadata playback_track;
|
||||
playback_track_metadata_from_v7(&playback_track,
|
||||
source_metadata_v7->tracks);
|
||||
source_metadata.tracks = &playback_track;
|
||||
|
||||
static size_t frame_count(size_t microseconds, uint32_t sample_rate) {
|
||||
return (microseconds * sample_rate) / 1000000;
|
||||
static_cast<::android::bluetooth::audio::hidl::BluetoothAudioPortHidl*>(
|
||||
out->bluetooth_output_.get())
|
||||
->UpdateTracksMetadata(&source_metadata);
|
||||
} else {
|
||||
static_cast<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl*>(
|
||||
out->bluetooth_output_.get())
|
||||
->UpdateSourceMetadata(source_metadata_v7);
|
||||
}
|
||||
}
|
||||
|
||||
int adev_open_output_stream(struct audio_hw_device* dev,
|
||||
|
@ -740,7 +776,10 @@ int adev_open_output_stream(struct audio_hw_device* dev,
|
|||
out->stream_out_.pause = out_pause;
|
||||
out->stream_out_.resume = out_resume;
|
||||
out->stream_out_.get_presentation_position = out_get_presentation_position;
|
||||
out->stream_out_.update_source_metadata = out_update_source_metadata;
|
||||
out->stream_out_.update_source_metadata_v7 = out_update_source_metadata_v7;
|
||||
/** Fix Coverity Scan Issue @{ */
|
||||
out->channel_mask_ = AUDIO_CHANNEL_NONE;
|
||||
/** @} */
|
||||
|
||||
if (!out->bluetooth_output_->LoadAudioConfig(config)) {
|
||||
LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
||||
|
@ -782,7 +821,7 @@ int adev_open_output_stream(struct audio_hw_device* dev,
|
|||
}
|
||||
|
||||
out->frames_count_ =
|
||||
frame_count(out->preferred_data_interval_us, out->sample_rate_);
|
||||
FrameCount(out->preferred_data_interval_us, out->sample_rate_);
|
||||
|
||||
out->frames_rendered_ = 0;
|
||||
out->frames_presented_ = 0;
|
||||
|
@ -1182,8 +1221,9 @@ static int in_set_microphone_field_dimension(
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static void in_update_sink_metadata(struct audio_stream_in* stream,
|
||||
const struct sink_metadata* sink_metadata) {
|
||||
static void in_update_sink_metadata_v7(
|
||||
struct audio_stream_in* stream,
|
||||
const struct sink_metadata_v7* sink_metadata) {
|
||||
LOG(INFO) << __func__;
|
||||
if (sink_metadata == nullptr || sink_metadata->track_count == 0) {
|
||||
return;
|
||||
|
@ -1254,7 +1294,7 @@ int adev_open_input_stream(struct audio_hw_device* dev,
|
|||
in->stream_in_.set_microphone_direction = in_set_microphone_direction;
|
||||
in->stream_in_.set_microphone_field_dimension =
|
||||
in_set_microphone_field_dimension;
|
||||
in->stream_in_.update_sink_metadata = in_update_sink_metadata;
|
||||
in->stream_in_.update_sink_metadata_v7 = in_update_sink_metadata_v7;
|
||||
|
||||
if (!in->bluetooth_input_->LoadAudioConfig(config)) {
|
||||
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
||||
|
@ -1277,7 +1317,7 @@ int adev_open_input_stream(struct audio_hw_device* dev,
|
|||
}
|
||||
|
||||
in->frames_count_ =
|
||||
frame_count(in->preferred_data_interval_us, in->sample_rate_);
|
||||
FrameCount(in->preferred_data_interval_us, in->sample_rate_);
|
||||
in->frames_presented_ = 0;
|
||||
|
||||
BluetoothStreamIn* in_ptr = in.release();
|
||||
|
|
|
@ -81,6 +81,7 @@ struct BluetoothAudioDevice {
|
|||
std::mutex mutex_;
|
||||
std::list<BluetoothStreamOut*> opened_stream_outs_ =
|
||||
std::list<BluetoothStreamOut*>(0);
|
||||
uint32_t next_unique_id = 1;
|
||||
};
|
||||
|
||||
struct BluetoothStreamIn {
|
||||
|
|
|
@ -57,6 +57,10 @@ std::string GetAudioParamString(
|
|||
return sout.str();
|
||||
}
|
||||
|
||||
size_t FrameCount(uint64_t microseconds, uint32_t sample_rate) {
|
||||
return (microseconds * sample_rate) / 1000000;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
|
|
|
@ -42,6 +42,7 @@ std::unordered_map<std::string, std::string> ParseAudioParams(
|
|||
std::string GetAudioParamString(
|
||||
std::unordered_map<std::string, std::string>& params_map);
|
||||
|
||||
size_t FrameCount(uint64_t microseconds, uint32_t sample_rate);
|
||||
} // namespace utils
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
|
|
142
bluetooth/audio/hw/utils_unittest.cc
Normal file
142
bluetooth/audio/hw/utils_unittest.cc
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::bluetooth::audio::utils::FrameCount;
|
||||
using ::android::bluetooth::audio::utils::ParseAudioParams;
|
||||
|
||||
class UtilsTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {}
|
||||
void TearDown() override { map_.clear(); }
|
||||
|
||||
std::unordered_map<std::string, std::string> map_;
|
||||
};
|
||||
|
||||
TEST_F(UtilsTest, HashMapEmptyParams) {
|
||||
std::string params = "";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapDelimitOnly) {
|
||||
std::string params = ";";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapNotKeyValuePair) {
|
||||
std::string params = "key0";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]=""}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_NE(map_.find("key0"), map_.end());
|
||||
EXPECT_EQ(map_["key0"].length(), 0);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapEmptyValue) {
|
||||
std::string params = "key0=";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]=""}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_NE(map_.find("key0"), map_.end());
|
||||
EXPECT_EQ(map_["key0"].length(), 0);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapEmptyValueWithDelimit) {
|
||||
std::string params = "key0=;";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]=""}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_NE(map_.find("key0"), map_.end());
|
||||
EXPECT_EQ(map_["key0"].length(), 0);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapOneKeyValuePair) {
|
||||
std::string params = "key0=value0";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]="value0"}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_EQ(map_["key0"], "value0");
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapOneKeyValuePairWithDelimit) {
|
||||
std::string params = "key0=value0;";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]="value0"}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_EQ(map_["key0"], "value0");
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapTwoKeyValuePairs) {
|
||||
std::string params = "key0=value0;key1=value1";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key0]="value0", [key1]="value1"}
|
||||
EXPECT_EQ(map_.size(), 2);
|
||||
EXPECT_EQ(map_["key0"], "value0");
|
||||
EXPECT_EQ(map_["key1"], "value1");
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapEmptyKey) {
|
||||
std::string params = "=value";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapEmptyKeyWithDelimit) {
|
||||
std::string params = "=value;";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapEquivalentOnly) {
|
||||
std::string params = "=";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapNoKeyValuePair) {
|
||||
std::string params = "=;";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {}
|
||||
EXPECT_TRUE(map_.empty());
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, HashMapTwoPairsWithFirstKeyEmpty) {
|
||||
std::string params = "=value0;key1=value1";
|
||||
map_ = ParseAudioParams(params);
|
||||
// map = {[key1]="value1"}
|
||||
EXPECT_EQ(map_.size(), 1);
|
||||
EXPECT_EQ(map_["key1"], "value1");
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, FrameCountTest) {
|
||||
EXPECT_EQ(FrameCount(120000, 44100), 5292);
|
||||
EXPECT_EQ(FrameCount(7500, 32000), 240);
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -29,9 +29,22 @@ cc_library_shared {
|
|||
"aidl_session/BluetoothAudioCodecs.cpp",
|
||||
"aidl_session/BluetoothAudioSession.cpp",
|
||||
"aidl_session/HidlToAidlMiddleware.cpp",
|
||||
"aidl_session/BluetoothLeAudioCodecsProvider.cpp",
|
||||
"aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp",
|
||||
],
|
||||
export_include_dirs: ["aidl_session/"],
|
||||
header_libs: ["libhardware_headers"],
|
||||
header_libs: [
|
||||
"libhardware_headers",
|
||||
"libxsdc-utils",
|
||||
],
|
||||
generated_sources: ["le_audio_codec_capabilities_system"],
|
||||
generated_headers: [
|
||||
"le_audio_codec_capabilities_system",
|
||||
"AIDLLeAudioSetConfigSchemas_h_system",
|
||||
],
|
||||
defaults: [
|
||||
"latest_android_hardware_bluetooth_audio_ndk_shared",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio@2.0",
|
||||
"android.hardware.bluetooth.audio@2.1",
|
||||
|
@ -40,7 +53,83 @@ cc_library_shared {
|
|||
"libbinder_ndk",
|
||||
"libfmq",
|
||||
"liblog",
|
||||
"android.hardware.bluetooth.audio-V3-ndk",
|
||||
"libhidlbase",
|
||||
"libxml2",
|
||||
"libflatbuffers-cpp",
|
||||
],
|
||||
}
|
||||
|
||||
xsd_config {
|
||||
name: "le_audio_codec_capabilities_system",
|
||||
srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"],
|
||||
package_name: "aidl.android.hardware.bluetooth.audio.setting",
|
||||
api_dir: "le_audio_codec_capabilities/schema",
|
||||
root_elements: ["leAudioOffloadSetting"],
|
||||
}
|
||||
genrule {
|
||||
name: "AIDLLeAudioSetConfigSchemas_h_system",
|
||||
tools: [
|
||||
"flatc",
|
||||
],
|
||||
cmd: "$(location flatc) -I hardware/interfaces/bluetooth/audio/utils/ -o $(genDir) --cpp $(in) ",
|
||||
srcs: [
|
||||
"le_audio_configuration_set/audio_set_configurations.fbs",
|
||||
"le_audio_configuration_set/audio_set_scenarios.fbs",
|
||||
],
|
||||
out: [
|
||||
"audio_set_configurations_generated.h",
|
||||
"audio_set_scenarios_generated.h",
|
||||
],
|
||||
}
|
||||
// Binary generation
|
||||
genrule {
|
||||
name: "AIDLLeAudioSetScenariosSchema_bfbs_system",
|
||||
tools: [
|
||||
"flatc",
|
||||
],
|
||||
cmd: "$(location flatc) -I device/peter/gsi/bluetooth/audio/utils/ -b --schema -o $(genDir) $(in) ",
|
||||
srcs: [
|
||||
"le_audio_configuration_set/audio_set_scenarios.fbs",
|
||||
],
|
||||
out: [
|
||||
"audio_set_scenarios.bfbs",
|
||||
],
|
||||
}
|
||||
genrule {
|
||||
name: "AIDLLeAudioSetConfigsSchema_bfbs_system",
|
||||
tools: [
|
||||
"flatc",
|
||||
],
|
||||
cmd: "$(location flatc) -I device/peter/gsi/bluetooth/audio/utils/ -b --schema -o $(genDir) $(in) ",
|
||||
srcs: [
|
||||
"le_audio_configuration_set/audio_set_configurations.fbs",
|
||||
],
|
||||
out: [
|
||||
"audio_set_configurations.bfbs",
|
||||
],
|
||||
}
|
||||
// Add to prebuilt etc
|
||||
prebuilt_etc {
|
||||
name: "aidl_audio_set_scenarios_bfbs_system",
|
||||
src: ":AIDLLeAudioSetScenariosSchema_bfbs_system",
|
||||
filename: "aidl_audio_set_scenarios.bfbs",
|
||||
sub_dir: "aidl/le_audio",
|
||||
}
|
||||
prebuilt_etc {
|
||||
name: "aidl_audio_set_scenarios_json_system",
|
||||
src: "le_audio_configuration_set/audio_set_scenarios.json",
|
||||
filename: "aidl_audio_set_scenarios.json",
|
||||
sub_dir: "aidl/le_audio",
|
||||
}
|
||||
prebuilt_etc {
|
||||
name: "aidl_audio_set_configurations_bfbs_system",
|
||||
src: ":AIDLLeAudioSetConfigsSchema_bfbs",
|
||||
filename: "aidl_audio_set_configurations.bfbs",
|
||||
sub_dir: "aidl/le_audio",
|
||||
}
|
||||
prebuilt_etc {
|
||||
name: "aidl_audio_set_configurations_json_system",
|
||||
src: "le_audio_configuration_set/audio_set_configurations.json",
|
||||
filename: "aidl_audio_set_configurations.json",
|
||||
sub_dir: "aidl/le_audio",
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
#include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "BluetoothLeAudioAseConfigurationSettingProvider.h"
|
||||
#include "BluetoothLeAudioCodecsProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
|
@ -39,7 +42,7 @@ namespace bluetooth {
|
|||
namespace audio {
|
||||
|
||||
static const PcmCapabilities kDefaultSoftwarePcmCapabilities = {
|
||||
.sampleRateHz = {16000, 24000, 32000, 44100, 48000, 88200, 96000},
|
||||
.sampleRateHz = {8000, 16000, 24000, 32000, 44100, 48000, 88200, 96000},
|
||||
.channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
|
||||
.bitsPerSample = {16, 24, 32},
|
||||
.dataIntervalUs = {},
|
||||
|
@ -95,67 +98,8 @@ const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
|
|||
{.codecType = CodecType::OPUS, .capabilities = {}}};
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities;
|
||||
|
||||
static const UnicastCapability kInvalidUnicastCapability = {
|
||||
.codecType = CodecType::UNKNOWN};
|
||||
|
||||
static const BroadcastCapability kInvalidBroadcastCapability = {
|
||||
.codecType = CodecType::UNKNOWN};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30
|
||||
static const Lc3Capabilities kLc3Capability_16_1 = {
|
||||
.samplingFrequencyHz = {16000},
|
||||
.frameDurationUs = {7500},
|
||||
.octetsPerFrame = {30}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40
|
||||
static const Lc3Capabilities kLc3Capability_16_2 = {
|
||||
.samplingFrequencyHz = {16000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {40}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60
|
||||
static const Lc3Capabilities kLc3Capability_24_2 = {
|
||||
.samplingFrequencyHz = {24000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {60}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80
|
||||
static const Lc3Capabilities kLc3Capability_32_2 = {
|
||||
.samplingFrequencyHz = {32000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {80}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120
|
||||
static const Lc3Capabilities kLc3Capability_48_4 = {
|
||||
.samplingFrequencyHz = {48000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {120}};
|
||||
|
||||
static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = {
|
||||
kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2,
|
||||
kLc3Capability_16_2, kLc3Capability_16_1};
|
||||
|
||||
static AudioLocation stereoAudio = static_cast<AudioLocation>(
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
|
||||
static AudioLocation monoAudio = AudioLocation::UNKNOWN;
|
||||
|
||||
// Stores the supported setting of audio location, connected device, and the
|
||||
// channel count for each device
|
||||
std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>>
|
||||
supportedDeviceSetting = {
|
||||
// Stereo, two connected device, one for L one for R
|
||||
std::make_tuple(stereoAudio, 2, 1),
|
||||
// Stereo, one connected device for both L and R
|
||||
std::make_tuple(stereoAudio, 1, 2),
|
||||
// Mono
|
||||
std::make_tuple(monoAudio, 1, 1)};
|
||||
std::unordered_map<SessionType, std::vector<CodecInfo>>
|
||||
kDefaultOffloadLeAudioCodecInfoMap;
|
||||
|
||||
template <class T>
|
||||
bool BluetoothAudioCodecs::ContainedInVector(
|
||||
|
@ -321,19 +265,6 @@ bool BluetoothAudioCodecs::IsOffloadOpusConfigurationValid(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
|
||||
const SessionType& session_type, const LeAudioConfiguration&) {
|
||||
if (session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<PcmCapabilities>
|
||||
BluetoothAudioCodecs::GetSoftwarePcmCapabilities() {
|
||||
return {kDefaultSoftwarePcmCapabilities};
|
||||
|
@ -384,6 +315,8 @@ BluetoothAudioCodecs::GetA2dpOffloadCodecCapabilities(
|
|||
case CodecType::VENDOR:
|
||||
case CodecType::LC3:
|
||||
case CodecType::APTX_ADAPTIVE:
|
||||
case CodecType::APTX_ADAPTIVE_LE:
|
||||
case CodecType::APTX_ADAPTIVE_LEX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -449,6 +382,8 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
|||
}
|
||||
break;
|
||||
case CodecType::APTX_ADAPTIVE:
|
||||
case CodecType::APTX_ADAPTIVE_LE:
|
||||
case CodecType::APTX_ADAPTIVE_LEX:
|
||||
case CodecType::LC3:
|
||||
case CodecType::UNKNOWN:
|
||||
case CodecType::VENDOR:
|
||||
|
@ -457,19 +392,6 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
|||
return false;
|
||||
}
|
||||
|
||||
UnicastCapability composeUnicastLc3Capability(
|
||||
AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount,
|
||||
const Lc3Capabilities& capability) {
|
||||
return {
|
||||
.codecType = CodecType::LC3,
|
||||
.supportedChannel = audioLocation,
|
||||
.deviceCount = deviceCnt,
|
||||
.channelCountPerDevice = channelCount,
|
||||
.leAudioCodecCapabilities =
|
||||
UnicastCapability::LeAudioCodecCapabilities(capability),
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
|
||||
const SessionType& session_type) {
|
||||
|
@ -483,37 +405,44 @@ BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
|
|||
}
|
||||
|
||||
if (kDefaultOffloadLeAudioCapabilities.empty()) {
|
||||
for (auto [audioLocation, deviceCnt, channelCount] :
|
||||
supportedDeviceSetting) {
|
||||
for (auto capability : supportedLc3CapabilityList) {
|
||||
UnicastCapability lc3Capability = composeUnicastLc3Capability(
|
||||
audioLocation, deviceCnt, channelCount, capability);
|
||||
UnicastCapability lc3MonoDecodeCapability =
|
||||
composeUnicastLc3Capability(monoAudio, 1, 1, capability);
|
||||
auto le_audio_offload_setting =
|
||||
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
|
||||
kDefaultOffloadLeAudioCapabilities =
|
||||
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
|
||||
le_audio_offload_setting);
|
||||
}
|
||||
return kDefaultOffloadLeAudioCapabilities;
|
||||
}
|
||||
|
||||
// Adds the capability for encode only
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = lc3Capability,
|
||||
.unicastDecodeCapability = kInvalidUnicastCapability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
|
||||
// Adds the capability for decode only
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = kInvalidUnicastCapability,
|
||||
.unicastDecodeCapability = lc3Capability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
|
||||
// Adds the capability for the case that encode and decode exist at the
|
||||
// same time
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = lc3Capability,
|
||||
.unicastDecodeCapability = lc3MonoDecodeCapability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
}
|
||||
}
|
||||
std::vector<CodecInfo> BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
|
||||
const SessionType& session_type) {
|
||||
if (session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
return std::vector<CodecInfo>();
|
||||
}
|
||||
|
||||
return kDefaultOffloadLeAudioCapabilities;
|
||||
if (kDefaultOffloadLeAudioCodecInfoMap.empty()) {
|
||||
auto le_audio_offload_setting =
|
||||
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
|
||||
auto kDefaultOffloadLeAudioCodecInfoMap =
|
||||
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
|
||||
le_audio_offload_setting);
|
||||
}
|
||||
auto codec_info_map_iter =
|
||||
kDefaultOffloadLeAudioCodecInfoMap.find(session_type);
|
||||
if (codec_info_map_iter == kDefaultOffloadLeAudioCodecInfoMap.end())
|
||||
return std::vector<CodecInfo>();
|
||||
return codec_info_map_iter->second;
|
||||
}
|
||||
|
||||
std::vector<LeAudioAseConfigurationSetting>
|
||||
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings() {
|
||||
return AudioSetConfigurationProviderJson::
|
||||
GetLeAudioAseConfigurationSettings();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecInfo.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/OpusConfiguration.h>
|
||||
|
@ -33,6 +35,9 @@ namespace hardware {
|
|||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using LeAudioAseConfigurationSetting =
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
|
||||
|
||||
class BluetoothAudioCodecs {
|
||||
public:
|
||||
static std::vector<PcmCapabilities> GetSoftwarePcmCapabilities();
|
||||
|
@ -44,11 +49,13 @@ class BluetoothAudioCodecs {
|
|||
static bool IsOffloadCodecConfigurationValid(
|
||||
const SessionType& session_type, const CodecConfiguration& codec_config);
|
||||
|
||||
static bool IsOffloadLeAudioConfigurationValid(
|
||||
const SessionType& session_type, const LeAudioConfiguration&);
|
||||
|
||||
static std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
GetLeAudioOffloadCodecCapabilities(const SessionType& session_type);
|
||||
static std::vector<CodecInfo> GetLeAudioOffloadCodecInfo(
|
||||
const SessionType& session_type);
|
||||
|
||||
static std::vector<LeAudioAseConfigurationSetting>
|
||||
GetLeAudioAseConfigurationSettings();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
|
@ -94,6 +95,8 @@ const AudioConfiguration BluetoothAudioSession::GetAudioConfig() {
|
|||
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(CodecConfiguration{});
|
||||
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
|
||||
return AudioConfiguration(HfpConfiguration{});
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioConfiguration{});
|
||||
|
@ -114,8 +117,16 @@ void BluetoothAudioSession::ReportAudioConfigChanged(
|
|||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
|
||||
LOG(ERROR) << __func__ << " invalid audio config type for SessionType ="
|
||||
<< toString(session_type_);
|
||||
return;
|
||||
}
|
||||
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
|
@ -145,6 +156,7 @@ bool BluetoothAudioSession::IsSessionReady() {
|
|||
session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||
(data_mq_ != nullptr && data_mq_->isValid()));
|
||||
return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr;
|
||||
}
|
||||
|
@ -266,6 +278,8 @@ bool BluetoothAudioSession::UpdateAudioConfig(
|
|||
bool is_software_session =
|
||||
(session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
|
@ -274,23 +288,37 @@ bool BluetoothAudioSession::UpdateAudioConfig(
|
|||
bool is_offload_a2dp_session =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH);
|
||||
bool is_offload_le_audio_session =
|
||||
bool is_offload_hfp_session =
|
||||
session_type_ == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH;
|
||||
bool is_offload_le_audio_unicast_session =
|
||||
(session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
|
||||
bool is_offload_le_audio_broadcast_session =
|
||||
(session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
|
||||
auto audio_config_tag = audio_config.getTag();
|
||||
bool is_software_audio_config =
|
||||
(is_software_session &&
|
||||
audio_config_tag == AudioConfiguration::pcmConfig);
|
||||
bool is_a2dp_offload_audio_config =
|
||||
(is_offload_a2dp_session &&
|
||||
audio_config_tag == AudioConfiguration::a2dpConfig);
|
||||
bool is_le_audio_offload_audio_config =
|
||||
(is_offload_le_audio_session &&
|
||||
(audio_config_tag == AudioConfiguration::a2dp ||
|
||||
audio_config_tag == AudioConfiguration::a2dpConfig));
|
||||
bool is_hfp_offload_audio_config =
|
||||
(is_offload_hfp_session &&
|
||||
audio_config_tag == AudioConfiguration::hfpConfig);
|
||||
bool is_le_audio_offload_unicast_audio_config =
|
||||
(is_offload_le_audio_unicast_session &&
|
||||
audio_config_tag == AudioConfiguration::leAudioConfig);
|
||||
bool is_le_audio_offload_broadcast_audio_config =
|
||||
(is_offload_le_audio_broadcast_session &&
|
||||
audio_config_tag == AudioConfiguration::leAudioBroadcastConfig);
|
||||
if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
|
||||
!is_le_audio_offload_audio_config) {
|
||||
!is_hfp_offload_audio_config &&
|
||||
!is_le_audio_offload_unicast_audio_config &&
|
||||
!is_le_audio_offload_broadcast_audio_config) {
|
||||
return false;
|
||||
}
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
|
@ -423,8 +451,21 @@ void BluetoothAudioSession::ReportControlStatus(bool start_resp,
|
|||
}
|
||||
|
||||
void BluetoothAudioSession::ReportLowLatencyModeAllowedChanged(bool allowed) {
|
||||
if (session_type_ != SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
low_latency_allowed_ = allowed;
|
||||
// TODO(b/294498919): Remove this after there is API to update latency mode
|
||||
// after audio session started. If low_latency_allowed_ is true, the session
|
||||
// can support LOW_LATENCY and FREE LatencyMode.
|
||||
if (low_latency_allowed_) {
|
||||
if (std::find(latency_modes_.begin(), latency_modes_.end(),
|
||||
LatencyMode::LOW_LATENCY) == latency_modes_.end()) {
|
||||
LOG(INFO) << __func__ << " - insert LOW_LATENCY LatencyMode";
|
||||
latency_modes_.push_back(LatencyMode::LOW_LATENCY);
|
||||
}
|
||||
}
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
|
@ -461,23 +502,9 @@ bool BluetoothAudioSession::GetPresentationPosition(
|
|||
|
||||
void BluetoothAudioSession::UpdateSourceMetadata(
|
||||
const struct source_metadata& source_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t track_count = source_metadata.track_count;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
|
||||
<< track_count << " track(s)";
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
SourceMetadata hal_source_metadata;
|
||||
hal_source_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
|
@ -494,33 +521,14 @@ void BluetoothAudioSession::UpdateSourceMetadata(
|
|||
<< toString(hal_source_metadata.tracks[i].contentType)
|
||||
<< ", gain=" << hal_source_metadata.tracks[i].gain;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->updateSourceMetadata(hal_source_metadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
UpdateSourceMetadata(hal_source_metadata);
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::UpdateSinkMetadata(
|
||||
const struct sink_metadata& sink_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t track_count = sink_metadata.track_count;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
|
||||
<< track_count << " track(s)";
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
SinkMetadata hal_sink_metadata;
|
||||
hal_sink_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
|
@ -535,12 +543,57 @@ void BluetoothAudioSession::UpdateSinkMetadata(
|
|||
<< ", dest_device_address="
|
||||
<< sink_metadata.tracks[i].dest_device_address;
|
||||
}
|
||||
UpdateSinkMetadata(hal_sink_metadata);
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::UpdateSourceMetadata(
|
||||
const SourceMetadata& hal_source_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->updateSourceMetadata(hal_source_metadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::UpdateSinkMetadata(
|
||||
const SinkMetadata& hal_sink_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->updateSinkMetadata(hal_sink_metadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<LatencyMode> BluetoothAudioSession::GetSupportedLatencyModes() {
|
||||
|
@ -550,15 +603,32 @@ std::vector<LatencyMode> BluetoothAudioSession::GetSupportedLatencyModes() {
|
|||
<< " has NO session";
|
||||
return std::vector<LatencyMode>();
|
||||
}
|
||||
if (low_latency_allowed_) return latency_modes_;
|
||||
std::vector<LatencyMode> modes;
|
||||
for (LatencyMode mode : latency_modes_) {
|
||||
if (mode == LatencyMode::LOW_LATENCY)
|
||||
// ignore those low latency mode if Bluetooth stack doesn't allow
|
||||
continue;
|
||||
modes.push_back(mode);
|
||||
|
||||
std::vector<LatencyMode> supported_latency_modes;
|
||||
if (session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
for (LatencyMode mode : latency_modes_) {
|
||||
if (mode == LatencyMode::LOW_LATENCY) {
|
||||
// LOW_LATENCY is not supported for LE_HARDWARE_OFFLOAD_ENC sessions
|
||||
continue;
|
||||
}
|
||||
supported_latency_modes.push_back(mode);
|
||||
}
|
||||
} else {
|
||||
for (LatencyMode mode : latency_modes_) {
|
||||
if (!low_latency_allowed_ && mode == LatencyMode::LOW_LATENCY) {
|
||||
// ignore LOW_LATENCY mode if Bluetooth stack doesn't allow
|
||||
continue;
|
||||
}
|
||||
if (mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_SOFTWARE ||
|
||||
mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_HARDWARE) {
|
||||
// DSA_SW and DSA_HW only supported for LE_HARDWARE_OFFLOAD_ENC sessions
|
||||
continue;
|
||||
}
|
||||
supported_latency_modes.push_back(mode);
|
||||
}
|
||||
}
|
||||
return modes;
|
||||
return supported_latency_modes;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::SetLatencyMode(const LatencyMode& latency_mode) {
|
||||
|
|
|
@ -23,12 +23,15 @@
|
|||
#include <aidl/android/hardware/bluetooth/audio/LatencyMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// To avoid inclusion of hardware/audio.h
|
||||
struct sink_metadata;
|
||||
struct source_metadata;
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
|
@ -183,6 +186,9 @@ class BluetoothAudioSession {
|
|||
bool GetPresentationPosition(PresentationPosition& presentation_position);
|
||||
void UpdateSourceMetadata(const struct source_metadata& source_metadata);
|
||||
void UpdateSinkMetadata(const struct sink_metadata& sink_metadata);
|
||||
// New versions for AIDL-only clients.
|
||||
bool UpdateSourceMetadata(const SourceMetadata& hal_source_metadata);
|
||||
bool UpdateSinkMetadata(const SinkMetadata& hal_sink_metadata);
|
||||
|
||||
std::vector<LatencyMode> GetSupportedLatencyModes();
|
||||
void SetLatencyMode(const LatencyMode& latency_mode);
|
||||
|
@ -220,8 +226,10 @@ class BluetoothAudioSession {
|
|||
|
||||
static inline std::atomic<bool> is_aidl_checked = false;
|
||||
static inline std::atomic<bool> is_aidl_available = false;
|
||||
// BEGIN sysbta
|
||||
static inline const std::string kDefaultAudioProviderFactoryInterface =
|
||||
std::string() + IBluetoothAudioProviderFactory::descriptor + "/sysbta";
|
||||
// END sysbta
|
||||
};
|
||||
|
||||
class BluetoothAudioSessionInstance {
|
||||
|
|
|
@ -84,6 +84,8 @@ class BluetoothAudioSessionControl {
|
|||
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(CodecConfiguration{});
|
||||
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
|
||||
return AudioConfiguration(HfpConfiguration{});
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioConfiguration{});
|
||||
|
@ -156,6 +158,26 @@ class BluetoothAudioSessionControl {
|
|||
}
|
||||
}
|
||||
|
||||
static bool UpdateSourceMetadata(const SessionType& session_type,
|
||||
const SourceMetadata& source_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->UpdateSourceMetadata(source_metadata);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool UpdateSinkMetadata(const SessionType& session_type,
|
||||
const SinkMetadata& sink_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->UpdateSinkMetadata(sink_metadata);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<LatencyMode> GetSupportedLatencyModes(
|
||||
const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
|
|
|
@ -0,0 +1,760 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define STREAM_TO_UINT8(u8, p) \
|
||||
{ \
|
||||
(u8) = (uint8_t)(*(p)); \
|
||||
(p) += 1; \
|
||||
}
|
||||
#define STREAM_TO_UINT16(u16, p) \
|
||||
{ \
|
||||
(u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \
|
||||
(p) += 2; \
|
||||
}
|
||||
#define STREAM_TO_UINT32(u32, p) \
|
||||
{ \
|
||||
(u32) = (((uint32_t)(*(p))) + ((((uint32_t)(*((p) + 1)))) << 8) + \
|
||||
((((uint32_t)(*((p) + 2)))) << 16) + \
|
||||
((((uint32_t)(*((p) + 3)))) << 24)); \
|
||||
(p) += 4; \
|
||||
}
|
||||
|
||||
#define LOG_TAG "BTAudioAseConfigAidl"
|
||||
|
||||
#include "BluetoothLeAudioAseConfigurationSettingProvider.h"
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/AudioConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/AudioContext.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecId.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificCapabilitiesLtv.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificConfigurationLtv.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioAseConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/Phy.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
/* Internal structure definition */
|
||||
std::map<std::string,
|
||||
std::tuple<std::vector<std::optional<AseDirectionConfiguration>>,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>,
|
||||
ConfigurationFlags>>
|
||||
configurations_;
|
||||
|
||||
std::vector<LeAudioAseConfigurationSetting> ase_configuration_settings_;
|
||||
|
||||
constexpr uint8_t kIsoDataPathHci = 0x00;
|
||||
constexpr uint8_t kIsoDataPathPlatformDefault = 0x01;
|
||||
constexpr uint8_t kIsoDataPathDisabled = 0xFF;
|
||||
|
||||
constexpr uint8_t kLeAudioDirectionSink = 0x01;
|
||||
constexpr uint8_t kLeAudioDirectionSource = 0x02;
|
||||
constexpr uint8_t kLeAudioDirectionBoth =
|
||||
kLeAudioDirectionSink | kLeAudioDirectionSource;
|
||||
|
||||
/* Sampling Frequencies */
|
||||
constexpr uint8_t kLeAudioSamplingFreq8000Hz = 0x01;
|
||||
constexpr uint8_t kLeAudioSamplingFreq11025Hz = 0x02;
|
||||
constexpr uint8_t kLeAudioSamplingFreq16000Hz = 0x03;
|
||||
constexpr uint8_t kLeAudioSamplingFreq22050Hz = 0x04;
|
||||
constexpr uint8_t kLeAudioSamplingFreq24000Hz = 0x05;
|
||||
constexpr uint8_t kLeAudioSamplingFreq32000Hz = 0x06;
|
||||
constexpr uint8_t kLeAudioSamplingFreq44100Hz = 0x07;
|
||||
constexpr uint8_t kLeAudioSamplingFreq48000Hz = 0x08;
|
||||
constexpr uint8_t kLeAudioSamplingFreq88200Hz = 0x09;
|
||||
constexpr uint8_t kLeAudioSamplingFreq96000Hz = 0x0A;
|
||||
constexpr uint8_t kLeAudioSamplingFreq176400Hz = 0x0B;
|
||||
constexpr uint8_t kLeAudioSamplingFreq192000Hz = 0x0C;
|
||||
constexpr uint8_t kLeAudioSamplingFreq384000Hz = 0x0D;
|
||||
|
||||
/* Frame Durations */
|
||||
constexpr uint8_t kLeAudioCodecFrameDur7500us = 0x00;
|
||||
constexpr uint8_t kLeAudioCodecFrameDur10000us = 0x01;
|
||||
|
||||
/* Audio Allocations */
|
||||
constexpr uint32_t kLeAudioLocationNotAllowed = 0x00000000;
|
||||
constexpr uint32_t kLeAudioLocationFrontLeft = 0x00000001;
|
||||
constexpr uint32_t kLeAudioLocationFrontRight = 0x00000002;
|
||||
constexpr uint32_t kLeAudioLocationFrontCenter = 0x00000004;
|
||||
constexpr uint32_t kLeAudioLocationLowFreqEffects1 = 0x00000008;
|
||||
constexpr uint32_t kLeAudioLocationBackLeft = 0x00000010;
|
||||
constexpr uint32_t kLeAudioLocationBackRight = 0x00000020;
|
||||
constexpr uint32_t kLeAudioLocationFrontLeftOfCenter = 0x00000040;
|
||||
constexpr uint32_t kLeAudioLocationFrontRightOfCenter = 0x00000080;
|
||||
constexpr uint32_t kLeAudioLocationBackCenter = 0x00000100;
|
||||
constexpr uint32_t kLeAudioLocationLowFreqEffects2 = 0x00000200;
|
||||
constexpr uint32_t kLeAudioLocationSideLeft = 0x00000400;
|
||||
constexpr uint32_t kLeAudioLocationSideRight = 0x00000800;
|
||||
constexpr uint32_t kLeAudioLocationTopFrontLeft = 0x00001000;
|
||||
constexpr uint32_t kLeAudioLocationTopFrontRight = 0x00002000;
|
||||
constexpr uint32_t kLeAudioLocationTopFrontCenter = 0x00004000;
|
||||
constexpr uint32_t kLeAudioLocationTopCenter = 0x00008000;
|
||||
constexpr uint32_t kLeAudioLocationTopBackLeft = 0x00010000;
|
||||
constexpr uint32_t kLeAudioLocationTopBackRight = 0x00020000;
|
||||
constexpr uint32_t kLeAudioLocationTopSideLeft = 0x00040000;
|
||||
constexpr uint32_t kLeAudioLocationTopSideRight = 0x00080000;
|
||||
constexpr uint32_t kLeAudioLocationTopBackCenter = 0x00100000;
|
||||
constexpr uint32_t kLeAudioLocationBottomFrontCenter = 0x00200000;
|
||||
constexpr uint32_t kLeAudioLocationBottomFrontLeft = 0x00400000;
|
||||
constexpr uint32_t kLeAudioLocationBottomFrontRight = 0x00800000;
|
||||
constexpr uint32_t kLeAudioLocationFrontLeftWide = 0x01000000;
|
||||
constexpr uint32_t kLeAudioLocationFrontRightWide = 0x02000000;
|
||||
constexpr uint32_t kLeAudioLocationLeftSurround = 0x04000000;
|
||||
constexpr uint32_t kLeAudioLocationRightSurround = 0x08000000;
|
||||
|
||||
constexpr uint32_t kLeAudioLocationAnyLeft =
|
||||
kLeAudioLocationFrontLeft | kLeAudioLocationBackLeft |
|
||||
kLeAudioLocationFrontLeftOfCenter | kLeAudioLocationSideLeft |
|
||||
kLeAudioLocationTopFrontLeft | kLeAudioLocationTopBackLeft |
|
||||
kLeAudioLocationTopSideLeft | kLeAudioLocationBottomFrontLeft |
|
||||
kLeAudioLocationFrontLeftWide | kLeAudioLocationLeftSurround;
|
||||
|
||||
constexpr uint32_t kLeAudioLocationAnyRight =
|
||||
kLeAudioLocationFrontRight | kLeAudioLocationBackRight |
|
||||
kLeAudioLocationFrontRightOfCenter | kLeAudioLocationSideRight |
|
||||
kLeAudioLocationTopFrontRight | kLeAudioLocationTopBackRight |
|
||||
kLeAudioLocationTopSideRight | kLeAudioLocationBottomFrontRight |
|
||||
kLeAudioLocationFrontRightWide | kLeAudioLocationRightSurround;
|
||||
|
||||
constexpr uint32_t kLeAudioLocationStereo =
|
||||
kLeAudioLocationFrontLeft | kLeAudioLocationFrontRight;
|
||||
|
||||
/* Octets Per Frame */
|
||||
constexpr uint16_t kLeAudioCodecFrameLen30 = 30;
|
||||
constexpr uint16_t kLeAudioCodecFrameLen40 = 40;
|
||||
constexpr uint16_t kLeAudioCodecFrameLen60 = 60;
|
||||
constexpr uint16_t kLeAudioCodecFrameLen80 = 80;
|
||||
constexpr uint16_t kLeAudioCodecFrameLen100 = 100;
|
||||
constexpr uint16_t kLeAudioCodecFrameLen120 = 120;
|
||||
|
||||
/* Helper map for matching various sampling frequency notations */
|
||||
const std::map<uint8_t, CodecSpecificConfigurationLtv::SamplingFrequency>
|
||||
sampling_freq_map = {
|
||||
{kLeAudioSamplingFreq8000Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000},
|
||||
{kLeAudioSamplingFreq16000Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
|
||||
{kLeAudioSamplingFreq24000Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000},
|
||||
{kLeAudioSamplingFreq32000Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000},
|
||||
{kLeAudioSamplingFreq44100Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ44100},
|
||||
{kLeAudioSamplingFreq48000Hz,
|
||||
CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000}};
|
||||
|
||||
/* Helper map for matching various frame durations notations */
|
||||
const std::map<uint8_t, CodecSpecificConfigurationLtv::FrameDuration>
|
||||
frame_duration_map = {
|
||||
{kLeAudioCodecFrameDur7500us,
|
||||
CodecSpecificConfigurationLtv::FrameDuration::US7500},
|
||||
{kLeAudioCodecFrameDur10000us,
|
||||
CodecSpecificConfigurationLtv::FrameDuration::US10000}};
|
||||
|
||||
/* Helper map for matching various audio channel allocation notations */
|
||||
std::map<uint32_t, uint32_t> audio_channel_allocation_map = {
|
||||
{kLeAudioLocationNotAllowed,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::NOT_ALLOWED},
|
||||
{kLeAudioLocationFrontLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT},
|
||||
{kLeAudioLocationFrontRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT},
|
||||
{kLeAudioLocationFrontCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER},
|
||||
{kLeAudioLocationLowFreqEffects1,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
||||
LOW_FREQUENCY_EFFECTS_1},
|
||||
{kLeAudioLocationBackLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_LEFT},
|
||||
{kLeAudioLocationBackRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_RIGHT},
|
||||
{kLeAudioLocationFrontLeftOfCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
||||
FRONT_LEFT_OF_CENTER},
|
||||
{kLeAudioLocationFrontRightOfCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
||||
FRONT_RIGHT_OF_CENTER},
|
||||
{kLeAudioLocationBackCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_CENTER},
|
||||
{kLeAudioLocationLowFreqEffects2,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
||||
LOW_FREQUENCY_EFFECTS_2},
|
||||
{kLeAudioLocationSideLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_LEFT},
|
||||
{kLeAudioLocationSideRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_RIGHT},
|
||||
{kLeAudioLocationTopFrontLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_LEFT},
|
||||
{kLeAudioLocationTopFrontRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_RIGHT},
|
||||
{kLeAudioLocationTopFrontCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_CENTER},
|
||||
{kLeAudioLocationTopCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_CENTER},
|
||||
{kLeAudioLocationTopBackLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_LEFT},
|
||||
{kLeAudioLocationTopBackRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_RIGHT},
|
||||
{kLeAudioLocationTopSideLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_LEFT},
|
||||
{kLeAudioLocationTopSideRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_RIGHT},
|
||||
{kLeAudioLocationTopBackCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_CENTER},
|
||||
{kLeAudioLocationBottomFrontCenter,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
||||
BOTTOM_FRONT_CENTER},
|
||||
{kLeAudioLocationBottomFrontLeft,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_LEFT},
|
||||
{kLeAudioLocationBottomFrontRight,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_RIGHT},
|
||||
{kLeAudioLocationFrontLeftWide,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT_WIDE},
|
||||
{kLeAudioLocationFrontRightWide,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT_WIDE},
|
||||
{kLeAudioLocationLeftSurround,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::LEFT_SURROUND},
|
||||
{kLeAudioLocationRightSurround,
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation::RIGHT_SURROUND},
|
||||
};
|
||||
|
||||
static const std::vector<
|
||||
std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
kLeAudioSetConfigs = {{"/vendor/etc/aidl/le_audio/"
|
||||
"aidl_audio_set_configurations.bfbs",
|
||||
"/vendor/etc/aidl/le_audio/"
|
||||
"aidl_audio_set_configurations.json"}};
|
||||
static const std::vector<
|
||||
std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
kLeAudioSetScenarios = {{"/vendor/etc/aidl/le_audio/"
|
||||
"aidl_audio_set_scenarios.bfbs",
|
||||
"/vendor/etc/aidl/le_audio/"
|
||||
"aidl_audio_set_scenarios.json"}};
|
||||
|
||||
/* Implementation */
|
||||
|
||||
std::vector<LeAudioAseConfigurationSetting>
|
||||
AudioSetConfigurationProviderJson::GetLeAudioAseConfigurationSettings() {
|
||||
AudioSetConfigurationProviderJson::LoadAudioSetConfigurationProviderJson();
|
||||
return ase_configuration_settings_;
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::
|
||||
LoadAudioSetConfigurationProviderJson() {
|
||||
if (configurations_.empty() || ase_configuration_settings_.empty()) {
|
||||
ase_configuration_settings_.clear();
|
||||
configurations_.clear();
|
||||
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
|
||||
CodecLocation::HOST);
|
||||
if (!loaded)
|
||||
LOG(ERROR) << ": Unable to load le audio set configuration files.";
|
||||
} else
|
||||
LOG(INFO) << ": Reusing loaded le audio set configuration";
|
||||
}
|
||||
|
||||
const le_audio::CodecSpecificConfiguration*
|
||||
AudioSetConfigurationProviderJson::LookupCodecSpecificParam(
|
||||
const flatbuffers::Vector<flatbuffers::Offset<
|
||||
le_audio::CodecSpecificConfiguration>>* flat_codec_specific_params,
|
||||
le_audio::CodecSpecificLtvGenericTypes type) {
|
||||
auto it = std::find_if(
|
||||
flat_codec_specific_params->cbegin(), flat_codec_specific_params->cend(),
|
||||
[&type](const auto& csc) { return (csc->type() == type); });
|
||||
return (it != flat_codec_specific_params->cend()) ? *it : nullptr;
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::populateAudioChannelAllocation(
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation&
|
||||
audio_channel_allocation,
|
||||
uint32_t audio_location) {
|
||||
audio_channel_allocation.bitmask = 0;
|
||||
for (auto [allocation, bitmask] : audio_channel_allocation_map) {
|
||||
if (audio_location & allocation)
|
||||
audio_channel_allocation.bitmask |= bitmask;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::populateConfigurationData(
|
||||
LeAudioAseConfiguration& ase,
|
||||
const flatbuffers::Vector<
|
||||
flatbuffers::Offset<le_audio::CodecSpecificConfiguration>>*
|
||||
flat_codec_specific_params) {
|
||||
uint8_t sampling_frequency = 0;
|
||||
uint8_t frame_duration = 0;
|
||||
uint32_t audio_channel_allocation = 0;
|
||||
uint16_t octets_per_codec_frame = 0;
|
||||
uint8_t codec_frames_blocks_per_sdu = 0;
|
||||
|
||||
auto param = LookupCodecSpecificParam(
|
||||
flat_codec_specific_params,
|
||||
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_SAMPLING_FREQUENCY);
|
||||
if (param) {
|
||||
auto ptr = param->compound_value()->value()->data();
|
||||
STREAM_TO_UINT8(sampling_frequency, ptr);
|
||||
}
|
||||
|
||||
param = LookupCodecSpecificParam(
|
||||
flat_codec_specific_params,
|
||||
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_FRAME_DURATION);
|
||||
if (param) {
|
||||
auto ptr = param->compound_value()->value()->data();
|
||||
STREAM_TO_UINT8(frame_duration, ptr);
|
||||
}
|
||||
|
||||
param = LookupCodecSpecificParam(
|
||||
flat_codec_specific_params,
|
||||
le_audio::
|
||||
CodecSpecificLtvGenericTypes_SUPPORTED_AUDIO_CHANNEL_ALLOCATION);
|
||||
if (param) {
|
||||
auto ptr = param->compound_value()->value()->data();
|
||||
STREAM_TO_UINT32(audio_channel_allocation, ptr);
|
||||
}
|
||||
|
||||
param = LookupCodecSpecificParam(
|
||||
flat_codec_specific_params,
|
||||
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_OCTETS_PER_CODEC_FRAME);
|
||||
if (param) {
|
||||
auto ptr = param->compound_value()->value()->data();
|
||||
STREAM_TO_UINT16(octets_per_codec_frame, ptr);
|
||||
}
|
||||
|
||||
param = LookupCodecSpecificParam(
|
||||
flat_codec_specific_params,
|
||||
le_audio::
|
||||
CodecSpecificLtvGenericTypes_SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU);
|
||||
if (param) {
|
||||
auto ptr = param->compound_value()->value()->data();
|
||||
STREAM_TO_UINT8(codec_frames_blocks_per_sdu, ptr);
|
||||
}
|
||||
|
||||
// Make the correct value
|
||||
ase.codecConfiguration = std::vector<CodecSpecificConfigurationLtv>();
|
||||
|
||||
auto sampling_freq_it = sampling_freq_map.find(sampling_frequency);
|
||||
if (sampling_freq_it != sampling_freq_map.end())
|
||||
ase.codecConfiguration.push_back(sampling_freq_it->second);
|
||||
auto frame_duration_it = frame_duration_map.find(frame_duration);
|
||||
if (frame_duration_it != frame_duration_map.end())
|
||||
ase.codecConfiguration.push_back(frame_duration_it->second);
|
||||
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation channel_allocation;
|
||||
populateAudioChannelAllocation(channel_allocation, audio_channel_allocation);
|
||||
ase.codecConfiguration.push_back(channel_allocation);
|
||||
|
||||
auto octet_structure = CodecSpecificConfigurationLtv::OctetsPerCodecFrame();
|
||||
octet_structure.value = octets_per_codec_frame;
|
||||
ase.codecConfiguration.push_back(octet_structure);
|
||||
|
||||
auto frame_sdu_structure =
|
||||
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
|
||||
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
|
||||
ase.codecConfiguration.push_back(frame_sdu_structure);
|
||||
// TODO: Channel count
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::populateAseConfiguration(
|
||||
LeAudioAseConfiguration& ase,
|
||||
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg) {
|
||||
// Target latency
|
||||
switch (qos_cfg->target_latency()) {
|
||||
case le_audio::AudioSetConfigurationTargetLatency::
|
||||
AudioSetConfigurationTargetLatency_BALANCED_RELIABILITY:
|
||||
ase.targetLatency =
|
||||
LeAudioAseConfiguration::TargetLatency::BALANCED_LATENCY_RELIABILITY;
|
||||
break;
|
||||
case le_audio::AudioSetConfigurationTargetLatency::
|
||||
AudioSetConfigurationTargetLatency_HIGH_RELIABILITY:
|
||||
ase.targetLatency =
|
||||
LeAudioAseConfiguration::TargetLatency::HIGHER_RELIABILITY;
|
||||
break;
|
||||
case le_audio::AudioSetConfigurationTargetLatency::
|
||||
AudioSetConfigurationTargetLatency_LOW:
|
||||
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::LOWER;
|
||||
break;
|
||||
default:
|
||||
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::UNDEFINED;
|
||||
break;
|
||||
};
|
||||
|
||||
ase.targetPhy = Phy::TWO_M;
|
||||
// Making CodecId
|
||||
if (flat_subconfig->codec_id()->coding_format() ==
|
||||
(uint8_t)CodecId::Core::LC3) {
|
||||
ase.codecId = CodecId::Core::LC3;
|
||||
} else {
|
||||
auto vendorC = CodecId::Vendor();
|
||||
vendorC.codecId = flat_subconfig->codec_id()->vendor_codec_id();
|
||||
vendorC.id = flat_subconfig->codec_id()->vendor_company_id();
|
||||
ase.codecId = vendorC;
|
||||
}
|
||||
// Codec configuration data
|
||||
populateConfigurationData(ase, flat_subconfig->codec_configuration());
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
|
||||
LeAudioAseQosConfiguration& qos,
|
||||
const le_audio::QosConfiguration* qos_cfg) {
|
||||
qos.maxTransportLatencyMs = qos_cfg->max_transport_latency();
|
||||
qos.retransmissionNum = qos_cfg->retransmission_number();
|
||||
}
|
||||
|
||||
// Parse into AseDirectionConfiguration
|
||||
AseDirectionConfiguration
|
||||
AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig(
|
||||
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg, CodecLocation location) {
|
||||
AseDirectionConfiguration direction_conf;
|
||||
|
||||
LeAudioAseConfiguration ase;
|
||||
LeAudioAseQosConfiguration qos;
|
||||
LeAudioDataPathConfiguration path;
|
||||
|
||||
// Translate into LeAudioAseConfiguration
|
||||
populateAseConfiguration(ase, flat_subconfig, qos_cfg);
|
||||
|
||||
// Translate into LeAudioAseQosConfiguration
|
||||
populateAseQosConfiguration(qos, qos_cfg);
|
||||
|
||||
// Translate location to data path id
|
||||
switch (location) {
|
||||
case CodecLocation::ADSP:
|
||||
path.isoDataPathConfiguration.isTransparent = true;
|
||||
path.dataPathId = kIsoDataPathPlatformDefault;
|
||||
break;
|
||||
case CodecLocation::HOST:
|
||||
path.isoDataPathConfiguration.isTransparent = true;
|
||||
path.dataPathId = kIsoDataPathHci;
|
||||
break;
|
||||
case CodecLocation::CONTROLLER:
|
||||
path.isoDataPathConfiguration.isTransparent = false;
|
||||
path.dataPathId = kIsoDataPathPlatformDefault;
|
||||
break;
|
||||
}
|
||||
|
||||
direction_conf.aseConfiguration = ase;
|
||||
direction_conf.qosConfiguration = qos;
|
||||
direction_conf.dataPathConfiguration = path;
|
||||
|
||||
return direction_conf;
|
||||
}
|
||||
|
||||
// Parse into AseDirectionConfiguration and the ConfigurationFlags
|
||||
// and put them in the given list.
|
||||
void AudioSetConfigurationProviderJson::processSubconfig(
|
||||
const le_audio::AudioSetSubConfiguration* subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
directionAseConfiguration,
|
||||
CodecLocation location) {
|
||||
directionAseConfiguration.push_back(
|
||||
SetConfigurationFromFlatSubconfig(subconfig, qos_cfg, location));
|
||||
}
|
||||
|
||||
void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
|
||||
const le_audio::AudioSetConfiguration* flat_cfg,
|
||||
std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
|
||||
std::vector<const le_audio::QosConfiguration*>* qos_cfgs,
|
||||
CodecLocation location,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
sourceAseConfiguration,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>& sinkAseConfiguration,
|
||||
ConfigurationFlags& /*configurationFlags*/) {
|
||||
if (flat_cfg == nullptr) {
|
||||
LOG(ERROR) << "flat_cfg cannot be null";
|
||||
return;
|
||||
}
|
||||
std::string codec_config_key = flat_cfg->codec_config_name()->str();
|
||||
auto* qos_config_key_array = flat_cfg->qos_config_name();
|
||||
|
||||
constexpr std::string_view default_qos = "QoS_Config_Balanced_Reliability";
|
||||
|
||||
std::string qos_sink_key(default_qos);
|
||||
std::string qos_source_key(default_qos);
|
||||
|
||||
/* We expect maximum two QoS settings. First for Sink and second for Source
|
||||
*/
|
||||
if (qos_config_key_array->size() > 0) {
|
||||
qos_sink_key = qos_config_key_array->Get(0)->str();
|
||||
if (qos_config_key_array->size() > 1) {
|
||||
qos_source_key = qos_config_key_array->Get(1)->str();
|
||||
} else {
|
||||
qos_source_key = qos_sink_key;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Audio set config " << flat_cfg->name()->c_str()
|
||||
<< ": codec config " << codec_config_key.c_str() << ", qos_sink "
|
||||
<< qos_sink_key.c_str() << ", qos_source "
|
||||
<< qos_source_key.c_str();
|
||||
|
||||
// Find the first qos config that match the name
|
||||
const le_audio::QosConfiguration* qos_sink_cfg = nullptr;
|
||||
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
|
||||
if ((*i)->name()->str() == qos_sink_key) {
|
||||
qos_sink_cfg = *i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const le_audio::QosConfiguration* qos_source_cfg = nullptr;
|
||||
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
|
||||
if ((*i)->name()->str() == qos_source_key) {
|
||||
qos_source_cfg = *i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// First codec_cfg with the same name
|
||||
const le_audio::CodecConfiguration* codec_cfg = nullptr;
|
||||
for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) {
|
||||
if ((*i)->name()->str() == codec_config_key) {
|
||||
codec_cfg = *i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process each subconfig and put it into the correct list
|
||||
if (codec_cfg != nullptr && codec_cfg->subconfigurations()) {
|
||||
/* Load subconfigurations */
|
||||
for (auto subconfig : *codec_cfg->subconfigurations()) {
|
||||
if (subconfig->direction() == kLeAudioDirectionSink) {
|
||||
processSubconfig(subconfig, qos_sink_cfg, sinkAseConfiguration,
|
||||
location);
|
||||
} else {
|
||||
processSubconfig(subconfig, qos_source_cfg, sourceAseConfiguration,
|
||||
location);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (codec_cfg == nullptr) {
|
||||
LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str()
|
||||
<< " found";
|
||||
} else {
|
||||
LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
|
||||
<< "' has no valid subconfigurations.";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Populate information for ConfigurationFlags
|
||||
}
|
||||
|
||||
bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles(
|
||||
const char* schema_file, const char* content_file, CodecLocation location) {
|
||||
flatbuffers::Parser configurations_parser_;
|
||||
std::string configurations_schema_binary_content;
|
||||
bool ok = flatbuffers::LoadFile(schema_file, true,
|
||||
&configurations_schema_binary_content);
|
||||
LOG(INFO) << __func__ << ": Loading file " << schema_file;
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Load the binary schema */
|
||||
ok = configurations_parser_.Deserialize(
|
||||
(uint8_t*)configurations_schema_binary_content.c_str(),
|
||||
configurations_schema_binary_content.length());
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Load the content from JSON */
|
||||
std::string configurations_json_content;
|
||||
LOG(INFO) << __func__ << ": Loading file " << content_file;
|
||||
ok = flatbuffers::LoadFile(content_file, false, &configurations_json_content);
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Parse */
|
||||
LOG(INFO) << __func__ << ": Parse JSON content";
|
||||
ok = configurations_parser_.Parse(configurations_json_content.c_str());
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Import from flatbuffers */
|
||||
LOG(INFO) << __func__ << ": Build flat buffer structure";
|
||||
auto configurations_root = le_audio::GetAudioSetConfigurations(
|
||||
configurations_parser_.builder_.GetBufferPointer());
|
||||
if (!configurations_root) return false;
|
||||
|
||||
auto flat_qos_configs = configurations_root->qos_configurations();
|
||||
if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0))
|
||||
return false;
|
||||
|
||||
LOG(DEBUG) << ": Updating " << flat_qos_configs->size()
|
||||
<< " qos config entries.";
|
||||
std::vector<const le_audio::QosConfiguration*> qos_cfgs;
|
||||
for (auto const& flat_qos_cfg : *flat_qos_configs) {
|
||||
qos_cfgs.push_back(flat_qos_cfg);
|
||||
}
|
||||
|
||||
auto flat_codec_configs = configurations_root->codec_configurations();
|
||||
if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0))
|
||||
return false;
|
||||
|
||||
LOG(DEBUG) << ": Updating " << flat_codec_configs->size()
|
||||
<< " codec config entries.";
|
||||
std::vector<const le_audio::CodecConfiguration*> codec_cfgs;
|
||||
for (auto const& flat_codec_cfg : *flat_codec_configs) {
|
||||
codec_cfgs.push_back(flat_codec_cfg);
|
||||
}
|
||||
|
||||
auto flat_configs = configurations_root->configurations();
|
||||
if ((flat_configs == nullptr) || (flat_configs->size() == 0)) return false;
|
||||
|
||||
LOG(DEBUG) << ": Updating " << flat_configs->size() << " config entries.";
|
||||
for (auto const& flat_cfg : *flat_configs) {
|
||||
// Create 3 vector to use
|
||||
std::vector<std::optional<AseDirectionConfiguration>>
|
||||
sourceAseConfiguration;
|
||||
std::vector<std::optional<AseDirectionConfiguration>> sinkAseConfiguration;
|
||||
ConfigurationFlags configurationFlags;
|
||||
PopulateAseConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs, location,
|
||||
sourceAseConfiguration,
|
||||
sinkAseConfiguration, configurationFlags);
|
||||
if (sourceAseConfiguration.empty() && sinkAseConfiguration.empty())
|
||||
continue;
|
||||
configurations_[flat_cfg->name()->str()] = std::make_tuple(
|
||||
sourceAseConfiguration, sinkAseConfiguration, configurationFlags);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles(
|
||||
const char* schema_file, const char* content_file) {
|
||||
flatbuffers::Parser scenarios_parser_;
|
||||
std::string scenarios_schema_binary_content;
|
||||
bool ok = flatbuffers::LoadFile(schema_file, true,
|
||||
&scenarios_schema_binary_content);
|
||||
LOG(INFO) << __func__ << ": Loading file " << schema_file;
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Load the binary schema */
|
||||
ok = scenarios_parser_.Deserialize(
|
||||
(uint8_t*)scenarios_schema_binary_content.c_str(),
|
||||
scenarios_schema_binary_content.length());
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Load the content from JSON */
|
||||
LOG(INFO) << __func__ << ": Loading file " << content_file;
|
||||
std::string scenarios_json_content;
|
||||
ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content);
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Parse */
|
||||
LOG(INFO) << __func__ << ": Parse json content";
|
||||
ok = scenarios_parser_.Parse(scenarios_json_content.c_str());
|
||||
if (!ok) return ok;
|
||||
|
||||
/* Import from flatbuffers */
|
||||
LOG(INFO) << __func__ << ": Build flat buffer structure";
|
||||
auto scenarios_root = le_audio::GetAudioSetScenarios(
|
||||
scenarios_parser_.builder_.GetBufferPointer());
|
||||
if (!scenarios_root) return false;
|
||||
|
||||
auto flat_scenarios = scenarios_root->scenarios();
|
||||
if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0))
|
||||
return false;
|
||||
|
||||
LOG(INFO) << __func__ << ": Turn flat buffer into structure";
|
||||
AudioContext media_context = AudioContext();
|
||||
media_context.bitmask =
|
||||
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
|
||||
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
|
||||
AudioContext::UNSPECIFIED | AudioContext::MEDIA);
|
||||
|
||||
AudioContext conversational_context = AudioContext();
|
||||
conversational_context.bitmask =
|
||||
(AudioContext::RINGTONE_ALERTS | AudioContext::CONVERSATIONAL);
|
||||
|
||||
AudioContext live_context = AudioContext();
|
||||
live_context.bitmask = AudioContext::LIVE_AUDIO;
|
||||
|
||||
AudioContext game_context = AudioContext();
|
||||
game_context.bitmask = AudioContext::GAME;
|
||||
|
||||
AudioContext voice_assistants_context = AudioContext();
|
||||
voice_assistants_context.bitmask = AudioContext::VOICE_ASSISTANTS;
|
||||
|
||||
LOG(DEBUG) << "Updating " << flat_scenarios->size() << " scenarios.";
|
||||
for (auto const& scenario : *flat_scenarios) {
|
||||
LOG(DEBUG) << "Scenario " << scenario->name()->c_str()
|
||||
<< " configs: " << scenario->configurations()->size();
|
||||
|
||||
if (!scenario->configurations()) continue;
|
||||
std::string scenario_name = scenario->name()->c_str();
|
||||
AudioContext context;
|
||||
if (scenario_name == "Media")
|
||||
context = AudioContext(media_context);
|
||||
else if (scenario_name == "Conversational")
|
||||
context = AudioContext(conversational_context);
|
||||
else if (scenario_name == "Live")
|
||||
context = AudioContext(live_context);
|
||||
else if (scenario_name == "Game")
|
||||
context = AudioContext(game_context);
|
||||
else if (scenario_name == "VoiceAssistants")
|
||||
context = AudioContext(voice_assistants_context);
|
||||
|
||||
for (auto it = scenario->configurations()->begin();
|
||||
it != scenario->configurations()->end(); ++it) {
|
||||
auto config_name = it->str();
|
||||
auto configuration = configurations_.find(config_name);
|
||||
if (configuration == configurations_.end()) continue;
|
||||
LOG(DEBUG) << "Getting configuration with name: " << config_name;
|
||||
auto [source, sink, flags] = configuration->second;
|
||||
// Each configuration will create a LeAudioAseConfigurationSetting
|
||||
// with the same {context, packing}
|
||||
// and different data
|
||||
LeAudioAseConfigurationSetting setting;
|
||||
setting.audioContext = context;
|
||||
// TODO: Packing
|
||||
setting.sourceAseConfiguration = source;
|
||||
setting.sinkAseConfiguration = sink;
|
||||
setting.flags = flags;
|
||||
// Add to list of setting
|
||||
LOG(DEBUG) << "Pushing configuration to list: " << config_name;
|
||||
ase_configuration_settings_.push_back(setting);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioSetConfigurationProviderJson::LoadContent(
|
||||
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
config_files,
|
||||
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
scenario_files,
|
||||
CodecLocation location) {
|
||||
for (auto [schema, content] : config_files) {
|
||||
if (!LoadConfigurationsFromFiles(schema, content, location)) return false;
|
||||
}
|
||||
|
||||
for (auto [schema, content] : scenario_files) {
|
||||
if (!LoadScenariosFromFiles(schema, content)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.h>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
#include "audio_set_configurations_generated.h"
|
||||
#include "audio_set_scenarios_generated.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using LeAudioAseConfigurationSetting =
|
||||
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
|
||||
using AseDirectionConfiguration = IBluetoothAudioProvider::
|
||||
LeAudioAseConfigurationSetting::AseDirectionConfiguration;
|
||||
using LeAudioAseQosConfiguration =
|
||||
IBluetoothAudioProvider::LeAudioAseQosConfiguration;
|
||||
using LeAudioDataPathConfiguration =
|
||||
IBluetoothAudioProvider::LeAudioDataPathConfiguration;
|
||||
|
||||
enum class CodecLocation {
|
||||
HOST,
|
||||
ADSP,
|
||||
CONTROLLER,
|
||||
};
|
||||
|
||||
class AudioSetConfigurationProviderJson {
|
||||
public:
|
||||
static std::vector<LeAudioAseConfigurationSetting>
|
||||
GetLeAudioAseConfigurationSettings();
|
||||
|
||||
private:
|
||||
static void LoadAudioSetConfigurationProviderJson();
|
||||
|
||||
static const le_audio::CodecSpecificConfiguration* LookupCodecSpecificParam(
|
||||
const flatbuffers::Vector<flatbuffers::Offset<
|
||||
le_audio::CodecSpecificConfiguration>>* flat_codec_specific_params,
|
||||
le_audio::CodecSpecificLtvGenericTypes type);
|
||||
|
||||
static void populateAudioChannelAllocation(
|
||||
CodecSpecificConfigurationLtv::AudioChannelAllocation&
|
||||
audio_channel_allocation,
|
||||
uint32_t audio_location);
|
||||
|
||||
static void populateConfigurationData(
|
||||
LeAudioAseConfiguration& ase,
|
||||
const flatbuffers::Vector<
|
||||
flatbuffers::Offset<le_audio::CodecSpecificConfiguration>>*
|
||||
flat_codec_specific_params);
|
||||
|
||||
static void populateAseConfiguration(
|
||||
LeAudioAseConfiguration& ase,
|
||||
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg);
|
||||
|
||||
static void populateAseQosConfiguration(
|
||||
LeAudioAseQosConfiguration& qos,
|
||||
const le_audio::QosConfiguration* qos_cfg);
|
||||
|
||||
static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
|
||||
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg, CodecLocation location);
|
||||
|
||||
static void processSubconfig(
|
||||
const le_audio::AudioSetSubConfiguration* subconfig,
|
||||
const le_audio::QosConfiguration* qos_cfg,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
directionAseConfiguration,
|
||||
CodecLocation location);
|
||||
|
||||
static void PopulateAseConfigurationFromFlat(
|
||||
const le_audio::AudioSetConfiguration* flat_cfg,
|
||||
std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
|
||||
std::vector<const le_audio::QosConfiguration*>* qos_cfgs,
|
||||
CodecLocation location,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
sourceAseConfiguration,
|
||||
std::vector<std::optional<AseDirectionConfiguration>>&
|
||||
sinkAseConfiguration,
|
||||
ConfigurationFlags& configurationFlags);
|
||||
|
||||
static bool LoadConfigurationsFromFiles(const char* schema_file,
|
||||
const char* content_file,
|
||||
CodecLocation location);
|
||||
|
||||
static bool LoadScenariosFromFiles(const char* schema_file,
|
||||
const char* content_file);
|
||||
|
||||
static bool LoadContent(
|
||||
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
config_files,
|
||||
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
||||
scenario_files,
|
||||
CodecLocation location);
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "aidl/android/hardware/bluetooth/audio/ChannelMode.h"
|
||||
#include "aidl/android/hardware/bluetooth/audio/CodecId.h"
|
||||
#include "aidl_android_hardware_bluetooth_audio_setting_enums.h"
|
||||
#define LOG_TAG "BTAudioCodecsProviderAidl"
|
||||
|
||||
#include "BluetoothLeAudioCodecsProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static const char* kLeAudioCodecCapabilitiesFile =
|
||||
"/vendor/etc/le_audio_codec_capabilities.xml";
|
||||
|
||||
static const AudioLocation kStereoAudio = static_cast<AudioLocation>(
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
|
||||
static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN;
|
||||
|
||||
static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
|
||||
|
||||
static bool isInvalidFileContent = false;
|
||||
|
||||
std::optional<setting::LeAudioOffloadSetting>
|
||||
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
|
||||
if (!leAudioCodecCapabilities.empty() || isInvalidFileContent) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto le_audio_offload_setting =
|
||||
setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
|
||||
if (!le_audio_offload_setting.has_value()) {
|
||||
LOG(ERROR) << __func__ << ": Failed to read "
|
||||
<< kLeAudioCodecCapabilitiesFile;
|
||||
}
|
||||
return le_audio_offload_setting;
|
||||
}
|
||||
|
||||
std::unordered_map<SessionType, std::vector<CodecInfo>>
|
||||
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
// Load from previous storage if present
|
||||
if (!session_codecs_map_.empty()) return session_codecs_map_;
|
||||
|
||||
isInvalidFileContent = true;
|
||||
if (!le_audio_offload_setting.has_value()) return {};
|
||||
|
||||
// Load scenario, configuration, codec configuration and strategy
|
||||
LoadConfigurationToMap(le_audio_offload_setting);
|
||||
if (supported_scenarios_.empty() || configuration_map_.empty() ||
|
||||
codec_configuration_map_.empty() || strategy_configuration_map_.empty())
|
||||
return {};
|
||||
|
||||
// Map each configuration into a CodecInfo
|
||||
std::unordered_map<std::string, CodecInfo> config_codec_info_map_;
|
||||
|
||||
for (auto& p : configuration_map_) {
|
||||
// Initialize new CodecInfo for the config
|
||||
auto config_name = p.first;
|
||||
if (config_codec_info_map_.count(config_name) == 0)
|
||||
config_codec_info_map_[config_name] = CodecInfo();
|
||||
|
||||
// Getting informations from codecConfig and strategyConfig
|
||||
const auto codec_config_name = p.second.getCodecConfiguration();
|
||||
const auto strategy_config_name = p.second.getStrategyConfiguration();
|
||||
const auto codec_configuration_map_iter =
|
||||
codec_configuration_map_.find(codec_config_name);
|
||||
if (codec_configuration_map_iter == codec_configuration_map_.end())
|
||||
continue;
|
||||
const auto strategy_configuration_map_iter =
|
||||
strategy_configuration_map_.find(strategy_config_name);
|
||||
if (strategy_configuration_map_iter == strategy_configuration_map_.end())
|
||||
continue;
|
||||
|
||||
const auto& codec_config = codec_configuration_map_iter->second;
|
||||
const auto codec = codec_config.getCodec();
|
||||
const auto& strategy_config = strategy_configuration_map_iter->second;
|
||||
const auto strategy_config_channel_count =
|
||||
strategy_config.getChannelCount();
|
||||
|
||||
// Initiate information
|
||||
auto& codec_info = config_codec_info_map_[config_name];
|
||||
switch (codec) {
|
||||
case setting::CodecType::LC3:
|
||||
codec_info.name = "LC3";
|
||||
codec_info.id = CodecId::Core::LC3;
|
||||
break;
|
||||
default:
|
||||
codec_info.name = "UNDEFINE";
|
||||
codec_info.id = CodecId::Vendor();
|
||||
break;
|
||||
}
|
||||
codec_info.transport =
|
||||
CodecInfo::Transport::make<CodecInfo::Transport::Tag::leAudio>();
|
||||
|
||||
// Mapping codec configuration information
|
||||
auto& transport =
|
||||
codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
|
||||
transport.samplingFrequencyHz.push_back(
|
||||
codec_config.getSamplingFrequency());
|
||||
// Mapping octetsPerCodecFrame to bitdepth for easier comparison.
|
||||
transport.bitdepth.push_back(codec_config.getOctetsPerCodecFrame());
|
||||
transport.frameDurationUs.push_back(codec_config.getFrameDurationUs());
|
||||
switch (strategy_config.getAudioLocation()) {
|
||||
case setting::AudioLocation::MONO:
|
||||
if (strategy_config_channel_count == 1)
|
||||
transport.channelMode.push_back(ChannelMode::MONO);
|
||||
else
|
||||
transport.channelMode.push_back(ChannelMode::DUALMONO);
|
||||
break;
|
||||
case setting::AudioLocation::STEREO:
|
||||
transport.channelMode.push_back(ChannelMode::STEREO);
|
||||
break;
|
||||
default:
|
||||
transport.channelMode.push_back(ChannelMode::UNKNOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Goes through every scenario, deduplicate configuration
|
||||
std::set<std::string> encoding_config, decoding_config, broadcast_config;
|
||||
for (auto& s : supported_scenarios_) {
|
||||
if (s.hasEncode()) encoding_config.insert(s.getEncode());
|
||||
if (s.hasDecode()) decoding_config.insert(s.getDecode());
|
||||
if (s.hasBroadcast()) broadcast_config.insert(s.getBroadcast());
|
||||
}
|
||||
|
||||
// Split by session types and add results
|
||||
const auto encoding_path =
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
const auto decoding_path =
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
|
||||
const auto broadcast_path =
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
session_codecs_map_ =
|
||||
std::unordered_map<SessionType, std::vector<CodecInfo>>();
|
||||
session_codecs_map_[encoding_path] = std::vector<CodecInfo>();
|
||||
session_codecs_map_[decoding_path] = std::vector<CodecInfo>();
|
||||
session_codecs_map_[broadcast_path] = std::vector<CodecInfo>();
|
||||
session_codecs_map_[encoding_path].reserve(encoding_config.size());
|
||||
session_codecs_map_[decoding_path].reserve(decoding_config.size());
|
||||
session_codecs_map_[broadcast_path].reserve(broadcast_config.size());
|
||||
for (auto& c : encoding_config)
|
||||
session_codecs_map_[encoding_path].push_back(config_codec_info_map_[c]);
|
||||
for (auto& c : decoding_config)
|
||||
session_codecs_map_[decoding_path].push_back(config_codec_info_map_[c]);
|
||||
for (auto& c : broadcast_config)
|
||||
session_codecs_map_[broadcast_path].push_back(config_codec_info_map_[c]);
|
||||
|
||||
isInvalidFileContent = session_codecs_map_.empty();
|
||||
|
||||
return session_codecs_map_;
|
||||
}
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
if (!leAudioCodecCapabilities.empty()) {
|
||||
return leAudioCodecCapabilities;
|
||||
}
|
||||
|
||||
isInvalidFileContent = true;
|
||||
|
||||
if (!le_audio_offload_setting.has_value()) {
|
||||
LOG(ERROR)
|
||||
<< __func__
|
||||
<< ": input le_audio_offload_setting content need to be non empty";
|
||||
return {};
|
||||
}
|
||||
|
||||
LoadConfigurationToMap(le_audio_offload_setting);
|
||||
if (supported_scenarios_.empty() || configuration_map_.empty() ||
|
||||
codec_configuration_map_.empty() || strategy_configuration_map_.empty())
|
||||
return {};
|
||||
|
||||
leAudioCodecCapabilities =
|
||||
ComposeLeAudioCodecCapabilities(supported_scenarios_);
|
||||
isInvalidFileContent = leAudioCodecCapabilities.empty();
|
||||
|
||||
return leAudioCodecCapabilities;
|
||||
}
|
||||
|
||||
void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() {
|
||||
leAudioCodecCapabilities.clear();
|
||||
configuration_map_.clear();
|
||||
codec_configuration_map_.clear();
|
||||
strategy_configuration_map_.clear();
|
||||
session_codecs_map_.clear();
|
||||
supported_scenarios_.clear();
|
||||
}
|
||||
|
||||
std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
std::vector<setting::Scenario> supported_scenarios;
|
||||
if (le_audio_offload_setting->hasScenarioList()) {
|
||||
for (const auto& scenario_list :
|
||||
le_audio_offload_setting->getScenarioList()) {
|
||||
if (!scenario_list.hasScenario()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& scenario : scenario_list.getScenario()) {
|
||||
if (scenario.hasEncode() && scenario.hasDecode()) {
|
||||
supported_scenarios.push_back(scenario);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return supported_scenarios;
|
||||
}
|
||||
|
||||
void BluetoothLeAudioCodecsProvider::UpdateConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
if (le_audio_offload_setting->hasConfigurationList()) {
|
||||
for (const auto& configuration_list :
|
||||
le_audio_offload_setting->getConfigurationList()) {
|
||||
if (!configuration_list.hasConfiguration()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& configuration : configuration_list.getConfiguration()) {
|
||||
if (configuration.hasName() && configuration.hasCodecConfiguration() &&
|
||||
configuration.hasStrategyConfiguration()) {
|
||||
configuration_map_.insert(
|
||||
make_pair(configuration.getName(), configuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothLeAudioCodecsProvider::UpdateCodecConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
if (le_audio_offload_setting->hasCodecConfigurationList()) {
|
||||
for (const auto& codec_configuration_list :
|
||||
le_audio_offload_setting->getCodecConfigurationList()) {
|
||||
if (!codec_configuration_list.hasCodecConfiguration()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& codec_configuration :
|
||||
codec_configuration_list.getCodecConfiguration()) {
|
||||
if (IsValidCodecConfiguration(codec_configuration)) {
|
||||
codec_configuration_map_.insert(
|
||||
make_pair(codec_configuration.getName(), codec_configuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothLeAudioCodecsProvider::UpdateStrategyConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
if (le_audio_offload_setting->hasStrategyConfigurationList()) {
|
||||
for (const auto& strategy_configuration_list :
|
||||
le_audio_offload_setting->getStrategyConfigurationList()) {
|
||||
if (!strategy_configuration_list.hasStrategyConfiguration()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& strategy_configuration :
|
||||
strategy_configuration_list.getStrategyConfiguration()) {
|
||||
if (IsValidStrategyConfiguration(strategy_configuration)) {
|
||||
strategy_configuration_map_.insert(make_pair(
|
||||
strategy_configuration.getName(), strategy_configuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothLeAudioCodecsProvider::LoadConfigurationToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting) {
|
||||
ClearLeAudioCodecCapabilities();
|
||||
|
||||
supported_scenarios_ = GetScenarios(le_audio_offload_setting);
|
||||
if (supported_scenarios_.empty()) {
|
||||
LOG(ERROR) << __func__ << ": No scenarios in "
|
||||
<< kLeAudioCodecCapabilitiesFile;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateConfigurationsToMap(le_audio_offload_setting);
|
||||
if (configuration_map_.empty()) {
|
||||
LOG(ERROR) << __func__ << ": No configurations in "
|
||||
<< kLeAudioCodecCapabilitiesFile;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCodecConfigurationsToMap(le_audio_offload_setting);
|
||||
if (codec_configuration_map_.empty()) {
|
||||
LOG(ERROR) << __func__ << ": No codec configurations in "
|
||||
<< kLeAudioCodecCapabilitiesFile;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStrategyConfigurationsToMap(le_audio_offload_setting);
|
||||
if (strategy_configuration_map_.empty()) {
|
||||
LOG(ERROR) << __func__ << ": No strategy configurations in "
|
||||
<< kLeAudioCodecCapabilitiesFile;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities(
|
||||
const std::vector<setting::Scenario>& supported_scenarios) {
|
||||
std::vector<LeAudioCodecCapabilitiesSetting> le_audio_codec_capabilities;
|
||||
for (const auto& scenario : supported_scenarios) {
|
||||
UnicastCapability unicast_encode_capability =
|
||||
GetUnicastCapability(scenario.getEncode());
|
||||
UnicastCapability unicast_decode_capability =
|
||||
GetUnicastCapability(scenario.getDecode());
|
||||
BroadcastCapability broadcast_capability = {.codecType =
|
||||
CodecType::UNKNOWN};
|
||||
|
||||
if (scenario.hasBroadcast()) {
|
||||
broadcast_capability = GetBroadcastCapability(scenario.getBroadcast());
|
||||
}
|
||||
|
||||
// At least one capability should be valid
|
||||
if (unicast_encode_capability.codecType == CodecType::UNKNOWN &&
|
||||
unicast_decode_capability.codecType == CodecType::UNKNOWN &&
|
||||
broadcast_capability.codecType == CodecType::UNKNOWN) {
|
||||
LOG(ERROR) << __func__ << ": None of the capability is valid.";
|
||||
continue;
|
||||
}
|
||||
|
||||
le_audio_codec_capabilities.push_back(
|
||||
{.unicastEncodeCapability = unicast_encode_capability,
|
||||
.unicastDecodeCapability = unicast_decode_capability,
|
||||
.broadcastCapability = broadcast_capability});
|
||||
}
|
||||
return le_audio_codec_capabilities;
|
||||
}
|
||||
|
||||
UnicastCapability BluetoothLeAudioCodecsProvider::GetUnicastCapability(
|
||||
const std::string& coding_direction) {
|
||||
if (coding_direction == "invalid") {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto configuration_iter = configuration_map_.find(coding_direction);
|
||||
if (configuration_iter == configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto codec_configuration_iter = codec_configuration_map_.find(
|
||||
configuration_iter->second.getCodecConfiguration());
|
||||
if (codec_configuration_iter == codec_configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto strategy_configuration_iter = strategy_configuration_map_.find(
|
||||
configuration_iter->second.getStrategyConfiguration());
|
||||
if (strategy_configuration_iter == strategy_configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
CodecType codec_type =
|
||||
GetCodecType(codec_configuration_iter->second.getCodec());
|
||||
if (codec_type == CodecType::LC3) {
|
||||
return ComposeUnicastCapability(
|
||||
codec_type,
|
||||
GetAudioLocation(
|
||||
strategy_configuration_iter->second.getAudioLocation()),
|
||||
strategy_configuration_iter->second.getConnectedDevice(),
|
||||
strategy_configuration_iter->second.getChannelCount(),
|
||||
ComposeLc3Capability(codec_configuration_iter->second));
|
||||
} else if (codec_type == CodecType::APTX_ADAPTIVE_LE ||
|
||||
codec_type == CodecType::APTX_ADAPTIVE_LEX) {
|
||||
return ComposeUnicastCapability(
|
||||
codec_type,
|
||||
GetAudioLocation(
|
||||
strategy_configuration_iter->second.getAudioLocation()),
|
||||
strategy_configuration_iter->second.getConnectedDevice(),
|
||||
strategy_configuration_iter->second.getChannelCount(),
|
||||
ComposeAptxAdaptiveLeCapability(codec_configuration_iter->second));
|
||||
}
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
BroadcastCapability BluetoothLeAudioCodecsProvider::GetBroadcastCapability(
|
||||
const std::string& coding_direction) {
|
||||
if (coding_direction == "invalid") {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto configuration_iter = configuration_map_.find(coding_direction);
|
||||
if (configuration_iter == configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto codec_configuration_iter = codec_configuration_map_.find(
|
||||
configuration_iter->second.getCodecConfiguration());
|
||||
if (codec_configuration_iter == codec_configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
auto strategy_configuration_iter = strategy_configuration_map_.find(
|
||||
configuration_iter->second.getStrategyConfiguration());
|
||||
if (strategy_configuration_iter == strategy_configuration_map_.end()) {
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
CodecType codec_type =
|
||||
GetCodecType(codec_configuration_iter->second.getCodec());
|
||||
std::vector<std::optional<Lc3Capabilities>> bcastLc3Cap(
|
||||
1, std::optional(ComposeLc3Capability(codec_configuration_iter->second)));
|
||||
|
||||
if (codec_type == CodecType::LC3) {
|
||||
return ComposeBroadcastCapability(
|
||||
codec_type,
|
||||
GetAudioLocation(
|
||||
strategy_configuration_iter->second.getAudioLocation()),
|
||||
strategy_configuration_iter->second.getChannelCount(), bcastLc3Cap);
|
||||
}
|
||||
return {.codecType = CodecType::UNKNOWN};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
BroadcastCapability BluetoothLeAudioCodecsProvider::ComposeBroadcastCapability(
|
||||
const CodecType& codec_type, const AudioLocation& audio_location,
|
||||
const uint8_t& channel_count, const std::vector<T>& capability) {
|
||||
return {.codecType = codec_type,
|
||||
.supportedChannel = audio_location,
|
||||
.channelCountPerStream = channel_count,
|
||||
.leAudioCodecCapabilities = std::optional(capability)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
UnicastCapability BluetoothLeAudioCodecsProvider::ComposeUnicastCapability(
|
||||
const CodecType& codec_type, const AudioLocation& audio_location,
|
||||
const uint8_t& device_cnt, const uint8_t& channel_count,
|
||||
const T& capability) {
|
||||
return {
|
||||
.codecType = codec_type,
|
||||
.supportedChannel = audio_location,
|
||||
.deviceCount = device_cnt,
|
||||
.channelCountPerDevice = channel_count,
|
||||
.leAudioCodecCapabilities =
|
||||
UnicastCapability::LeAudioCodecCapabilities(capability),
|
||||
};
|
||||
}
|
||||
|
||||
Lc3Capabilities BluetoothLeAudioCodecsProvider::ComposeLc3Capability(
|
||||
const setting::CodecConfiguration& codec_configuration) {
|
||||
return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
|
||||
.frameDurationUs = {codec_configuration.getFrameDurationUs()},
|
||||
.octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
|
||||
}
|
||||
|
||||
AptxAdaptiveLeCapabilities
|
||||
BluetoothLeAudioCodecsProvider::ComposeAptxAdaptiveLeCapability(
|
||||
const setting::CodecConfiguration& codec_configuration) {
|
||||
return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
|
||||
.frameDurationUs = {codec_configuration.getFrameDurationUs()},
|
||||
.octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
|
||||
}
|
||||
|
||||
AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation(
|
||||
const setting::AudioLocation& audio_location) {
|
||||
switch (audio_location) {
|
||||
case setting::AudioLocation::MONO:
|
||||
return kMonoAudio;
|
||||
case setting::AudioLocation::STEREO:
|
||||
return kStereoAudio;
|
||||
default:
|
||||
return AudioLocation::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
CodecType BluetoothLeAudioCodecsProvider::GetCodecType(
|
||||
const setting::CodecType& codec_type) {
|
||||
switch (codec_type) {
|
||||
case setting::CodecType::LC3:
|
||||
return CodecType::LC3;
|
||||
case setting::CodecType::APTX_ADAPTIVE_LE:
|
||||
return CodecType::APTX_ADAPTIVE_LE;
|
||||
case setting::CodecType::APTX_ADAPTIVE_LEX:
|
||||
return CodecType::APTX_ADAPTIVE_LEX;
|
||||
default:
|
||||
return CodecType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothLeAudioCodecsProvider::IsValidCodecConfiguration(
|
||||
const setting::CodecConfiguration& codec_configuration) {
|
||||
return codec_configuration.hasName() && codec_configuration.hasCodec() &&
|
||||
codec_configuration.hasSamplingFrequency() &&
|
||||
codec_configuration.hasFrameDurationUs() &&
|
||||
codec_configuration.hasOctetsPerCodecFrame();
|
||||
}
|
||||
|
||||
bool BluetoothLeAudioCodecsProvider::IsValidStrategyConfiguration(
|
||||
const setting::StrategyConfiguration& strategy_configuration) {
|
||||
if (!strategy_configuration.hasName() ||
|
||||
!strategy_configuration.hasAudioLocation() ||
|
||||
!strategy_configuration.hasConnectedDevice() ||
|
||||
!strategy_configuration.hasChannelCount()) {
|
||||
return false;
|
||||
}
|
||||
if (strategy_configuration.getAudioLocation() ==
|
||||
setting::AudioLocation::STEREO) {
|
||||
if ((strategy_configuration.getConnectedDevice() == 2 &&
|
||||
strategy_configuration.getChannelCount() == 1) ||
|
||||
(strategy_configuration.getConnectedDevice() == 1 &&
|
||||
strategy_configuration.getChannelCount() == 2)) {
|
||||
// Stereo
|
||||
// 1. two connected device, one for L one for R
|
||||
// 2. one connected device for both L and R
|
||||
return true;
|
||||
} else if (strategy_configuration.getConnectedDevice() == 0 &&
|
||||
strategy_configuration.getChannelCount() == 2) {
|
||||
// Broadcast
|
||||
return true;
|
||||
}
|
||||
} else if (strategy_configuration.getAudioLocation() ==
|
||||
setting::AudioLocation::MONO) {
|
||||
if (strategy_configuration.getConnectedDevice() == 1 &&
|
||||
strategy_configuration.getChannelCount() == 1) {
|
||||
// Mono
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
|
||||
#include "aidl/android/hardware/bluetooth/audio/SessionType.h"
|
||||
#include "aidl_android_hardware_bluetooth_audio_setting.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothLeAudioCodecsProvider {
|
||||
public:
|
||||
static std::optional<setting::LeAudioOffloadSetting>
|
||||
ParseFromLeAudioOffloadSettingFile();
|
||||
static std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
GetLeAudioCodecCapabilities(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
static void ClearLeAudioCodecCapabilities();
|
||||
static std::unordered_map<SessionType, std::vector<CodecInfo>>
|
||||
GetLeAudioCodecInfo(const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
|
||||
private:
|
||||
static inline std::vector<setting::Scenario> supported_scenarios_;
|
||||
static inline std::unordered_map<std::string, setting::Configuration>
|
||||
configuration_map_;
|
||||
static inline std::unordered_map<std::string, setting::CodecConfiguration>
|
||||
codec_configuration_map_;
|
||||
static inline std::unordered_map<std::string, setting::StrategyConfiguration>
|
||||
strategy_configuration_map_;
|
||||
static inline std::unordered_map<SessionType, std::vector<CodecInfo>>
|
||||
session_codecs_map_;
|
||||
|
||||
static std::vector<setting::Scenario> GetScenarios(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
static void UpdateConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
static void UpdateCodecConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
static void UpdateStrategyConfigurationsToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
static void LoadConfigurationToMap(
|
||||
const std::optional<setting::LeAudioOffloadSetting>&
|
||||
le_audio_offload_setting);
|
||||
|
||||
static std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
ComposeLeAudioCodecCapabilities(
|
||||
const std::vector<setting::Scenario>& supported_scenarios);
|
||||
|
||||
static UnicastCapability GetUnicastCapability(
|
||||
const std::string& coding_direction);
|
||||
static BroadcastCapability GetBroadcastCapability(
|
||||
const std::string& coding_direction);
|
||||
|
||||
template <class T>
|
||||
static inline UnicastCapability ComposeUnicastCapability(
|
||||
const CodecType& codec_type, const AudioLocation& audio_location,
|
||||
const uint8_t& device_cnt, const uint8_t& channel_count,
|
||||
const T& capability);
|
||||
|
||||
template <class T>
|
||||
static inline BroadcastCapability ComposeBroadcastCapability(
|
||||
const CodecType& codec_type, const AudioLocation& audio_location,
|
||||
const uint8_t& channel_count, const std::vector<T>& capability);
|
||||
|
||||
static inline Lc3Capabilities ComposeLc3Capability(
|
||||
const setting::CodecConfiguration& codec_configuration);
|
||||
|
||||
static inline AptxAdaptiveLeCapabilities ComposeAptxAdaptiveLeCapability(
|
||||
const setting::CodecConfiguration& codec_configuration);
|
||||
|
||||
static inline AudioLocation GetAudioLocation(
|
||||
const setting::AudioLocation& audio_location);
|
||||
static inline CodecType GetCodecType(const setting::CodecType& codec_type);
|
||||
|
||||
static inline bool IsValidCodecConfiguration(
|
||||
const setting::CodecConfiguration& codec_configuration);
|
||||
static inline bool IsValidStrategyConfiguration(
|
||||
const setting::StrategyConfiguration& strategy_configuration);
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
||||
#include "BluetoothLeAudioCodecsProvider.h"
|
||||
|
||||
using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
|
||||
using aidl::android::hardware::bluetooth::audio::
|
||||
LeAudioCodecCapabilitiesSetting;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::
|
||||
CodecConfigurationList;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::CodecType;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::Configuration;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::ConfigurationList;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::LeAudioOffloadSetting;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::Scenario;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::ScenarioList;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::StrategyConfiguration;
|
||||
using aidl::android::hardware::bluetooth::audio::setting::
|
||||
StrategyConfigurationList;
|
||||
|
||||
typedef std::tuple<std::vector<ScenarioList>, std::vector<ConfigurationList>,
|
||||
std::vector<CodecConfigurationList>,
|
||||
std::vector<StrategyConfigurationList>>
|
||||
OffloadSetting;
|
||||
|
||||
// Define valid components for each list
|
||||
// Scenario
|
||||
static const Scenario kValidScenario(std::make_optional("OneChanStereo_16_1"),
|
||||
std::make_optional("OneChanStereo_16_1"),
|
||||
std::nullopt);
|
||||
static const Scenario kValidBroadcastScenario(
|
||||
std::nullopt, std::nullopt, std::make_optional("BcastStereo_16_2"));
|
||||
|
||||
// Configuration
|
||||
static const Configuration kValidConfigOneChanStereo_16_1(
|
||||
std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
|
||||
// CodecConfiguration
|
||||
static const CodecConfiguration kValidCodecLC3_16k_1(
|
||||
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
|
||||
std::nullopt, std::make_optional(16000), std::make_optional(7500),
|
||||
std::make_optional(30), std::nullopt);
|
||||
// StrategyConfiguration
|
||||
static const StrategyConfiguration kValidStrategyStereoOneCis(
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO), std::make_optional(2),
|
||||
std::make_optional(1));
|
||||
static const StrategyConfiguration kValidStrategyStereoTwoCis(
|
||||
std::make_optional("STEREO_TWO_CISES_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO), std::make_optional(1),
|
||||
std::make_optional(2));
|
||||
static const StrategyConfiguration kValidStrategyMonoOneCis(
|
||||
std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::MONO), std::make_optional(1),
|
||||
std::make_optional(1));
|
||||
static const StrategyConfiguration kValidStrategyBroadcastStereo(
|
||||
std::make_optional("BROADCAST_STEREO"),
|
||||
std::make_optional(AudioLocation::STEREO), std::make_optional(0),
|
||||
std::make_optional(2));
|
||||
|
||||
// Define valid test list built from above valid components
|
||||
// Scenario, Configuration, CodecConfiguration, StrategyConfiguration
|
||||
static const std::vector<ScenarioList> kValidScenarioList = {ScenarioList(
|
||||
std::vector<Scenario>{kValidScenario, kValidBroadcastScenario})};
|
||||
static const std::vector<ConfigurationList> kValidConfigurationList = {
|
||||
ConfigurationList(
|
||||
std::vector<Configuration>{kValidConfigOneChanStereo_16_1})};
|
||||
static const std::vector<CodecConfigurationList> kValidCodecConfigurationList =
|
||||
{CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{kValidCodecLC3_16k_1})};
|
||||
static const std::vector<StrategyConfigurationList>
|
||||
kValidStrategyConfigurationList = {
|
||||
StrategyConfigurationList(std::vector<StrategyConfiguration>{
|
||||
kValidStrategyStereoOneCis, kValidStrategyStereoTwoCis,
|
||||
kValidStrategyMonoOneCis, kValidStrategyBroadcastStereo})};
|
||||
|
||||
class BluetoothLeAudioCodecsProviderTest
|
||||
: public ::testing::TestWithParam<OffloadSetting> {
|
||||
public:
|
||||
static std::vector<OffloadSetting> CreateTestCases(
|
||||
const std::vector<ScenarioList>& scenario_lists,
|
||||
const std::vector<ConfigurationList>& configuration_lists,
|
||||
const std::vector<CodecConfigurationList>& codec_configuration_lists,
|
||||
const std::vector<StrategyConfigurationList>&
|
||||
strategy_configuration_lists) {
|
||||
// make each vector in output test_cases has only one element
|
||||
// to match the input of test params
|
||||
// normally only one vector in input has multiple elements
|
||||
// we just split elements in this vector to several vector
|
||||
std::vector<OffloadSetting> test_cases;
|
||||
for (const auto& scenario_list : scenario_lists) {
|
||||
for (const auto& configuration_list : configuration_lists) {
|
||||
for (const auto& codec_configuration_list : codec_configuration_lists) {
|
||||
for (const auto& strategy_configuration_list :
|
||||
strategy_configuration_lists) {
|
||||
test_cases.push_back(CreateTestCase(
|
||||
scenario_list, configuration_list, codec_configuration_list,
|
||||
strategy_configuration_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return test_cases;
|
||||
}
|
||||
|
||||
protected:
|
||||
void Initialize() {
|
||||
BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities();
|
||||
}
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting> RunTestCase() {
|
||||
auto& [scenario_lists, configuration_lists, codec_configuration_lists,
|
||||
strategy_configuration_lists] = GetParam();
|
||||
LeAudioOffloadSetting le_audio_offload_setting(
|
||||
scenario_lists, configuration_lists, codec_configuration_lists,
|
||||
strategy_configuration_lists);
|
||||
auto le_audio_codec_capabilities =
|
||||
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
|
||||
std::make_optional(le_audio_offload_setting));
|
||||
return le_audio_codec_capabilities;
|
||||
}
|
||||
|
||||
private:
|
||||
static inline OffloadSetting CreateTestCase(
|
||||
const ScenarioList& scenario_list,
|
||||
const ConfigurationList& configuration_list,
|
||||
const CodecConfigurationList& codec_configuration_list,
|
||||
const StrategyConfigurationList& strategy_configuration_list) {
|
||||
return std::make_tuple(
|
||||
std::vector<ScenarioList>{scenario_list},
|
||||
std::vector<ConfigurationList>{configuration_list},
|
||||
std::vector<CodecConfigurationList>{codec_configuration_list},
|
||||
std::vector<StrategyConfigurationList>{strategy_configuration_list});
|
||||
}
|
||||
};
|
||||
|
||||
class GetScenariosTest : public BluetoothLeAudioCodecsProviderTest {
|
||||
public:
|
||||
static std::vector<ScenarioList> CreateInvalidScenarios() {
|
||||
std::vector<ScenarioList> invalid_scenario_test_cases;
|
||||
invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
|
||||
Scenario(std::nullopt, std::make_optional("OneChanStereo_16_1"),
|
||||
std::nullopt)}));
|
||||
|
||||
invalid_scenario_test_cases.push_back(ScenarioList(
|
||||
std::vector<Scenario>{Scenario(std::make_optional("OneChanStereo_16_1"),
|
||||
std::nullopt, std::nullopt)}));
|
||||
|
||||
invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
|
||||
Scenario(std::nullopt, std::nullopt, std::nullopt)}));
|
||||
|
||||
invalid_scenario_test_cases.push_back(
|
||||
ScenarioList(std::vector<Scenario>{}));
|
||||
|
||||
return invalid_scenario_test_cases;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(GetScenariosTest, InvalidScenarios) {
|
||||
Initialize();
|
||||
auto le_audio_codec_capabilities = RunTestCase();
|
||||
ASSERT_TRUE(le_audio_codec_capabilities.empty());
|
||||
}
|
||||
|
||||
class UpdateConfigurationsToMapTest
|
||||
: public BluetoothLeAudioCodecsProviderTest {
|
||||
public:
|
||||
static std::vector<ConfigurationList> CreateInvalidConfigurations() {
|
||||
std::vector<ConfigurationList> invalid_configuration_test_cases;
|
||||
invalid_configuration_test_cases.push_back(
|
||||
ConfigurationList(std::vector<Configuration>{
|
||||
Configuration(std::nullopt, std::make_optional("LC3_16k_1"),
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
|
||||
|
||||
invalid_configuration_test_cases.push_back(
|
||||
ConfigurationList(std::vector<Configuration>{Configuration(
|
||||
std::make_optional("OneChanStereo_16_1"), std::nullopt,
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
|
||||
|
||||
invalid_configuration_test_cases.push_back(
|
||||
ConfigurationList(std::vector<Configuration>{
|
||||
Configuration(std::make_optional("OneChanStereo_16_1"),
|
||||
std::make_optional("LC3_16k_1"), std::nullopt)}));
|
||||
|
||||
invalid_configuration_test_cases.push_back(
|
||||
ConfigurationList(std::vector<Configuration>{}));
|
||||
|
||||
return invalid_configuration_test_cases;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(UpdateConfigurationsToMapTest, InvalidConfigurations) {
|
||||
Initialize();
|
||||
auto le_audio_codec_capabilities = RunTestCase();
|
||||
ASSERT_TRUE(le_audio_codec_capabilities.empty());
|
||||
}
|
||||
|
||||
class UpdateCodecConfigurationsToMapTest
|
||||
: public BluetoothLeAudioCodecsProviderTest {
|
||||
public:
|
||||
static std::vector<CodecConfigurationList>
|
||||
CreateInvalidCodecConfigurations() {
|
||||
std::vector<CodecConfigurationList> invalid_codec_configuration_test_cases;
|
||||
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{CodecConfiguration(
|
||||
std::nullopt, std::make_optional(CodecType::LC3), std::nullopt,
|
||||
std::make_optional(16000), std::make_optional(7500),
|
||||
std::make_optional(30), std::nullopt)}));
|
||||
|
||||
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{CodecConfiguration(
|
||||
std::make_optional("LC3_16k_1"), std::nullopt, std::nullopt,
|
||||
std::make_optional(16000), std::make_optional(7500),
|
||||
std::make_optional(30), std::nullopt)}));
|
||||
|
||||
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{CodecConfiguration(
|
||||
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
|
||||
std::nullopt, std::nullopt, std::make_optional(7500),
|
||||
std::make_optional(30), std::nullopt)}));
|
||||
|
||||
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{CodecConfiguration(
|
||||
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
|
||||
std::nullopt, std::make_optional(16000), std::nullopt,
|
||||
std::make_optional(30), std::nullopt)}));
|
||||
|
||||
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
|
||||
std::vector<CodecConfiguration>{CodecConfiguration(
|
||||
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
|
||||
std::nullopt, std::make_optional(16000), std::make_optional(7500),
|
||||
std::nullopt, std::nullopt)}));
|
||||
|
||||
invalid_codec_configuration_test_cases.push_back(
|
||||
CodecConfigurationList(std::vector<CodecConfiguration>{}));
|
||||
|
||||
return invalid_codec_configuration_test_cases;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(UpdateCodecConfigurationsToMapTest, InvalidCodecConfigurations) {
|
||||
Initialize();
|
||||
auto le_audio_codec_capabilities = RunTestCase();
|
||||
ASSERT_TRUE(le_audio_codec_capabilities.empty());
|
||||
}
|
||||
|
||||
class UpdateStrategyConfigurationsToMapTest
|
||||
: public BluetoothLeAudioCodecsProviderTest {
|
||||
public:
|
||||
static std::vector<StrategyConfigurationList>
|
||||
CreateInvalidStrategyConfigurations() {
|
||||
std::vector<StrategyConfigurationList>
|
||||
invalid_strategy_configuration_test_cases;
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO),
|
||||
std::make_optional(2), std::make_optional(2))}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO),
|
||||
std::make_optional(2), std::make_optional(2))}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::nullopt, std::make_optional(AudioLocation::STEREO),
|
||||
std::make_optional(2), std::make_optional(1))}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"), std::nullopt,
|
||||
std::make_optional(2), std::make_optional(1))}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO), std::nullopt,
|
||||
std::make_optional(1))}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(
|
||||
std::vector<StrategyConfiguration>{StrategyConfiguration(
|
||||
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
|
||||
std::make_optional(AudioLocation::STEREO),
|
||||
std::make_optional(2), std::nullopt)}));
|
||||
|
||||
invalid_strategy_configuration_test_cases.push_back(
|
||||
StrategyConfigurationList(std::vector<StrategyConfiguration>{}));
|
||||
|
||||
return invalid_strategy_configuration_test_cases;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(UpdateStrategyConfigurationsToMapTest, InvalidStrategyConfigurations) {
|
||||
Initialize();
|
||||
auto le_audio_codec_capabilities = RunTestCase();
|
||||
ASSERT_TRUE(le_audio_codec_capabilities.empty());
|
||||
}
|
||||
|
||||
class ComposeLeAudioCodecCapabilitiesTest
|
||||
: public BluetoothLeAudioCodecsProviderTest {
|
||||
public:
|
||||
};
|
||||
|
||||
TEST_P(ComposeLeAudioCodecCapabilitiesTest, CodecCapabilitiesNotEmpty) {
|
||||
Initialize();
|
||||
auto le_audio_codec_capabilities = RunTestCase();
|
||||
ASSERT_TRUE(!le_audio_codec_capabilities.empty());
|
||||
}
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
|
||||
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
|
||||
GetScenariosTest::CreateInvalidScenarios(), kValidConfigurationList,
|
||||
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateConfigurationsToMapTest);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BluetoothLeAudioCodecsProviderTest, UpdateConfigurationsToMapTest,
|
||||
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
|
||||
kValidScenarioList,
|
||||
UpdateConfigurationsToMapTest::CreateInvalidConfigurations(),
|
||||
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
|
||||
UpdateCodecConfigurationsToMapTest);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BluetoothLeAudioCodecsProviderTest, UpdateCodecConfigurationsToMapTest,
|
||||
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
|
||||
kValidScenarioList, kValidConfigurationList,
|
||||
UpdateCodecConfigurationsToMapTest::CreateInvalidCodecConfigurations(),
|
||||
kValidStrategyConfigurationList)));
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
|
||||
UpdateStrategyConfigurationsToMapTest);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BluetoothLeAudioCodecsProviderTest, UpdateStrategyConfigurationsToMapTest,
|
||||
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
|
||||
kValidScenarioList, kValidConfigurationList,
|
||||
kValidCodecConfigurationList,
|
||||
UpdateStrategyConfigurationsToMapTest::
|
||||
CreateInvalidStrategyConfigurations())));
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
|
||||
ComposeLeAudioCodecCapabilitiesTest);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BluetoothLeAudioCodecsProviderTest, ComposeLeAudioCodecCapabilitiesTest,
|
||||
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
|
||||
kValidScenarioList, kValidConfigurationList,
|
||||
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -467,6 +467,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1(
|
|||
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
|
||||
audio_config.get<AudioConfiguration::a2dpConfig>()));
|
||||
break;
|
||||
case AudioConfiguration::a2dp:
|
||||
break;
|
||||
case AudioConfiguration::leAudioConfig:
|
||||
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1(
|
||||
audio_config.get<AudioConfiguration::leAudioConfig>()));
|
||||
|
@ -475,6 +477,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1(
|
|||
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_broadcast_config_2_1(
|
||||
audio_config.get<AudioConfiguration::leAudioBroadcastConfig>()));
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << __func__ << ": unexpected AudioConfiguration";
|
||||
}
|
||||
return hidl_audio_config;
|
||||
}
|
||||
|
@ -583,7 +587,7 @@ bool HidlToAidlMiddleware_2_0::GetPresentationPosition(
|
|||
*total_bytes_readed = presentation_position.transmittedOctets;
|
||||
if (data_position)
|
||||
*data_position = {
|
||||
.tv_sec = static_cast<__kernel_old_time_t>(
|
||||
.tv_sec = static_cast<long>(
|
||||
presentation_position.transmittedOctetsTimestamp.tvSec),
|
||||
.tv_nsec = static_cast<long>(
|
||||
presentation_position.transmittedOctetsTimestamp.tvNSec)};
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!---
|
||||
This is an example to configure LE Audio hardware offload supported capability settings
|
||||
In the follow list, there would be only one list in this file. Add element into each list as needed.
|
||||
|
||||
codecConfigurationList:
|
||||
Supported codec capability along with its parameter setting
|
||||
|
||||
strategyConfigurationList:
|
||||
ASE Configuration strategies
|
||||
|
||||
configurationList:
|
||||
For each configuration , there are two attributes
|
||||
- codecConfiguration
|
||||
- strategyConfiguration
|
||||
|
||||
scenarioList:
|
||||
There would be only one `scenarios` group
|
||||
For each scenario, the are two attributes
|
||||
- encode
|
||||
- decode
|
||||
If a scenario is unidirectional, mark another direction as `invalid`
|
||||
The configuration should be chosen from `configurationList`
|
||||
-->
|
||||
<leAudioOffloadSetting>
|
||||
<scenarioList>
|
||||
<!-- encode only -->
|
||||
<scenario encode="OneChanMono_16_1" decode="invalid"/>
|
||||
<scenario encode="TwoChanStereo_16_1" decode="invalid"/>
|
||||
<scenario encode="OneChanStereo_16_1" decode="invalid"/>
|
||||
<scenario encode="OneChanMono_16_2" decode="invalid"/>
|
||||
<scenario encode="TwoChanStereo_16_2" decode="invalid"/>
|
||||
<scenario encode="OneChanStereo_16_2" decode="invalid"/>
|
||||
<scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_48" decode="invalid"/>
|
||||
<scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_96" decode="invalid"/>
|
||||
<scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_48" decode="invalid"/>
|
||||
<scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_96" decode="invalid"/>
|
||||
<!-- encode and decode -->
|
||||
<scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/>
|
||||
<scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/>
|
||||
<scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/>
|
||||
<scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/>
|
||||
<scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/>
|
||||
<scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/>
|
||||
<scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/>
|
||||
<scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/>
|
||||
<!-- broadcast -->
|
||||
<scenario encode="invalid" decode="invalid" broadcast="BcastStereo_16_2"/>
|
||||
</scenarioList>
|
||||
<configurationList>
|
||||
<configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
|
||||
<configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
<configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
|
||||
<configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
|
||||
<configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
<configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
|
||||
<configuration name="BcastStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="BROADCAST_STEREO"/>
|
||||
<configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LE_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
<configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LE_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
<configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LEX_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
<configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LEX_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
|
||||
</configurationList>
|
||||
<codecConfigurationList>
|
||||
<codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/>
|
||||
<codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/>
|
||||
<codecConfiguration name="APTX_ADAPTIVE_LE_48k" codec="APTX_ADAPTIVE_LE" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
|
||||
<codecConfiguration name="APTX_ADAPTIVE_LE_96k" codec="APTX_ADAPTIVE_LE" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
|
||||
<codecConfiguration name="APTX_ADAPTIVE_LEX_48k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
|
||||
<codecConfiguration name="APTX_ADAPTIVE_LEX_96k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
|
||||
</codecConfigurationList>
|
||||
<strategyConfigurationList>
|
||||
<strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/>
|
||||
<strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/>
|
||||
<strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/>
|
||||
<strategyConfiguration name="BROADCAST_STEREO" audioLocation="STEREO" connectedDevice="0" channelCount="2"/>
|
||||
</strategyConfigurationList>
|
||||
</leAudioOffloadSetting>
|
|
@ -0,0 +1,77 @@
|
|||
<!-- LE Audio Offload Codec Capability Schema -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="leAudioOffloadSetting">
|
||||
<xs:complexType>
|
||||
<xs:element ref="scenarioList" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element ref="configurationList" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element ref="codecConfigurationList" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element ref="strategyConfigurationList" minOccurs="1" maxOccurs="1"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="scenarioList">
|
||||
<xs:complexType>
|
||||
<xs:element ref="scenario" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="configurationList">
|
||||
<xs:complexType>
|
||||
<xs:element ref="configuration" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="codecConfigurationList">
|
||||
<xs:complexType>
|
||||
<xs:element ref="codecConfiguration" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="strategyConfigurationList">
|
||||
<xs:complexType>
|
||||
<xs:element ref="strategyConfiguration" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="scenario">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="encode" type="xs:string"/>
|
||||
<xs:attribute name="decode" type="xs:string"/>
|
||||
<xs:attribute name="broadcast" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="configuration">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="name" type="xs:string"/>
|
||||
<xs:attribute name="codecConfiguration" type="xs:string"/>
|
||||
<xs:attribute name="strategyConfiguration" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="codecConfiguration">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="name" type="xs:string"/>
|
||||
<xs:attribute name="codec" type="codecType"/>
|
||||
<xs:attribute name="pcmBitDepth" type="xs:unsignedByte"/>
|
||||
<xs:attribute name="samplingFrequency" type="xs:int"/>
|
||||
<xs:attribute name="frameDurationUs" type="xs:int"/>
|
||||
<xs:attribute name="octetsPerCodecFrame" type="xs:int"/>
|
||||
<xs:attribute name="codecFrameBlocksPerSdu" type="xs:unsignedByte"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="strategyConfiguration">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="name" type="xs:string"/>
|
||||
<xs:attribute name="audioLocation" type="audioLocation"/>
|
||||
<xs:attribute name="connectedDevice" type="xs:unsignedByte"/>
|
||||
<xs:attribute name="channelCount" type="xs:unsignedByte"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:simpleType name="audioLocation">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="MONO"/>
|
||||
<xs:enumeration value="STEREO"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="codecType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="LC3"/>
|
||||
<xs:enumeration value="APTX_ADAPTIVE_LE"/>
|
||||
<xs:enumeration value="APTX_ADAPTIVE_LEX"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,107 @@
|
|||
// Signature format: 2.0
|
||||
package aidl.android.hardware.bluetooth.audio.setting {
|
||||
|
||||
public enum AudioLocation {
|
||||
method public String getRawName();
|
||||
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation MONO;
|
||||
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation STEREO;
|
||||
}
|
||||
|
||||
public class CodecConfiguration {
|
||||
ctor public CodecConfiguration();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.CodecType getCodec();
|
||||
method public short getCodecFrameBlocksPerSdu();
|
||||
method public int getFrameDurationUs();
|
||||
method public String getName();
|
||||
method public int getOctetsPerCodecFrame();
|
||||
method public short getPcmBitDepth();
|
||||
method public int getSamplingFrequency();
|
||||
method public void setCodec(aidl.android.hardware.bluetooth.audio.setting.CodecType);
|
||||
method public void setCodecFrameBlocksPerSdu(short);
|
||||
method public void setFrameDurationUs(int);
|
||||
method public void setName(String);
|
||||
method public void setOctetsPerCodecFrame(int);
|
||||
method public void setPcmBitDepth(short);
|
||||
method public void setSamplingFrequency(int);
|
||||
}
|
||||
|
||||
public class CodecConfigurationList {
|
||||
ctor public CodecConfigurationList();
|
||||
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.CodecConfiguration> getCodecConfiguration();
|
||||
}
|
||||
|
||||
public enum CodecType {
|
||||
method public String getRawName();
|
||||
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LE;
|
||||
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LEX;
|
||||
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType LC3;
|
||||
}
|
||||
|
||||
public class Configuration {
|
||||
ctor public Configuration();
|
||||
method public String getCodecConfiguration();
|
||||
method public String getName();
|
||||
method public String getStrategyConfiguration();
|
||||
method public void setCodecConfiguration(String);
|
||||
method public void setName(String);
|
||||
method public void setStrategyConfiguration(String);
|
||||
}
|
||||
|
||||
public class ConfigurationList {
|
||||
ctor public ConfigurationList();
|
||||
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Configuration> getConfiguration();
|
||||
}
|
||||
|
||||
public class LeAudioOffloadSetting {
|
||||
ctor public LeAudioOffloadSetting();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList getCodecConfigurationList();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.ConfigurationList getConfigurationList();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.ScenarioList getScenarioList();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList getStrategyConfigurationList();
|
||||
method public void setCodecConfigurationList(aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList);
|
||||
method public void setConfigurationList(aidl.android.hardware.bluetooth.audio.setting.ConfigurationList);
|
||||
method public void setScenarioList(aidl.android.hardware.bluetooth.audio.setting.ScenarioList);
|
||||
method public void setStrategyConfigurationList(aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList);
|
||||
}
|
||||
|
||||
public class Scenario {
|
||||
ctor public Scenario();
|
||||
method public String getBroadcast();
|
||||
method public String getDecode();
|
||||
method public String getEncode();
|
||||
method public void setBroadcast(String);
|
||||
method public void setDecode(String);
|
||||
method public void setEncode(String);
|
||||
}
|
||||
|
||||
public class ScenarioList {
|
||||
ctor public ScenarioList();
|
||||
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Scenario> getScenario();
|
||||
}
|
||||
|
||||
public class StrategyConfiguration {
|
||||
ctor public StrategyConfiguration();
|
||||
method public aidl.android.hardware.bluetooth.audio.setting.AudioLocation getAudioLocation();
|
||||
method public short getChannelCount();
|
||||
method public short getConnectedDevice();
|
||||
method public String getName();
|
||||
method public void setAudioLocation(aidl.android.hardware.bluetooth.audio.setting.AudioLocation);
|
||||
method public void setChannelCount(short);
|
||||
method public void setConnectedDevice(short);
|
||||
method public void setName(String);
|
||||
}
|
||||
|
||||
public class StrategyConfigurationList {
|
||||
ctor public StrategyConfigurationList();
|
||||
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.StrategyConfiguration> getStrategyConfiguration();
|
||||
}
|
||||
|
||||
public class XmlParser {
|
||||
ctor public XmlParser();
|
||||
method public static aidl.android.hardware.bluetooth.audio.setting.LeAudioOffloadSetting readLeAudioOffloadSetting(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
// Signature format: 2.0
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
namespace aidl.android.hardware.bluetooth.audio.le_audio;
|
||||
enum CodecSpecificLtvGenericTypes : byte {
|
||||
SUPPORTED_SAMPLING_FREQUENCY = 0x01,
|
||||
SUPPORTED_FRAME_DURATION = 0x02,
|
||||
SUPPORTED_AUDIO_CHANNEL_ALLOCATION = 0x03,
|
||||
SUPPORTED_OCTETS_PER_CODEC_FRAME = 0x04,
|
||||
SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU = 0x05,
|
||||
}
|
||||
/// Note: Holds either a single value (when `value_width == 0`) or multiple
|
||||
/// values if `value.length()` is no-remainder divisible by the non-zero
|
||||
/// `value_width`.
|
||||
/// Note: Consider extending it with `flags` field, to hold additional info like
|
||||
/// IsBitfield, IsRange, etc. if we need these type-specific validations.
|
||||
table CompoundValue {
|
||||
value: [ubyte] (required);
|
||||
value_width: ubyte = 0;
|
||||
}
|
||||
table CodecSpecificConfiguration {
|
||||
name: string;
|
||||
type: ubyte (key);
|
||||
compound_value: CompoundValue;
|
||||
}
|
||||
struct CodecId {
|
||||
coding_format: ubyte;
|
||||
vendor_company_id : ushort;
|
||||
vendor_codec_id : ushort;
|
||||
}
|
||||
enum AudioSetConfigurationStrategy : byte {
|
||||
MONO_ONE_CIS_PER_DEVICE = 0x00,
|
||||
STEREO_TWO_CISES_PER_DEVICE = 0x01,
|
||||
STEREO_ONE_CIS_PER_DEVICE = 0x02,
|
||||
}
|
||||
enum AudioSetConfigurationDirection : byte {
|
||||
SINK = 0x01,
|
||||
SOURCE = 0x02,
|
||||
}
|
||||
enum AudioSetConfigurationTargetLatency : byte {
|
||||
LOW = 0x01,
|
||||
BALANCED_RELIABILITY = 0x02,
|
||||
HIGH_RELIABILITY = 0x03,
|
||||
}
|
||||
table AudioSetSubConfiguration {
|
||||
device_cnt: ubyte;
|
||||
ase_cnt: ubyte;
|
||||
direction: AudioSetConfigurationDirection = SINK;
|
||||
configuration_strategy: AudioSetConfigurationStrategy;
|
||||
codec_id : CodecId (required);
|
||||
codec_configuration: [CodecSpecificConfiguration] (required);
|
||||
}
|
||||
table CodecConfiguration {
|
||||
name: string (key, required);
|
||||
subconfigurations: [AudioSetSubConfiguration] (required);
|
||||
}
|
||||
table QosConfiguration {
|
||||
name: string (key, required);
|
||||
target_latency: AudioSetConfigurationTargetLatency = BALANCED_RELIABILITY;
|
||||
retransmission_number: ubyte;
|
||||
max_transport_latency : ushort;
|
||||
}
|
||||
/// Each set configration can contain multiple logical subconfigurations, which
|
||||
/// all must be configurable with the current set of audio devices. For example,
|
||||
/// one can define multiple output stream configurations with different
|
||||
/// qualities, or assign different configurations to each stream direction.
|
||||
table AudioSetConfiguration {
|
||||
name: string (key, required);
|
||||
codec_config_name: string (required);
|
||||
qos_config_name: [string] (required);
|
||||
}
|
||||
table AudioSetConfigurations {
|
||||
_comments_: [string];
|
||||
configurations: [AudioSetConfiguration] (required);
|
||||
codec_configurations: [CodecConfiguration] (required);
|
||||
qos_configurations: [QosConfiguration] (required);
|
||||
}
|
||||
root_type AudioSetConfigurations;
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
namespace aidl.android.hardware.bluetooth.audio.le_audio;
|
||||
/// Scenario represents the use case such as "Media", "Conversation", etc.
|
||||
/// Each scenario can list any number of codec configurations by their names in
|
||||
/// the order of preference. That means if the first entry does not meet all
|
||||
/// the current requirements (such as peer device capabilities etc.) next
|
||||
/// configurations are being checked.
|
||||
///
|
||||
/// The referenced codec configurations are defined by the
|
||||
/// audio_set_configurations.fbs schema and loaded from a different source file.
|
||||
/// Multiple scenarios can reference same codec configurations.
|
||||
table AudioSetScenario {
|
||||
_comments_: [string];
|
||||
name: string (key, required);
|
||||
configurations: [string] (required);
|
||||
}
|
||||
table AudioSetScenarios {
|
||||
_comments_: [string];
|
||||
scenarios: [AudioSetScenario] (required);
|
||||
}
|
||||
root_type AudioSetScenarios;
|
|
@ -0,0 +1,304 @@
|
|||
{
|
||||
"_comments_": [
|
||||
"== Audio Set Scenarios ==",
|
||||
" Each defined scenario references externally defined audio set",
|
||||
" configurations, listed in the order of priority."
|
||||
],
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "Conversational",
|
||||
"configurations": [
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_2",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_2",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_2",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_2",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
|
||||
"DualDev_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_48_4_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_48_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_48_3_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_48_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_32_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_24_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_16_1_Balanced_Reliability",
|
||||
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_Server_Prefered_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_60oct_R3_L22_1",
|
||||
"DualDev_OneChanMonoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_16_2_Balanced_Reliability"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Media",
|
||||
"configurations": [
|
||||
"DualDev_OneChanStereoSnk_48_4_High_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_2",
|
||||
"DualDev_OneChanStereoSnk_48_2_High_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_2_2",
|
||||
"DualDev_OneChanStereoSnk_48_3_High_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_3_2",
|
||||
"DualDev_OneChanStereoSnk_48_1_High_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_1_2",
|
||||
"DualDev_OneChanStereoSnk_24_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_24_2_2",
|
||||
"DualDev_OneChanStereoSnk_16_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_16_2_2",
|
||||
"DualDev_OneChanStereoSnk_16_1_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_16_1_2",
|
||||
"SingleDev_OneChanStereoSnk_48_4_High_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_4_2",
|
||||
"SingleDev_OneChanStereoSnk_48_2_High_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_2_2",
|
||||
"SingleDev_OneChanStereoSnk_48_3_High_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_3_2",
|
||||
"SingleDev_OneChanStereoSnk_48_1_High_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_1_2",
|
||||
"SingleDev_OneChanStereoSnk_24_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_24_2_2",
|
||||
"SingleDev_OneChanStereoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_16_2_2",
|
||||
"SingleDev_OneChanStereoSnk_16_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_16_1_2",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_High_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_2",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_High_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_2",
|
||||
"SingleDev_TwoChanStereoSnk_48_2_High_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_2_2",
|
||||
"SingleDev_TwoChanStereoSnk_48_3_High_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_3_2",
|
||||
"SingleDev_TwoChanStereoSnk_48_1_High_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_1_2",
|
||||
"SingleDev_TwoChanStereoSnk_24_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_24_2_2",
|
||||
"SingleDev_TwoChanStereoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_16_2_2",
|
||||
"SingleDev_TwoChanStereoSnk_16_1_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_16_1_2",
|
||||
"SingleDev_OneChanMonoSnk_48_4_High_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_4_2",
|
||||
"SingleDev_OneChanMonoSnk_48_2_High_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_2_2",
|
||||
"SingleDev_OneChanMonoSnk_48_3_High_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_3_2",
|
||||
"SingleDev_OneChanMonoSnk_48_1_High_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_1_2",
|
||||
"SingleDev_OneChanMonoSnk_32_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_32_2_2",
|
||||
"SingleDev_OneChanMonoSnk_32_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_32_1_2",
|
||||
"SingleDev_OneChanMonoSnk_24_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_24_2_2",
|
||||
"SingleDev_OneChanMonoSnk_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_16_2_2",
|
||||
"SingleDev_OneChanMonoSnk_16_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_16_1_2",
|
||||
"VND_DualDev_OneChanStereoSnk_48khz_100octs_High_Reliability_1",
|
||||
"VND_DualDev_OneChanStereoSnk_48khz_100octs_R15_L70_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_100octs_High_Reliability_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_100octs_R15_L70_1",
|
||||
"VND_SingleDev_OneChanStereoSnk_48khz_100octs_High_Reliability_1",
|
||||
"VND_SingleDev_OneChanStereoSnk_48khz_100octs_R15_L70_1",
|
||||
"DualDev_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Game",
|
||||
"configurations": [
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_TwoChanStereoSrc_16khz_30octs_Balanced_Reliability_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_TwoChanStereoSrc_16khz_30octs_R3_L12_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_High_Reliability_1",
|
||||
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_48_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_48_3_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_48_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_32_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_24_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_24_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_48_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_48_3_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_48_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_32_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_24_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_24_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_16_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_48_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_48_3_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_48_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_32_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_32_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_24_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_24_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_16_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_16_1_Low_Latency"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "VoiceAssistants",
|
||||
"configurations": [
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_16_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_24_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_32_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_24_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Live",
|
||||
"configurations": [
|
||||
"VND_SingleDev_TwoChanStereoSrc_48khz_100octs_Balanced_Reliability_1",
|
||||
"VND_SingleDev_TwoChanStereoSrc_48khz_100octs_R11_L40_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
|
||||
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
|
||||
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
|
||||
"SingleDev_OneChanMonoSrc_48_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_48_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_32_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_32_1_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability",
|
||||
"SingleDev_OneChanMonoSrc_16_1_Balanced_Reliability"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue