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

Commit 361aeb61 authored by Jeremy Wu's avatar Jeremy Wu Committed by Gerrit Code Review
Browse files

Merge "Floss: generalize LC3 decode buffer" into main

parents 8fb68002 8f29050e
Loading
Loading
Loading
Loading
+108 −28
Original line number Diff line number Diff line
@@ -139,6 +139,14 @@ size_t write(const uint8_t* p_buf, uint32_t len) {
  return UIPC_Send(*sco_uipc, UIPC_CH_ID_AV_AUDIO, 0, p_buf, len) ? len : 0;
}

enum decode_buf_state {
  DECODE_BUF_EMPTY,
  DECODE_BUF_FULL,

  // Neither empty nor full.
  DECODE_BUF_HALFFULL,
};

namespace wbs {

/* Second octet of H2 header is composed by 4 bits fixed 0x8 and 4 bits
@@ -396,14 +404,6 @@ struct tBTM_MSBC_INFO {
  size_t packet_size; /* SCO mSBC packet size supported by lower layer */
  size_t buf_size; /* The size of the buffer, determined by the packet_size. */

  enum decode_buf_state {
    DECODE_BUF_EMPTY,
    DECODE_BUF_FULL,

    // Neither empty nor full.
    DECODE_BUF_HALFFULL,
  };

  uint8_t* packet_buf;      /* Temporary buffer to store the data */
  uint8_t* msbc_decode_buf; /* Buffer to store mSBC packets to decode */
  size_t decode_buf_wo;     /* Write offset of the decode buffer */
@@ -843,20 +843,43 @@ constexpr uint8_t btm_h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8};
 * code ties to limited packet size values. Specifically list them out
 * to check against when setting packet size. The first entry is the default
 * value as a fallback. */
constexpr size_t btm_swb_supported_pkt_size[] = {BTM_LC3_PKT_LEN, 72, 0};
constexpr size_t btm_swb_supported_pkt_size[] = {BTM_LC3_PKT_LEN, 72, 24, 0};

/* Buffer size should be set to least common multiple of SCO packet size and
 * BTM_LC3_PKT_LEN for optimizing buffer copy. */
constexpr size_t btm_swb_lc3_buffer_size[] = {BTM_LC3_PKT_LEN, 360, 0};
constexpr size_t btm_swb_lc3_buffer_size[] = {BTM_LC3_PKT_LEN, 360, 120, 0};

/* Define the structure that contains LC3 data */
struct tBTM_LC3_INFO {
  size_t packet_size; /* SCO LC3 packet size supported by lower layer */
  size_t buf_size; /* The size of the buffer, determined by the packet_size. */

  uint8_t* packet_buf;     /* Temporary buffer to store the data */
  uint8_t* lc3_decode_buf; /* Buffer to store LC3 packets to decode */
  size_t decode_buf_wo;    /* Write offset of the decode buffer */
  size_t decode_buf_ro;    /* Read offset of the decode buffer */

  /* Within the circular buffer, which can be visualized as having
     two halves, mirror indicators track the pointer's location,
     signaling whether it resides in the first or second segment:

              [buf_size-1] ┼ - - -─┼ [0]
                           │       │
                           │       │
     wo = x, wo_mirror = 0 ^       v ro = x, ro_mirror = 1
                           │       │
                           │       │
                       [0] ┼ - - - ┼ [buf_size-1]
              (First Half)           (Second Half)
  */
  bool decode_buf_wo_mirror; /* The mirror indicator specifies whether
                                the write pointer is currently located
                                in the first or second half of the
                                circular buffer */
  bool decode_buf_ro_mirror; /* The mirror indicator specifies whether
                                the read pointer is currently located
                                in the first or second half of the
                                circular buffer */
  bool read_corrupted;     /* If the current LC3 packet read is corrupted */

