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

Commit e117a613 authored by Thomas Girardier's avatar Thomas Girardier Committed by Android (Google) Code Review
Browse files

Merge changes from topic "codec-tests" into tm-qpr-dev

* changes:
  Improve AAC unit test coverage
  OPUS codec (wrapper) unit tests.
  Fix bugs in OPUS codec wrappers.
  SBC codec (wrapper) unit tests.
  [Pandora] - LDAC codec unit tests
  AAC codec (wrapper) unit tests.
parents 2acc5bef b08e998b
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
@@ -612,6 +612,108 @@ cc_test {
    },
}

cc_test {
    name: "net_test_stack_a2dp_codecs_native",
    defaults: [
        "fluoride_defaults",
        "mts_defaults",
    ],
    test_suites: ["device-tests"],
    host_supported: true,
    test_options: {
        unit_test: true,
    },
    include_dirs: [
        "external/aac/libAACenc/include",
        "external/aac/libAACdec/include",
        "external/aac/libSYS/include",
        "external/libldac/inc",
        "external/libldac/abr/inc",
        "external/libopus/include",
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/btif/include",
        "packages/modules/Bluetooth/system/embdrv/encoder_for_aptxhd/include",
        "packages/modules/Bluetooth/system/gd",
        "packages/modules/Bluetooth/system/stack/include",
        "packages/modules/Bluetooth/system/utils/include",
    ],
    target: {
        host: {
            srcs: [
                ":BluetoothHostTestingLogCapture",
            ],
        },
        android: {
            srcs: [
                ":BluetoothAndroidTestingLogCapture",
            ],
            test_config: "test/a2dp/AndroidTest.xml",
        }
    },
    data: [
        "test/a2dp/raw_data/*",
    ],
    srcs: [
        "a2dp/a2dp_aac.cc",
        "a2dp/a2dp_aac_decoder.cc",
        "a2dp/a2dp_aac_encoder.cc",
        "a2dp/a2dp_codec_config.cc",
        "a2dp/a2dp_sbc.cc",
        "a2dp/a2dp_sbc_decoder.cc",
        "a2dp/a2dp_sbc_encoder.cc",
        "a2dp/a2dp_sbc_up_sample.cc",
        "a2dp/a2dp_vendor.cc",
        "a2dp/a2dp_vendor_aptx.cc",
        "a2dp/a2dp_vendor_aptx_hd.cc",
        "a2dp/a2dp_vendor_aptx_encoder.cc",
        "a2dp/a2dp_vendor_aptx_hd_encoder.cc",
        "a2dp/a2dp_vendor_ldac.cc",
        "a2dp/a2dp_vendor_ldac_decoder.cc",
        "a2dp/a2dp_vendor_ldac_encoder.cc",
        "a2dp/a2dp_vendor_opus.cc",
        "a2dp/a2dp_vendor_opus_encoder.cc",
        "a2dp/a2dp_vendor_opus_decoder.cc",
        "test/a2dp/a2dp_aac_unittest.cc",
        "test/a2dp/a2dp_sbc_unittest.cc",
        "test/a2dp/a2dp_opus_unittest.cc",
        "test/a2dp/a2dp_vendor_ldac_unittest.cc",
        "test/a2dp/mock_bta_av_codec.cc",
        "test/a2dp/test_util.cc",
        "test/a2dp/wav_reader.cc",
        "test/a2dp/wav_reader_unittest.cc",
        ":TestMockBta",
        ":TestMockStackA2dpApi",
    ],
    shared_libs: [
        "libcrypto",
        "libcutils",
        "libprotobuf-cpp-lite",
    ],
    static_libs: [
        "libbt-common",
        "libbt-protos-lite",
        "libbt-sbc-decoder",
        "libbt-sbc-encoder",
        "libFraunhoferAAC",
        "libgmock",
        "liblog",
        "libopus",
        "libosi",
        "libosi-AllocationTestHarness",
    ],
    whole_static_libs: [
        "libaptx_enc",
        "libaptxhd_enc",
        "libldacBT_abr",
        "libldacBT_enc",
    ],
    sanitize: {
        address: true,
        cfi: true,
        misc_undefined: ["bounds"],
    },
}

