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

Commit 4e34d05b authored by Antoine SOULIER's avatar Antoine SOULIER Committed by Henri Chataing
Browse files

Codec Extensibility A2DP HAL Reference Implementation

Test: m
Bug: 297037759
Change-Id: I84b32c2ef22da8c3e920e497501b9f16feaf803c
parent 33c4e5a6
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
/*
 * 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
+67 −8
Original line number Diff line number Diff line
@@ -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) {
  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>())) {
            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
+10 −1
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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
Loading