  uint8_t* lc3_encode_buf; /* Buffer to store the encoded SCO packets */
@@ -902,6 +925,9 @@ struct tBTM_LC3_INFO {
  size_t init(size_t pkt_size) {
    decode_buf_wo = 0;
    decode_buf_ro = 0;
    decode_buf_wo_mirror = false;
    decode_buf_ro_mirror = false;

    encode_buf_wo = 0;
    encode_buf_ro = 0;

@@ -909,6 +935,8 @@ struct tBTM_LC3_INFO {
    if (pkt_size == packet_size) return packet_size;
    packet_size = pkt_size;

    if (!packet_buf) packet_buf = (uint8_t*)osi_calloc(BTM_LC3_PKT_LEN);

    if (lc3_decode_buf) osi_free(lc3_decode_buf);
    lc3_decode_buf = (uint8_t*)osi_calloc(buf_size);

@@ -924,11 +952,44 @@ struct tBTM_LC3_INFO {

  void deinit() {
    if (lc3_decode_buf) osi_free(lc3_decode_buf);
    if (packet_buf) osi_free(packet_buf);
    if (lc3_encode_buf) osi_free(lc3_encode_buf);
    if (pkt_status) osi_free_and_reset((void**)&pkt_status);
  }

  size_t decodable() { return decode_buf_wo - decode_buf_ro; }
  void incr_buf_offset(size_t& offset, bool& mirror, size_t bsize,
                       size_t amount) {
    if (bsize - offset > amount) {
      offset += amount;
      return;
    }

    mirror = !mirror;
    offset = amount - (bsize - offset);
  }

  decode_buf_state decode_buf_status() {
    if (decode_buf_ro == decode_buf_wo) {
      if (decode_buf_ro_mirror == decode_buf_wo_mirror) return DECODE_BUF_EMPTY;
      return DECODE_BUF_FULL;
    }
    return DECODE_BUF_HALFFULL;
  }

  size_t decode_buf_data_len() {
    switch (decode_buf_status()) {
      case DECODE_BUF_EMPTY:
        return 0;
      case DECODE_BUF_FULL:
        return buf_size;
      case DECODE_BUF_HALFFULL:
      default:
        if (decode_buf_wo > decode_buf_ro) return decode_buf_wo - decode_buf_ro;
        return buf_size - (decode_buf_ro - decode_buf_wo);
    };
  }

  size_t decode_buf_avail_len() { return buf_size - decode_buf_data_len(); }

  uint8_t* fill_lc3_pkt_template() {
    uint8_t* wp = &lc3_encode_buf[encode_buf_wo];
@@ -946,29 +1007,35 @@ struct tBTM_LC3_INFO {
  }

  void mark_pkt_decoded() {
    if (decode_buf_ro + BTM_LC3_PKT_LEN > decode_buf_wo) {
    if (decode_buf_data_len() < BTM_LC3_PKT_LEN) {
      log::error("Trying to mark read offset beyond write offset.");
      return;
    }

    decode_buf_ro += BTM_LC3_PKT_LEN;
    if (decode_buf_ro == decode_buf_wo) {
      decode_buf_ro = 0;
      decode_buf_wo = 0;
    }
    incr_buf_offset(decode_buf_ro, decode_buf_ro_mirror, buf_size,
                    BTM_LC3_PKT_LEN);
  }

  size_t write(const std::vector<uint8_t>& input) {
    if (input.size() > buf_size - decode_buf_wo) {
    if (input.size() > decode_buf_avail_len()) {
      log::warn(
          "Cannot write input with size {} into decode_buf with {} empty "
          "space.",
          input.size(), buf_size - decode_buf_wo);
          input.size(), decode_buf_avail_len());
      return 0;
    }

    if (buf_size - decode_buf_wo > input.size()) {
      std::copy(input.begin(), input.end(), lc3_decode_buf + decode_buf_wo);
    decode_buf_wo += input.size();
    } else {
      std::copy(input.begin(), input.begin() + buf_size - decode_buf_wo,
                lc3_decode_buf + decode_buf_wo);
      std::copy(input.begin() + buf_size - decode_buf_wo, input.end(),
                lc3_decode_buf);
    }

    incr_buf_offset(decode_buf_wo, decode_buf_wo_mirror, buf_size,
                    input.size());
    return input.size();
  }

@@ -979,10 +1046,12 @@ struct tBTM_LC3_INFO {
    }

    size_t rp = 0;
    while (rp < BTM_LC3_PKT_LEN &&
           decode_buf_wo - (decode_buf_ro + rp) >= BTM_LC3_PKT_LEN) {
      if ((lc3_decode_buf[decode_buf_ro + rp] != BTM_LC3_H2_HEADER_0) ||
          !verify_h2_header_seq_num(lc3_decode_buf[decode_buf_ro + rp + 1])) {
    size_t data_len = decode_buf_data_len();
    while (rp < BTM_LC3_PKT_LEN && data_len - rp >= BTM_LC3_PKT_LEN) {
      if ((lc3_decode_buf[(decode_buf_ro + rp) % buf_size] !=
           BTM_LC3_H2_HEADER_0) ||
          !verify_h2_header_seq_num(
              lc3_decode_buf[(decode_buf_ro + rp + 1) % buf_size])) {
        rp++;
        continue;
      }
@@ -990,11 +1059,22 @@ struct tBTM_LC3_INFO {
      if (rp != 0) {
        log::warn("Skipped {} bytes of LC3 data ahead of a valid LC3 frame",
                  (unsigned long)rp);
        decode_buf_ro += rp;
        incr_buf_offset(decode_buf_ro, decode_buf_ro_mirror, buf_size, rp);
      }

      // Get the frame head.
      if (buf_size - decode_buf_ro >= BTM_LC3_PKT_LEN) {
        return &lc3_decode_buf[decode_buf_ro];
      }

      std::copy(lc3_decode_buf + decode_buf_ro, lc3_decode_buf + buf_size,
                packet_buf);
      std::copy(lc3_decode_buf,
                lc3_decode_buf + BTM_LC3_PKT_LEN - (buf_size - decode_buf_ro),
                packet_buf + (buf_size - decode_buf_ro));
      return packet_buf;
    }

    return nullptr;
  }

@@ -1099,7 +1179,7 @@ size_t decode(const uint8_t** out_data) {
    return 0;
  }

  if (lc3_info->decodable() < BTM_LC3_PKT_LEN) {
  if (lc3_info->decode_buf_data_len() < BTM_LC3_PKT_LEN) {
    return 0;
  }

+126 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>

#include <algorithm>
#include <map>
#include <memory>

#include "btif/include/core_callbacks.h"
@@ -59,6 +60,16 @@ const std::vector<uint8_t> lc3_zero_packet{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x38, 0x24, 0xf9, 0x4a, 0x0d, 0x00, 0x00, 0x03};

// Maps irregular packet size to expected decode buffer size.
// See |btm_wbs_supported_pkt_size| and |btm_wbs_msbc_buffer_size|.
const std::map<size_t, size_t> irregular_packet_to_buffer_size{
    {72, 360},
    {24, 120},
};

// The encoded packet size is 60 regardless of the codec.
const int ENCODED_PACKET_SIZE = 60;

struct MsbcCodecInterface : bluetooth::core::CodecInterface {
  MsbcCodecInterface() : bluetooth::core::CodecInterface(){};

@@ -354,6 +365,121 @@ TEST_F(ScoHciSwbWithInitCleanTest, SwbDecode) {
  ASSERT_EQ(decoded, nullptr);
}

TEST_F(ScoHciWbsTest, WbsDecodeWithIrregularOffset) {
  for (auto [pkt_size, buf_size] : irregular_packet_to_buffer_size) {
    ASSERT_EQ(buf_size % pkt_size, 0u);

    bluetooth::audio::sco::wbs::init(pkt_size);

    const uint8_t* decoded = nullptr;

    // No data to decode
    ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
    ASSERT_EQ(decoded, nullptr);

    // Start the payload with an irregular offset that misaligns with the
    // packet size.
    std::vector<uint8_t> payload = std::vector<uint8_t>(1, 0);
    while (payload.size() <= pkt_size) {
      payload.insert(payload.end(), msbc_zero_packet.begin(),
                     msbc_zero_packet.end());
    }
    size_t packet_offset =
        msbc_zero_packet.size() - (payload.size() - pkt_size);
    payload.resize(pkt_size);

    // Try to decode as many packets as to hit the boundary.
    for (size_t iter = 0, decodable = 0; iter < 2 * buf_size / pkt_size;
         ++iter) {
      ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false),
                true);
      decodable += payload.size() - !iter;  // compensate for the first offset

      while (decodable >= ENCODED_PACKET_SIZE) {
        decoded = nullptr;
        ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded),
                  size_t(BTM_MSBC_CODE_SIZE));
        ASSERT_NE(decoded, nullptr);
        for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
          ASSERT_EQ(decoded[i], 0);
        }
        decodable -= ENCODED_PACKET_SIZE;
      }

      payload = std::vector<uint8_t>(msbc_zero_packet.begin() + packet_offset,
                                     msbc_zero_packet.end());
      while (payload.size() < pkt_size) {
        payload.insert(payload.end(), msbc_zero_packet.begin(),
                       msbc_zero_packet.end());
      }
      packet_offset += msbc_zero_packet.size() - packet_offset;
      packet_offset += msbc_zero_packet.size() -
                       (payload.size() - pkt_size) % msbc_zero_packet.size();
      packet_offset %= msbc_zero_packet.size();
      payload.resize(pkt_size);
    }

    bluetooth::audio::sco::wbs::cleanup();
  }
}

TEST_F(ScoHciSwbTest, SwbDecodeWithIrregularOffset) {
  for (auto [pkt_size, buf_size] : irregular_packet_to_buffer_size) {
    ASSERT_EQ(buf_size % pkt_size, 0u);

    bluetooth::audio::sco::swb::init(pkt_size);

    const uint8_t* decoded = nullptr;

    // No data to decode
    ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(0));
    ASSERT_EQ(decoded, nullptr);

    // Start the payload with an irregular offset that misaligns with the
    // packet size.
    std::vector<uint8_t> payload = std::vector<uint8_t>(1, 0);
    while (payload.size() <= pkt_size) {
      payload.insert(payload.end(), lc3_zero_packet.begin(),
                     lc3_zero_packet.end());
    }
    size_t packet_offset = lc3_zero_packet.size() - (payload.size() - pkt_size);
    payload.resize(pkt_size);

    // Try to decode as many packets as to hit the boundary.
    for (size_t iter = 0, decodable = 0; iter < 2 * buf_size / pkt_size;
         ++iter) {
      ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false),
                true);
      decodable += payload.size() - !iter;  // compensate for the first offset

      while (decodable >= ENCODED_PACKET_SIZE) {
        decoded = nullptr;
        ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded),
                  size_t(BTM_LC3_CODE_SIZE));
        ASSERT_NE(decoded, nullptr);
        for (size_t i = 0; i < BTM_LC3_CODE_SIZE; i++) {
          ASSERT_EQ(decoded[i], 0);
        }
        decodable -= ENCODED_PACKET_SIZE;
      }

      payload = std::vector<uint8_t>(lc3_zero_packet.begin() + packet_offset,
                                     lc3_zero_packet.end());
      while (payload.size() < pkt_size) {
        payload.insert(payload.end(), lc3_zero_packet.begin(),
                       lc3_zero_packet.end());
      }
      packet_offset += lc3_zero_packet.size() - packet_offset;
      packet_offset += lc3_zero_packet.size() -
                       (payload.size() - pkt_size) % lc3_zero_packet.size();
      packet_offset %= lc3_zero_packet.size();
      payload.resize(pkt_size);
    }

    bluetooth::audio::sco::swb::cleanup();
  }
}

TEST_F(ScoHciWbsTest, WbsEncodeWithoutInit) {
  int16_t data[120] = {0};
  // Return 0 if buffer is uninitialized