cc_test {
    name: "net_test_stack_a2dp_native",
    defaults: [
+5 −0
Original line number Diff line number Diff line
@@ -100,6 +100,11 @@ bool a2dp_vendor_opus_decoder_decode_packet(BT_HDR* p_buf) {
    return false;
  }

  if (p_buf->len == 0) {
    LOG_ERROR("Empty packet");
    return false;
  }

  auto* pBuffer =
      reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset + 1);
  int32_t bufferSize = p_buf->len - 1;
+2 −1
Original line number Diff line number Diff line
@@ -231,7 +231,6 @@ static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu,
  LOG_INFO("sample_rate=%u bits_per_sample=%u channel_count=%u",
           p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
           p_feeding_params->channel_count);
  a2dp_vendor_opus_feeding_reset();

  // The codec parameters
  p_encoder_params->sample_rate =
@@ -241,6 +240,8 @@ static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu,
  p_encoder_params->framesize = A2DP_VendorGetFrameSizeOpus(p_codec_info);
  p_encoder_params->bitrate = A2DP_VendorGetBitRateOpus(p_codec_info);

  a2dp_vendor_opus_feeding_reset();

  uint16_t mtu_size =
      BT_DEFAULT_BUFFER_SIZE - A2DP_OPUS_OFFSET - sizeof(BT_HDR);
  if (mtu_size < peer_mtu) {
+39 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 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.
-->
<configuration description="Runs net_test_stack_a2dp_codecs_native.">
  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
  <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
        <option name="cleanup" value="true" />
        <option name="push" value="net_test_stack_a2dp_codecs_native->/data/local/tmp/net_test_stack_a2dp_codecs_native" />
        <option name="append-bitness" value="true" />
  </target_preparer>
  <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
        <option name="cleanup" value="true" />
        <option name="push" value="pcm0844s.wav->/data/local/tmp/test/a2dp/raw_data/pcm0844s.wav" />
        <option name="push" value="pcm1644s.wav->/data/local/tmp/test/a2dp/raw_data/pcm1644s.wav" />
  </target_preparer>
  <test class="com.android.tradefed.testtype.GTest" >
    <option name="native-test-device-path" value="/data/local/tmp" />
    <option name="module-name" value="net_test_stack_a2dp_codecs_native" />
    <option name="run-test-as" value="0" />
  </test>

  <!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
  <object type="module_controller"
          class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
      <option name="mainline-module-package-name" value="com.google.android.bluetooth" />
  </object>
</configuration>
+307 −0
Original line number Diff line number Diff line
/*
 * Copyright 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 "stack/include/a2dp_aac.h"

#include <base/logging.h>
#include <gtest/gtest.h>
#include <stdio.h>

#include <cstdint>
#include <fstream>
#include <future>
#include <iomanip>
#include <map>
#include <string>

#include "common/init_flags.h"
#include "common/testing/log_capture.h"
#include "common/time_util.h"
#include "os/log.h"
#include "osi/include/allocator.h"
#include "osi/test/AllocationTestHarness.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/a2dp_aac_decoder.h"
#include "stack/include/a2dp_aac_encoder.h"
#include "stack/include/avdt_api.h"
#include "test_util.h"
#include "wav_reader.h"

extern void allocation_tracker_uninit(void);
namespace {
constexpr uint32_t kAacReadSize = 1024 * 2 * 2;
constexpr uint32_t kA2dpTickUs = 23 * 1000;
constexpr char kDecodedDataCallbackIsInvoked[] =
    "A2DP decoded data callback is invoked.";
constexpr char kEnqueueCallbackIsInvoked[] =
    "A2DP source enqueue callback is invoked.";
constexpr uint16_t kPeerMtu = 1000;
constexpr char kWavFile[] = "test/a2dp/raw_data/pcm1644s.wav";
constexpr uint8_t kCodecInfoAacCapability[AVDT_CODEC_SIZE] = {
    8,           // Length (A2DP_AAC_INFO_LEN)
    0,           // Media Type: AVDT_MEDIA_TYPE_AUDIO
    2,           // Media Codec Type: A2DP_MEDIA_CT_AAC
    0x80,        // Object Type: A2DP_AAC_OBJECT_TYPE_MPEG2_LC
    0x01,        // Sampling Frequency: A2DP_AAC_SAMPLING_FREQ_44100
    0x04,        // Channels: A2DP_AAC_CHANNEL_MODE_STEREO
    0x00 | 0x4,  // Variable Bit Rate:
                 // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED
                 // Bit Rate: 320000 = 0x4e200
    0xe2,        // Bit Rate: 320000 = 0x4e200
    0x00,        // Bit Rate: 320000 = 0x4e200
    7,           // Unused
    8,           // Unused
    9            // Unused
};
uint8_t* Data(BT_HDR* packet) { return packet->data + packet->offset; }
}  // namespace

namespace bluetooth {
namespace testing {

static BT_HDR* packet = nullptr;
static WavReader wav_reader = WavReader(GetWavFilePath(kWavFile).c_str());

class A2dpAacTest : public AllocationTestHarness {
 protected:
  void SetUp() override {
    AllocationTestHarness::SetUp();
    common::InitFlags::SetAllForTesting();
    // Disable our allocation tracker to allow ASAN full range
    allocation_tracker_uninit();
    SetCodecConfig();
    encoder_iface_ = const_cast<tA2DP_ENCODER_INTERFACE*>(
        A2DP_GetEncoderInterfaceAac(kCodecInfoAacCapability));
    ASSERT_NE(encoder_iface_, nullptr);
    decoder_iface_ = const_cast<tA2DP_DECODER_INTERFACE*>(
        A2DP_GetDecoderInterfaceAac(kCodecInfoAacCapability));
    ASSERT_NE(decoder_iface_, nullptr);
  }

  void TearDown() override {
    if (a2dp_codecs_ != nullptr) {
      delete a2dp_codecs_;
    }
    if (encoder_iface_ != nullptr) {
      encoder_iface_->encoder_cleanup();
    }
    A2DP_UnloadEncoderAac();
    if (decoder_iface_ != nullptr) {
      decoder_iface_->decoder_cleanup();
    }
    A2DP_UnloadDecoderAac();
    AllocationTestHarness::TearDown();
  }

  void SetCodecConfig() {
    uint8_t codec_info_result[AVDT_CODEC_SIZE];
    btav_a2dp_codec_index_t peer_codec_index;
    a2dp_codecs_ = new A2dpCodecs(std::vector<btav_a2dp_codec_config_t>());

    ASSERT_TRUE(a2dp_codecs_->init());

    // Create the codec capability - AAC Sink
    memset(codec_info_result, 0, sizeof(codec_info_result));
    ASSERT_TRUE(A2DP_IsSinkCodecSupportedAac(kCodecInfoAacCapability));
    peer_codec_index = A2DP_SinkCodecIndex(kCodecInfoAacCapability);
    ASSERT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
    sink_codec_config_ = a2dp_codecs_->findSinkCodecConfig(kCodecInfoAacCapability);
    ASSERT_NE(sink_codec_config_, nullptr);
    ASSERT_TRUE(a2dp_codecs_->setSinkCodecConfig(kCodecInfoAacCapability, true,
                                                 codec_info_result, true));
    ASSERT_TRUE(a2dp_codecs_->setPeerSinkCodecCapabilities(kCodecInfoAacCapability));
    // Compare the result codec with the local test codec info
    for (size_t i = 0; i < kCodecInfoAacCapability[0] + 1; i++) {
      ASSERT_EQ(codec_info_result[i], kCodecInfoAacCapability[i]);
    }
    ASSERT_TRUE(a2dp_codecs_->setCodecConfig(kCodecInfoAacCapability, true, codec_info_result, true));
    source_codec_config_ = a2dp_codecs_->getCurrentCodecConfig();
  }

  void InitializeEncoder(bool peer_supports_3mbps, a2dp_source_read_callback_t read_cb,
                         a2dp_source_enqueue_callback_t enqueue_cb) {
    tA2DP_ENCODER_INIT_PEER_PARAMS peer_params = {true, peer_supports_3mbps, kPeerMtu};
    encoder_iface_->encoder_init(&peer_params, sink_codec_config_, read_cb,
                                 enqueue_cb);
  }

  void InitializeDecoder(decoded_data_callback_t data_cb) {
    decoder_iface_->decoder_init(data_cb);
  }

  BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const {
    auto packet = AllocatePacket(data.size());
    std::copy(data.cbegin(), data.cend(), Data(packet));
    return packet;
  }

  BT_HDR* AllocatePacket(size_t packet_length) const {
    BT_HDR* packet =
        static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length));
    packet->len = packet_length;
    return packet;
  }
  A2dpCodecConfig* sink_codec_config_;
  A2dpCodecConfig* source_codec_config_;
  A2dpCodecs* a2dp_codecs_;
  tA2DP_ENCODER_INTERFACE* encoder_iface_;
  tA2DP_DECODER_INTERFACE* decoder_iface_;
  std::unique_ptr<LogCapture> log_capture_;
};

TEST_F(A2dpAacTest, a2dp_source_read_underflow) {
  log_capture_ = std::make_unique<LogCapture>();
  auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t {
    // underflow
    return 0;
  };
  auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool {
    return false;
  };
  InitializeEncoder(true, read_cb, enqueue_cb);
  uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);
  usleep(kA2dpTickUs);
  timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);
  std::promise<void> promise;
  log_capture_->WaitUntilLogContains(&promise,
                                     "a2dp_aac_encode_frames: underflow");
}

TEST_F(A2dpAacTest, a2dp_enqueue_cb_is_invoked) {
  log_capture_ = std::make_unique<LogCapture>();
  auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t {
    ASSERT(kAacReadSize == len);
    return len;
  };
  auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool {
    LOG_DEBUG("%s", kEnqueueCallbackIsInvoked);
    osi_free(p_buf);
    return false;
  };
  InitializeEncoder(true, read_cb, enqueue_cb);
  uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);
  usleep(kA2dpTickUs);
  timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);
  std::promise<void> promise;
  log_capture_->WaitUntilLogContains(&promise, kEnqueueCallbackIsInvoked);
}

TEST_F(A2dpAacTest, decoded_data_cb_not_invoked_when_empty_packet) {
  auto data_cb = +[](uint8_t* p_buf, uint32_t len) { FAIL(); };
  InitializeDecoder(data_cb);
  std::vector<uint8_t> data;
  BT_HDR* packet = AllocateL2capPacket(data);
  decoder_iface_->decode_packet(packet);
  osi_free(packet);
}

TEST_F(A2dpAacTest, decoded_data_cb_invoked) {
  log_capture_ = std::make_unique<LogCapture>();
  auto data_cb = +[](uint8_t* p_buf, uint32_t len) {
    LOG_DEBUG("%s", kDecodedDataCallbackIsInvoked);
  };
  InitializeDecoder(data_cb);

  auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t {
    static uint32_t counter = 0;
    memcpy(p_buf, wav_reader.GetSamples() + counter, len);
    counter += len;
    return len;
  };
  auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool {
    packet = p_buf;
    LOG_DEBUG("%s", kEnqueueCallbackIsInvoked);
    return false;
  };
  InitializeEncoder(true, read_cb, enqueue_cb);

  uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);
  usleep(kA2dpTickUs);
  timestamp_us = bluetooth::common::time_gettimeofday_us();
  encoder_iface_->send_frames(timestamp_us);

  std::promise<void> promise;
  log_capture_->WaitUntilLogContains(&promise, kEnqueueCallbackIsInvoked);
  decoder_iface_->decode_packet(packet);
  osi_free(packet);
  ASSERT_TRUE(log_capture_->Find(kDecodedDataCallbackIsInvoked));
}

TEST_F(A2dpAacTest, set_source_codec_config_works) {
  uint8_t codec_info_result[AVDT_CODEC_SIZE];
  ASSERT_TRUE(a2dp_codecs_->setCodecConfig(kCodecInfoAacCapability, true, codec_info_result, true));
  ASSERT_TRUE(A2DP_CodecTypeEqualsAac(codec_info_result, kCodecInfoAacCapability));
  ASSERT_TRUE(A2DP_CodecEqualsAac(codec_info_result, kCodecInfoAacCapability));
  auto* codec_config = a2dp_codecs_->findSourceCodecConfig(kCodecInfoAacCapability);
  ASSERT_EQ(codec_config->name(), source_codec_config_->name());
  ASSERT_EQ(codec_config->getAudioBitsPerSample(), source_codec_config_->getAudioBitsPerSample());
}

TEST_F(A2dpAacTest, sink_supports_aac) {
  ASSERT_TRUE(A2DP_IsSinkCodecSupportedAac(kCodecInfoAacCapability));
}

TEST_F(A2dpAacTest, effective_mtu_when_peer_supports_3mbps) {
  auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t {
    ASSERT(kAacReadSize == len);
    return len;
  };
  auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool {
    osi_free(p_buf);
    return false;
  };
  InitializeEncoder(true, read_cb, enqueue_cb);
  ASSERT_EQ(a2dp_aac_get_effective_frame_size(), kPeerMtu);
}

TEST_F(A2dpAacTest, effective_mtu_when_peer_does_not_support_3mbps) {
  auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t {
    ASSERT(kAacReadSize == len);
    return len;
  };
  auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool {
    osi_free(p_buf);
    return false;
  };
  InitializeEncoder(false, read_cb, enqueue_cb);
  ASSERT_EQ(a2dp_aac_get_effective_frame_size(), 663 /* MAX_2MBPS_AVDTP_MTU */);
}

TEST_F(A2dpAacTest, debug_codec_dump) {
  log_capture_ = std::make_unique<LogCapture>();
  a2dp_codecs_->debug_codec_dump(2);
  std::promise<void> promise;
  log_capture_->WaitUntilLogContains(&promise,
                                     "Current Codec: AAC");
}

TEST_F(A2dpAacTest, codec_info_string) {
  auto codec_info = A2DP_CodecInfoString(kCodecInfoAacCapability);
  ASSERT_NE(codec_info.find("samp_freq: 44100"), std::string::npos);
  ASSERT_NE(codec_info.find("ch_mode: Stereo"), std::string::npos);
}

TEST_F(A2dpAacTest, get_track_bits_per_sample) {
  ASSERT_EQ(A2DP_GetTrackBitsPerSampleAac(kCodecInfoAacCapability), 16);
}

}  // namespace testing
}  // namespace bluetooth
Loading