diff --git a/system/audio_hal_interface/fuzzer/Android.bp b/system/audio_hal_interface/fuzzer/Android.bp index ac74f1382fcfe97c4745ac1835830771f5758781..844fdd35846e0367ee1ad20c2d68704d2b29bb47 100644 --- a/system/audio_hal_interface/fuzzer/Android.bp +++ b/system/audio_hal_interface/fuzzer/Android.bp @@ -72,6 +72,7 @@ cc_defaults { "libudrv-uipc", "libbt-common", "liblc3", + "libopus", "libstatslog_bt", "libvndksupport", "libprocessgroup", diff --git a/system/btif/Android.bp b/system/btif/Android.bp index 079d12cc04eefa8da2d6f3272132ef0f28ef156a..0a43f0601773b99a52964053a5665a5ff6113dca 100644 --- a/system/btif/Android.bp +++ b/system/btif/Android.bp @@ -238,6 +238,7 @@ cc_test { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libosi", "libudrv-uipc", ], diff --git a/system/build/Android.bp b/system/build/Android.bp index 9a0c996a7d17d1d1a2b5d62fb0c74a26384fa724..79f7d65632e4d4911194ffd63d9e92f0e23dd76e 100644 --- a/system/build/Android.bp +++ b/system/build/Android.bp @@ -207,6 +207,7 @@ fluoride_defaults { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libprotobuf-cpp-lite", "libstatslog_bt", "libudrv-uipc", diff --git a/system/gd/rust/topshim/facade/Android.bp b/system/gd/rust/topshim/facade/Android.bp index 5d2b06b2ca7e39daaffe35228b7d5ec05cdf66b9..058b781b3bc4324f6f34b82e8325cb6ea9f7062e 100644 --- a/system/gd/rust/topshim/facade/Android.bp +++ b/system/gd/rust/topshim/facade/Android.bp @@ -55,6 +55,7 @@ rust_binary_host { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libudrv-uipc", "libbluetooth_gd", // Gabeldorsche "libbluetooth-dumpsys", diff --git a/system/service/Android.bp b/system/service/Android.bp index 91dcff058dd7367dbd8c3dfac63b32b3febdbd4b..560ab2b83305ce66edd52cd952ce649a7469982c 100644 --- a/system/service/Android.bp +++ b/system/service/Android.bp @@ -119,6 +119,7 @@ cc_binary { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libosi", "libudrv-uipc", ], @@ -202,6 +203,7 @@ cc_test { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libosi", "libudrv-uipc", ], diff --git a/system/stack/Android.bp b/system/stack/Android.bp index 8d1b8e544725e336b584d5f7e800c54d0da2e530..94e0f4ea80f0064a001eb7f8783fa4d404b7df87 100644 --- a/system/stack/Android.bp +++ b/system/stack/Android.bp @@ -55,6 +55,7 @@ cc_library_static { "external/aac/libSYS/include", "external/libldac/inc", "external/libldac/abr/inc", + "external/libopus/include", "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/vnd/include", @@ -85,6 +86,9 @@ cc_library_static { "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", "avct/avct_api.cc", "avct/avct_bcb_act.cc", "avct/avct_ccb.cc", @@ -268,6 +272,7 @@ cc_test { "libbtdevice", "libg722codec", "liblc3", + "libopus", "libosi", "libudrv-uipc", "libbt-protos-lite", diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc index 52e0f6fcb1969d42d1de0113a593bb68585ec906..b7b970fbe481eb4345b975401f32216aa51a6272 100644 --- a/system/stack/a2dp/a2dp_codec_config.cc +++ b/system/stack/a2dp/a2dp_codec_config.cc @@ -33,6 +33,7 @@ #include "a2dp_vendor_aptx.h" #include "a2dp_vendor_aptx_hd.h" #include "a2dp_vendor_ldac.h" +#include "a2dp_vendor_opus.h" #endif #include "bta/av/bta_av_int.h" @@ -111,7 +112,7 @@ void A2dpCodecConfig::setDefaultCodecPriority() { A2dpCodecConfig* A2dpCodecConfig::createCodec( btav_a2dp_codec_index_t codec_index, btav_a2dp_codec_priority_t codec_priority) { - LOG_INFO("%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index)); + LOG_INFO("%s", A2DP_CodecIndexStr(codec_index)); A2dpCodecConfig* codec_config = nullptr; switch (codec_index) { @@ -140,6 +141,12 @@ A2dpCodecConfig* A2dpCodecConfig::createCodec( case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: codec_config = new A2dpCodecConfigLdacSink(codec_priority); break; + case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: + codec_config = new A2dpCodecConfigOpusSource(codec_priority); + break; + case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: + codec_config = new A2dpCodecConfigOpusSink(codec_priority); + break; #endif case BTAV_A2DP_CODEC_INDEX_MAX: default: @@ -586,6 +593,9 @@ bool A2dpCodecs::init() { } else if (strcmp(tok, "ldac") == 0) { LOG_INFO("%s: LDAC offload supported", __func__); offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC] = true; + } else if (strcmp(tok, "opus") == 0) { + LOG_INFO("%s: Opus offload supported", __func__); + offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS] = true; #endif } tok = strtok_r(NULL, "-", &tmp_token); diff --git a/system/stack/a2dp/a2dp_vendor.cc b/system/stack/a2dp/a2dp_vendor.cc index 30dc2069f89c2bab501eb4af8278530419d615dd..cde70c1b59875df1abc75f642a036cd498eb6f8d 100644 --- a/system/stack/a2dp/a2dp_vendor.cc +++ b/system/stack/a2dp/a2dp_vendor.cc @@ -25,6 +25,7 @@ #include "a2dp_vendor_aptx.h" #include "a2dp_vendor_aptx_hd.h" #include "a2dp_vendor_ldac.h" +#include "a2dp_vendor_opus.h" #include "bt_target.h" #include "osi/include/log.h" #include "osi/include/osi.h" @@ -51,6 +52,11 @@ bool A2DP_IsVendorSourceCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorSourceCodecValidLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsVendorSourceCodecValidOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -68,6 +74,11 @@ bool A2DP_IsVendorSinkCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorSinkCodecValidLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsVendorSinkCodecValidOpus(p_codec_info); + } + return false; } @@ -83,6 +94,11 @@ bool A2DP_IsVendorPeerSourceCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorPeerSourceCodecValidLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsVendorPeerSourceCodecValidOpus(p_codec_info); + } + return false; } @@ -107,6 +123,11 @@ bool A2DP_IsVendorPeerSinkCodecValid(const uint8_t* p_codec_info) { return A2DP_IsVendorPeerSinkCodecValidLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsVendorPeerSinkCodecValidOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -124,6 +145,11 @@ bool A2DP_IsVendorSinkCodecSupported(const uint8_t* p_codec_info) { return A2DP_IsVendorSinkCodecSupportedLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsVendorSinkCodecSupportedOpus(p_codec_info); + } + return false; } @@ -139,6 +165,11 @@ bool A2DP_IsVendorPeerSourceCodecSupported(const uint8_t* p_codec_info) { return A2DP_IsPeerSourceCodecSupportedLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_IsPeerSourceCodecSupportedOpus(p_codec_info); + } + return false; } @@ -185,6 +216,12 @@ bool A2DP_VendorUsesRtpHeader(bool content_protection_enabled, p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorUsesRtpHeaderOpus(content_protection_enabled, + p_codec_info); + } + // Add checks based on <content_protection_enabled, vendor_id, codec_id> return true; @@ -211,6 +248,11 @@ const char* A2DP_VendorCodecName(const uint8_t* p_codec_info) { return A2DP_VendorCodecNameLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorCodecNameOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return "UNKNOWN VENDOR CODEC"; @@ -250,6 +292,11 @@ bool A2DP_VendorCodecTypeEquals(const uint8_t* p_codec_info_a, return A2DP_VendorCodecTypeEqualsLdac(p_codec_info_a, p_codec_info_b); } + // Check for Opus + if (vendor_id_a == A2DP_OPUS_VENDOR_ID && codec_id_a == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorCodecTypeEqualsOpus(p_codec_info_a, p_codec_info_b); + } + // OPTIONAL: Add extra vendor-specific checks based on the // vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b". @@ -290,6 +337,11 @@ bool A2DP_VendorCodecEquals(const uint8_t* p_codec_info_a, return A2DP_VendorCodecEqualsLdac(p_codec_info_a, p_codec_info_b); } + // Check for Opus + if (vendor_id_a == A2DP_OPUS_VENDOR_ID && codec_id_a == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorCodecEqualsOpus(p_codec_info_a, p_codec_info_b); + } + // Add extra vendor-specific checks based on the // vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b". @@ -317,6 +369,11 @@ int A2DP_VendorGetBitRate(const uint8_t* p_codec_info) { return A2DP_VendorGetBitRateLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetBitRateOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -343,6 +400,11 @@ int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackSampleRateLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetTrackSampleRateOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -369,6 +431,11 @@ int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetTrackBitsPerSampleOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -395,6 +462,11 @@ int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info) { return A2DP_VendorGetTrackChannelCountLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetTrackChannelCountOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return -1; @@ -412,6 +484,11 @@ int A2DP_VendorGetSinkTrackChannelType(const uint8_t* p_codec_info) { return A2DP_VendorGetSinkTrackChannelTypeLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetSinkTrackChannelTypeOpus(p_codec_info); + } + return -1; } @@ -439,6 +516,11 @@ bool A2DP_VendorGetPacketTimestamp(const uint8_t* p_codec_info, return A2DP_VendorGetPacketTimestampLdac(p_codec_info, p_data, p_timestamp); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetPacketTimestampOpus(p_codec_info, p_data, p_timestamp); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -469,6 +551,12 @@ bool A2DP_VendorBuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf, frames_per_packet); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorBuildCodecHeaderOpus(p_codec_info, p_buf, + frames_per_packet); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -496,6 +584,11 @@ const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterface( return A2DP_VendorGetEncoderInterfaceLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetEncoderInterfaceOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return NULL; @@ -514,6 +607,11 @@ const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterface( return A2DP_VendorGetDecoderInterfaceLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorGetDecoderInterfaceOpus(p_codec_info); + } + return NULL; } @@ -538,6 +636,11 @@ bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info) { return A2DP_VendorAdjustCodecLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorAdjustCodecOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return false; @@ -565,6 +668,11 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex( return A2DP_VendorSourceCodecIndexLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorSourceCodecIndexOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return BTAV_A2DP_CODEC_INDEX_MAX; @@ -582,6 +690,11 @@ btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndex(const uint8_t* p_codec_info) { return A2DP_VendorSinkCodecIndexLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorSinkCodecIndexOpus(p_codec_info); + } + return BTAV_A2DP_CODEC_INDEX_MAX; } @@ -604,11 +717,9 @@ const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: return "LC3 not implemented"; case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: - // TODO(b/226441860): in-progress - return "Opus"; + return A2DP_VendorCodecIndexStrOpus(); case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: - // TODO(b/226441860): in-progress - return "Opus SINK"; + return A2DP_VendorCodecIndexStrOpusSink(); // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; @@ -637,9 +748,9 @@ bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index, case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: break; // not implemented case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: - // TODO(b/226441860): in-progress + return A2DP_VendorInitCodecConfigOpus(p_cfg); case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: - // TODO(b/226441860): in-progress + return A2DP_VendorInitCodecConfigOpusSink(p_cfg); // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; @@ -669,6 +780,11 @@ std::string A2DP_VendorCodecInfoString(const uint8_t* p_codec_info) { return A2DP_VendorCodecInfoStringLdac(p_codec_info); } + // Check for Opus + if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { + return A2DP_VendorCodecInfoStringOpus(p_codec_info); + } + // Add checks based on <vendor_id, codec_id> return "Unsupported codec vendor_id: " + loghex(vendor_id) + diff --git a/system/stack/a2dp/a2dp_vendor_opus.cc b/system/stack/a2dp/a2dp_vendor_opus.cc new file mode 100644 index 0000000000000000000000000000000000000000..8390035b0a4642b1df74206180b9c29017a26913 --- /dev/null +++ b/system/stack/a2dp/a2dp_vendor_opus.cc @@ -0,0 +1,1332 @@ +/* + * Copyright 2021 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. + */ + +/****************************************************************************** + * + * Utility functions to help build and parse the Opus Codec Information + * Element and Media Payload. + * + ******************************************************************************/ + +#define LOG_TAG "a2dp_vendor_opus" + +#include "a2dp_vendor_opus.h" + +#include <base/logging.h> +#include <string.h> + +#include "a2dp_vendor.h" +#include "a2dp_vendor_opus_decoder.h" +#include "a2dp_vendor_opus_encoder.h" +#include "bt_target.h" +#include "bt_utils.h" +#include "btif_av_co.h" +#include "osi/include/log.h" +#include "osi/include/osi.h" + +// data type for the Opus Codec Information Element */ +// NOTE: bits_per_sample and frameSize for Opus encoder initialization. +typedef struct { + uint32_t vendorId; + uint16_t codecId; /* Codec ID for Opus */ + uint8_t sampleRate; /* Sampling Frequency */ + uint8_t channelMode; /* STEREO/DUAL/MONO */ + btav_a2dp_codec_bits_per_sample_t bits_per_sample; + uint8_t future1; /* codec_specific_1 framesize */ + uint8_t future2; /* codec_specific_2 */ + uint8_t future3; /* codec_specific_3 */ + uint8_t future4; /* codec_specific_4 */ +} tA2DP_OPUS_CIE; + +/* Opus Source codec capabilities */ +static const tA2DP_OPUS_CIE a2dp_opus_source_caps = { + A2DP_OPUS_VENDOR_ID, // vendorId + A2DP_OPUS_CODEC_ID, // codecId + // sampleRate + (A2DP_OPUS_SAMPLING_FREQ_48000), + // channelMode + (A2DP_OPUS_CHANNEL_MODE_STEREO), + // bits_per_sample + (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16), + // future 1 frameSize + (A2DP_OPUS_20MS_FRAMESIZE), + // future 2 + 0x00, + // future 3 + 0x00, + // future 4 + 0x00}; + +/* Opus Sink codec capabilities */ +static const tA2DP_OPUS_CIE a2dp_opus_sink_caps = { + A2DP_OPUS_VENDOR_ID, // vendorId + A2DP_OPUS_CODEC_ID, // codecId + // sampleRate + (A2DP_OPUS_SAMPLING_FREQ_48000), + // channelMode + (A2DP_OPUS_CHANNEL_MODE_STEREO), + // bits_per_sample + (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16), + // future 1 frameSize + (A2DP_OPUS_20MS_FRAMESIZE), + // future 2 + 0x00, + // future 3 + 0x00, + // future 4 + 0x00}; + +/* Default Opus codec configuration */ +static const tA2DP_OPUS_CIE a2dp_opus_default_config = { + A2DP_OPUS_VENDOR_ID, // vendorId + A2DP_OPUS_CODEC_ID, // codecId + A2DP_OPUS_SAMPLING_FREQ_48000, // sampleRate + A2DP_OPUS_CHANNEL_MODE_STEREO, // channelMode + BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16, // bits_per_sample + A2DP_OPUS_20MS_FRAMESIZE, // frameSize + 0x00, // future 2 + 0x00, // future 3 + 0x00 // future 4 +}; + +static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_opus = { + a2dp_vendor_opus_encoder_init, + a2dp_vendor_opus_encoder_cleanup, + a2dp_vendor_opus_feeding_reset, + a2dp_vendor_opus_feeding_flush, + a2dp_vendor_opus_get_encoder_interval_ms, + a2dp_vendor_opus_get_effective_frame_size, + a2dp_vendor_opus_send_frames, + a2dp_vendor_opus_set_transmit_queue_length}; + +static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_opus = { + a2dp_vendor_opus_decoder_init, a2dp_vendor_opus_decoder_cleanup, + a2dp_vendor_opus_decoder_decode_packet, a2dp_vendor_opus_decoder_start, + a2dp_vendor_opus_decoder_suspend, a2dp_vendor_opus_decoder_configure, +}; + +UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityOpus( + const tA2DP_OPUS_CIE* p_cap, const uint8_t* p_codec_info, + bool is_peer_codec_info); + +// Builds the Opus Media Codec Capabilities byte sequence beginning from the +// LOSC octet. |media_type| is the media type |AVDT_MEDIA_TYPE_*|. +// |p_ie| is a pointer to the Opus Codec Information Element information. +// The result is stored in |p_result|. Returns A2DP_SUCCESS on success, +// otherwise the corresponding A2DP error status code. +static tA2DP_STATUS A2DP_BuildInfoOpus(uint8_t media_type, + const tA2DP_OPUS_CIE* p_ie, + uint8_t* p_result) { + if (p_ie == NULL || p_result == NULL) { + LOG_ERROR("invalid information element"); + return A2DP_INVALID_PARAMS; + } + + *p_result++ = A2DP_OPUS_CODEC_LEN; + *p_result++ = (media_type << 4); + *p_result++ = A2DP_MEDIA_CT_NON_A2DP; + + // Vendor ID and Codec ID + *p_result++ = (uint8_t)(p_ie->vendorId & 0x000000FF); + *p_result++ = (uint8_t)((p_ie->vendorId & 0x0000FF00) >> 8); + *p_result++ = (uint8_t)((p_ie->vendorId & 0x00FF0000) >> 16); + *p_result++ = (uint8_t)((p_ie->vendorId & 0xFF000000) >> 24); + *p_result++ = (uint8_t)(p_ie->codecId & 0x00FF); + *p_result++ = (uint8_t)((p_ie->codecId & 0xFF00) >> 8); + + *p_result = 0; + *p_result |= (uint8_t)(p_ie->channelMode) & A2DP_OPUS_CHANNEL_MODE_MASK; + if ((*p_result & A2DP_OPUS_CHANNEL_MODE_MASK) == 0) { + LOG_ERROR("channelmode 0x%X setting failed", (p_ie->channelMode)); + return A2DP_INVALID_PARAMS; + } + + *p_result |= ((uint8_t)(p_ie->future1) & A2DP_OPUS_FRAMESIZE_MASK); + if ((*p_result & A2DP_OPUS_FRAMESIZE_MASK) == 0) { + LOG_ERROR("frameSize 0x%X setting failed", (p_ie->future1)); + return A2DP_INVALID_PARAMS; + } + + *p_result |= ((uint8_t)(p_ie->sampleRate) & A2DP_OPUS_SAMPLING_FREQ_MASK); + if ((*p_result & A2DP_OPUS_SAMPLING_FREQ_MASK) == 0) { + LOG_ERROR("samplerate 0x%X setting failed", (p_ie->sampleRate)); + return A2DP_INVALID_PARAMS; + } + + p_result++; + + return A2DP_SUCCESS; +} + +// Parses the Opus Media Codec Capabilities byte sequence beginning from the +// LOSC octet. The result is stored in |p_ie|. The byte sequence to parse is +// |p_codec_info|. If |is_capability| is true, the byte sequence is +// codec capabilities, otherwise is codec configuration. +// Returns A2DP_SUCCESS on success, otherwise the corresponding A2DP error +// status code. +static tA2DP_STATUS A2DP_ParseInfoOpus(tA2DP_OPUS_CIE* p_ie, + const uint8_t* p_codec_info, + bool is_capability) { + uint8_t losc; + uint8_t media_type; + tA2DP_CODEC_TYPE codec_type; + + if (p_ie == NULL || p_codec_info == NULL) { + LOG_ERROR("unable to parse information element"); + return A2DP_INVALID_PARAMS; + } + + // Check the codec capability length + losc = *p_codec_info++; + if (losc != A2DP_OPUS_CODEC_LEN) { + LOG_ERROR("invalid codec ie length %d", losc); + return A2DP_WRONG_CODEC; + } + + media_type = (*p_codec_info++) >> 4; + codec_type = *p_codec_info++; + /* Check the Media Type and Media Codec Type */ + if (media_type != AVDT_MEDIA_TYPE_AUDIO || + codec_type != A2DP_MEDIA_CT_NON_A2DP) { + LOG_ERROR("invalid codec"); + return A2DP_WRONG_CODEC; + } + + // Check the Vendor ID and Codec ID */ + p_ie->vendorId = (*p_codec_info & 0x000000FF) | + (*(p_codec_info + 1) << 8 & 0x0000FF00) | + (*(p_codec_info + 2) << 16 & 0x00FF0000) | + (*(p_codec_info + 3) << 24 & 0xFF000000); + p_codec_info += 4; + p_ie->codecId = + (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00); + p_codec_info += 2; + if (p_ie->vendorId != A2DP_OPUS_VENDOR_ID || + p_ie->codecId != A2DP_OPUS_CODEC_ID) { + LOG_ERROR("wrong vendor or codec id"); + return A2DP_WRONG_CODEC; + } + + p_ie->channelMode = *p_codec_info & A2DP_OPUS_CHANNEL_MODE_MASK; + p_ie->future1 = *p_codec_info & A2DP_OPUS_FRAMESIZE_MASK; + p_ie->sampleRate = *p_codec_info & A2DP_OPUS_SAMPLING_FREQ_MASK; + p_ie->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + + if (is_capability) { + // NOTE: The checks here are very liberal. We should be using more + // pedantic checks specific to the SRC or SNK as specified in the spec. + if (A2DP_BitsSet(p_ie->sampleRate) == A2DP_SET_ZERO_BIT) { + LOG_ERROR("invalid sample rate 0x%X", p_ie->sampleRate); + return A2DP_BAD_SAMP_FREQ; + } + if (A2DP_BitsSet(p_ie->channelMode) == A2DP_SET_ZERO_BIT) { + LOG_ERROR("invalid channel mode"); + return A2DP_BAD_CH_MODE; + } + + return A2DP_SUCCESS; + } + + if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT) { + LOG_ERROR("invalid sampling frequency 0x%X", p_ie->sampleRate); + return A2DP_BAD_SAMP_FREQ; + } + if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT) { + LOG_ERROR("invalid channel mode."); + return A2DP_BAD_CH_MODE; + } + + return A2DP_SUCCESS; +} + +// Build the Opus Media Payload Header. +// |p_dst| points to the location where the header should be written to. +// If |frag| is true, the media payload frame is fragmented. +// |start| is true for the first packet of a fragmented frame. +// |last| is true for the last packet of a fragmented frame. +// If |frag| is false, |num| is the number of number of frames in the packet, +// otherwise is the number of remaining fragments (including this one). +static void A2DP_BuildMediaPayloadHeaderOpus(uint8_t* p_dst, bool frag, + bool start, bool last, + uint8_t num) { + if (p_dst == NULL) return; + + *p_dst = 0; + if (frag) *p_dst |= A2DP_OPUS_HDR_F_MSK; + if (start) *p_dst |= A2DP_OPUS_HDR_S_MSK; + if (last) *p_dst |= A2DP_OPUS_HDR_L_MSK; + *p_dst |= (A2DP_OPUS_HDR_NUM_MSK & num); +} + +bool A2DP_IsVendorSourceCodecValidOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +bool A2DP_IsVendorSinkCodecValidOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +bool A2DP_IsVendorPeerSourceCodecValidOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +bool A2DP_IsVendorPeerSinkCodecValidOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE cfg_cie; + + /* Use a liberal check when parsing the codec info */ + return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) || + (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); +} + +bool A2DP_IsVendorSinkCodecSupportedOpus(const uint8_t* p_codec_info) { + return A2DP_CodecInfoMatchesCapabilityOpus(&a2dp_opus_sink_caps, p_codec_info, + false) == A2DP_SUCCESS; +} +bool A2DP_IsPeerSourceCodecSupportedOpus(const uint8_t* p_codec_info) { + return A2DP_CodecInfoMatchesCapabilityOpus(&a2dp_opus_sink_caps, p_codec_info, + true) == A2DP_SUCCESS; +} + +// Checks whether A2DP Opus codec configuration matches with a device's codec +// capabilities. |p_cap| is the Opus codec configuration. |p_codec_info| is +// the device's codec capabilities. +// If |is_capability| is true, the byte sequence is codec capabilities, +// otherwise is codec configuration. +// |p_codec_info| contains the codec capabilities for a peer device that +// is acting as an A2DP source. +// Returns A2DP_SUCCESS if the codec configuration matches with capabilities, +// otherwise the corresponding A2DP error status code. +static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityOpus( + const tA2DP_OPUS_CIE* p_cap, const uint8_t* p_codec_info, + bool is_capability) { + tA2DP_STATUS status; + tA2DP_OPUS_CIE cfg_cie; + + /* parse configuration */ + status = A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, is_capability); + if (status != A2DP_SUCCESS) { + LOG_ERROR("parsing failed %d", status); + return status; + } + + /* verify that each parameter is in range */ + + LOG_VERBOSE("SAMPLING FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate, + p_cap->sampleRate); + LOG_VERBOSE("CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode, + p_cap->channelMode); + LOG_VERBOSE("FRAMESIZE peer: 0x%x, capability 0x%x", cfg_cie.future1, + p_cap->future1); + + /* sampling frequency */ + if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_NS_SAMP_FREQ; + + /* channel mode */ + if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE; + + /* frameSize */ + if ((cfg_cie.future1 & p_cap->future1) == 0) return A2DP_NS_FRAMESIZE; + + return A2DP_SUCCESS; +} + +bool A2DP_VendorUsesRtpHeaderOpus(UNUSED_ATTR bool content_protection_enabled, + UNUSED_ATTR const uint8_t* p_codec_info) { + return true; +} + +const char* A2DP_VendorCodecNameOpus(UNUSED_ATTR const uint8_t* p_codec_info) { + return "Opus"; +} + +bool A2DP_VendorCodecTypeEqualsOpus(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b) { + tA2DP_OPUS_CIE Opus_cie_a; + tA2DP_OPUS_CIE Opus_cie_b; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = + A2DP_ParseInfoOpus(&Opus_cie_a, p_codec_info_a, true); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return false; + } + a2dp_status = A2DP_ParseInfoOpus(&Opus_cie_b, p_codec_info_b, true); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return false; + } + + return true; +} + +bool A2DP_VendorCodecEqualsOpus(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b) { + tA2DP_OPUS_CIE Opus_cie_a; + tA2DP_OPUS_CIE Opus_cie_b; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = + A2DP_ParseInfoOpus(&Opus_cie_a, p_codec_info_a, true); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return false; + } + a2dp_status = A2DP_ParseInfoOpus(&Opus_cie_b, p_codec_info_b, true); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return false; + } + + return (Opus_cie_a.sampleRate == Opus_cie_b.sampleRate) && + (Opus_cie_a.channelMode == Opus_cie_b.channelMode) && + (Opus_cie_a.future1 == Opus_cie_b.future1); +} + +int A2DP_VendorGetBitRateOpus(const uint8_t* p_codec_info) { + int channel_count = A2DP_VendorGetTrackChannelCountOpus(p_codec_info); + int framesize = A2DP_VendorGetFrameSizeOpus(p_codec_info); + int samplerate = A2DP_VendorGetTrackSampleRateOpus(p_codec_info); + + // in milliseconds + switch ((framesize * 1000) / samplerate) { + case 20: + if (channel_count == 2) { + return 256000; + } else if (channel_count == 1) { + return 128000; + } else + return -1; + default: + return -1; + } +} + +int A2DP_VendorGetTrackSampleRateOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + + switch (Opus_cie.sampleRate) { + case A2DP_OPUS_SAMPLING_FREQ_48000: + return 48000; + } + + return -1; +} + +int A2DP_VendorGetTrackBitsPerSampleOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + + switch (Opus_cie.bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + return 16; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + return 24; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + return 32; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + default: + LOG_ERROR("Invalid bit depth setting"); + return -1; + } +} + +int A2DP_VendorGetTrackChannelCountOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + + switch (Opus_cie.channelMode) { + case A2DP_OPUS_CHANNEL_MODE_MONO: + return 1; + case A2DP_OPUS_CHANNEL_MODE_STEREO: + case A2DP_OPUS_CHANNEL_MODE_DUAL_MONO: + return 2; + default: + LOG_ERROR("Invalid channel setting"); + } + + return -1; +} + +int A2DP_VendorGetSinkTrackChannelTypeOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + + switch (Opus_cie.channelMode) { + case A2DP_OPUS_CHANNEL_MODE_MONO: + return 1; + case A2DP_OPUS_CHANNEL_MODE_STEREO: + return 2; + } + + return -1; +} + +int A2DP_VendorGetChannelModeCodeOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + + switch (Opus_cie.channelMode) { + case A2DP_OPUS_CHANNEL_MODE_MONO: + case A2DP_OPUS_CHANNEL_MODE_STEREO: + return Opus_cie.channelMode; + default: + break; + } + + return -1; +} + +int A2DP_VendorGetFrameSizeOpus(const uint8_t* p_codec_info) { + tA2DP_OPUS_CIE Opus_cie; + + // Check whether the codec info contains valid data + tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false); + if (a2dp_status != A2DP_SUCCESS) { + LOG_ERROR("cannot decode codec information: %d", a2dp_status); + return -1; + } + int samplerate = A2DP_VendorGetTrackSampleRateOpus(p_codec_info); + + switch (Opus_cie.future1) { + case A2DP_OPUS_20MS_FRAMESIZE: + if (samplerate == 48000) { + return 960; + } + } + + return -1; +} + +bool A2DP_VendorGetPacketTimestampOpus(UNUSED_ATTR const uint8_t* p_codec_info, + const uint8_t* p_data, + uint32_t* p_timestamp) { + *p_timestamp = *(const uint32_t*)p_data; + return true; +} + +bool A2DP_VendorBuildCodecHeaderOpus(UNUSED_ATTR const uint8_t* p_codec_info, + BT_HDR* p_buf, + uint16_t frames_per_packet) { + uint8_t* p; + + p_buf->offset -= A2DP_OPUS_MPL_HDR_LEN; + p = (uint8_t*)(p_buf + 1) + p_buf->offset; + p_buf->len += A2DP_OPUS_MPL_HDR_LEN; + + A2DP_BuildMediaPayloadHeaderOpus(p, false, false, false, + (uint8_t)frames_per_packet); + + return true; +} + +std::string A2DP_VendorCodecInfoStringOpus(const uint8_t* p_codec_info) { + std::stringstream res; + std::string field; + tA2DP_STATUS a2dp_status; + tA2DP_OPUS_CIE Opus_cie; + + a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, true); + if (a2dp_status != A2DP_SUCCESS) { + res << "A2DP_ParseInfoOpus fail: " << loghex(a2dp_status); + return res.str(); + } + + res << "\tname: Opus\n"; + + // Sample frequency + field.clear(); + AppendField(&field, (Opus_cie.sampleRate == 0), "NONE"); + AppendField(&field, (Opus_cie.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000), + "48000"); + res << "\tsamp_freq: " << field << " (" << loghex(Opus_cie.sampleRate) + << ")\n"; + + // Channel mode + field.clear(); + AppendField(&field, (Opus_cie.channelMode == 0), "NONE"); + AppendField(&field, (Opus_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO), + "Mono"); + AppendField(&field, (Opus_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO), + "Stereo"); + res << "\tch_mode: " << field << " (" << loghex(Opus_cie.channelMode) + << ")\n"; + + // Framesize + field.clear(); + AppendField(&field, (Opus_cie.future1 == 0), "NONE"); + AppendField(&field, (Opus_cie.future1 & A2DP_OPUS_20MS_FRAMESIZE), "20ms"); + AppendField(&field, (Opus_cie.future1 & A2DP_OPUS_10MS_FRAMESIZE), "10ms"); + res << "\tframesize: " << field << " (" << loghex(Opus_cie.future1) << ")\n"; + + return res.str(); +} + +const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceOpus( + const uint8_t* p_codec_info) { + if (!A2DP_IsVendorSourceCodecValidOpus(p_codec_info)) return NULL; + + return &a2dp_encoder_interface_opus; +} + +const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceOpus( + const uint8_t* p_codec_info) { + if (!A2DP_IsVendorSinkCodecValidOpus(p_codec_info)) return NULL; + + return &a2dp_decoder_interface_opus; +} + +bool A2DP_VendorAdjustCodecOpus(uint8_t* p_codec_info) { + tA2DP_OPUS_CIE cfg_cie; + + // Nothing to do: just verify the codec info is valid + if (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS) + return false; + + return true; +} + +btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexOpus( + UNUSED_ATTR const uint8_t* p_codec_info) { + return BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS; +} + +btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexOpus( + UNUSED_ATTR const uint8_t* p_codec_info) { + return BTAV_A2DP_CODEC_INDEX_SINK_OPUS; +} + +const char* A2DP_VendorCodecIndexStrOpus(void) { return "Opus"; } + +const char* A2DP_VendorCodecIndexStrOpusSink(void) { return "Opus SINK"; } + +bool A2DP_VendorInitCodecConfigOpus(AvdtpSepConfig* p_cfg) { + if (A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &a2dp_opus_source_caps, + p_cfg->codec_info) != A2DP_SUCCESS) { + return false; + } + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) + /* Content protection info - support SCMS-T */ + uint8_t* p = p_cfg->protect_info; + *p++ = AVDT_CP_LOSC; + UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID); + p_cfg->num_protect = 1; +#endif + + return true; +} + +bool A2DP_VendorInitCodecConfigOpusSink(AvdtpSepConfig* p_cfg) { + return A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &a2dp_opus_sink_caps, + p_cfg->codec_info) == A2DP_SUCCESS; +} + +UNUSED_ATTR static void build_codec_config(const tA2DP_OPUS_CIE& config_cie, + btav_a2dp_codec_config_t* result) { + if (config_cie.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) + result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + + result->bits_per_sample = config_cie.bits_per_sample; + + if (config_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) + result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + if (config_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + if (config_cie.future1 & A2DP_OPUS_20MS_FRAMESIZE) + result->codec_specific_1 |= BTAV_A2DP_CODEC_FRAME_SIZE_20MS; + if (config_cie.future1 & A2DP_OPUS_10MS_FRAMESIZE) + result->codec_specific_1 |= BTAV_A2DP_CODEC_FRAME_SIZE_10MS; +} + +A2dpCodecConfigOpusSource::A2dpCodecConfigOpusSource( + btav_a2dp_codec_priority_t codec_priority) + : A2dpCodecConfigOpusBase(BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS, + A2DP_VendorCodecIndexStrOpus(), codec_priority, + true) { + // Compute the local capability + if (a2dp_opus_source_caps.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + codec_local_capability_.bits_per_sample = + a2dp_opus_source_caps.bits_per_sample; + if (a2dp_opus_source_caps.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (a2dp_opus_source_caps.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } +} + +A2dpCodecConfigOpusSource::~A2dpCodecConfigOpusSource() {} + +bool A2dpCodecConfigOpusSource::init() { + if (!isValid()) return false; + + return true; +} + +bool A2dpCodecConfigOpusSource::useRtpHeaderMarkerBit() const { return false; } + +// +// Selects the best sample rate from |sampleRate|. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_best_sample_rate(uint8_t sampleRate, + tA2DP_OPUS_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + p_result->sampleRate = A2DP_OPUS_SAMPLING_FREQ_48000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + return true; + } + return false; +} + +// +// Selects the audio sample rate from |p_codec_audio_config|. +// |sampleRate| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_sample_rate( + const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t sampleRate, + tA2DP_OPUS_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) { + switch (p_codec_audio_config->sample_rate) { + case BTAV_A2DP_CODEC_SAMPLE_RATE_48000: + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + p_result->sampleRate = A2DP_OPUS_SAMPLING_FREQ_48000; + p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + return true; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_16000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_24000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_44100: + case BTAV_A2DP_CODEC_SAMPLE_RATE_88200: + case BTAV_A2DP_CODEC_SAMPLE_RATE_96000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_176400: + case BTAV_A2DP_CODEC_SAMPLE_RATE_192000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE: + break; + } + + return false; +} + +// +// Selects the best bits per sample from |bits_per_sample|. +// |bits_per_sample| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_best_bits_per_sample( + btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_OPUS_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + return true; + } + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + return true; + } + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + return true; + } + return false; +} + +// +// Selects the audio bits per sample from |p_codec_audio_config|. +// |bits_per_sample| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_bits_per_sample( + const btav_a2dp_codec_config_t* p_codec_audio_config, + btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_OPUS_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + switch (p_codec_audio_config->bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32; + return true; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + break; + } + return false; +} + +// +// Selects the best channel mode from |channelMode|. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_best_channel_mode(uint8_t channelMode, + tA2DP_OPUS_CIE* p_result, + btav_a2dp_codec_config_t* p_codec_config) { + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_STEREO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + return true; + } + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_MONO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + return true; + } + return false; +} + +// +// Selects the audio channel mode from |p_codec_audio_config|. +// |channelMode| contains the capability. +// The result is stored in |p_result| and |p_codec_config|. +// Returns true if a selection was made, otherwise false. +// +static bool select_audio_channel_mode( + const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t channelMode, + tA2DP_OPUS_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) { + switch (p_codec_audio_config->channel_mode) { + case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO: + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_MONO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + return true; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO: + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_STEREO; + p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + return true; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE: + break; + } + + return false; +} + +bool A2dpCodecConfigOpusBase::setCodecConfig(const uint8_t* p_peer_codec_info, + bool is_capability, + uint8_t* p_result_codec_config) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + tA2DP_OPUS_CIE peer_info_cie; + tA2DP_OPUS_CIE result_config_cie; + uint8_t channelMode; + uint8_t sampleRate; + uint8_t frameSize; + btav_a2dp_codec_bits_per_sample_t bits_per_sample; + const tA2DP_OPUS_CIE* p_a2dp_opus_caps = + (is_source_) ? &a2dp_opus_source_caps : &a2dp_opus_sink_caps; + + btav_a2dp_codec_config_t device_codec_config_ = getCodecConfig(); + + LOG_INFO( + "AudioManager stream config %d sample rate %d bit depth %d channel " + "mode", + device_codec_config_.sample_rate, device_codec_config_.bits_per_sample, + device_codec_config_.channel_mode); + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_config = codec_config_; + btav_a2dp_codec_config_t saved_codec_capability = codec_capability_; + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_; + btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_; + uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE]; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_)); + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_, + sizeof(ota_codec_peer_config_)); + + tA2DP_STATUS status = + A2DP_ParseInfoOpus(&peer_info_cie, p_peer_codec_info, is_capability); + if (status != A2DP_SUCCESS) { + LOG_ERROR("can't parse peer's capabilities: error = %d", status); + goto fail; + } + + // + // Build the preferred configuration + // + memset(&result_config_cie, 0, sizeof(result_config_cie)); + result_config_cie.vendorId = p_a2dp_opus_caps->vendorId; + result_config_cie.codecId = p_a2dp_opus_caps->codecId; + + // + // Select the sample frequency + // + sampleRate = p_a2dp_opus_caps->sampleRate & peer_info_cie.sampleRate; + codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE; + + switch (codec_user_config_.sample_rate) { + case BTAV_A2DP_CODEC_SAMPLE_RATE_48000: + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + result_config_cie.sampleRate = A2DP_OPUS_SAMPLING_FREQ_48000; + codec_capability_.sample_rate = codec_user_config_.sample_rate; + codec_config_.sample_rate = codec_user_config_.sample_rate; + } + break; + case BTAV_A2DP_CODEC_SAMPLE_RATE_44100: + case BTAV_A2DP_CODEC_SAMPLE_RATE_88200: + case BTAV_A2DP_CODEC_SAMPLE_RATE_96000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_176400: + case BTAV_A2DP_CODEC_SAMPLE_RATE_192000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_16000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_24000: + case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE: + codec_capability_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE; + codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE; + break; + } + + // Select the sample frequency if there is no user preference + do { + // Compute the selectable capability + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + + if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break; + + // Compute the common capability + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) + codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + + // No user preference - try the codec audio config + if (select_audio_sample_rate(&codec_audio_config_, sampleRate, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - try the default config + if (select_best_sample_rate( + a2dp_opus_default_config.sampleRate & peer_info_cie.sampleRate, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - use the best match + if (select_best_sample_rate(sampleRate, &result_config_cie, + &codec_config_)) { + break; + } + } while (false); + if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) { + LOG_ERROR( + "cannot match sample frequency: local caps = 0x%x " + "peer info = 0x%x", + p_a2dp_opus_caps->sampleRate, peer_info_cie.sampleRate); + goto fail; + } + + // + // Select the bits per sample + // + // NOTE: this information is NOT included in the Opus A2DP codec description + // that is sent OTA. + bits_per_sample = p_a2dp_opus_caps->bits_per_sample; + codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + switch (codec_user_config_.bits_per_sample) { + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: + if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { + result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample; + codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample; + codec_config_.bits_per_sample = codec_user_config_.bits_per_sample; + } + break; + case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: + result_config_cie.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE; + break; + } + + // Select the bits per sample if there is no user preference + do { + // Compute the selectable capability + codec_selectable_capability_.bits_per_sample = + p_a2dp_opus_caps->bits_per_sample; + + if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) + break; + + // Compute the common capability + codec_capability_.bits_per_sample = bits_per_sample; + + // No user preference - try yhe codec audio config + if (select_audio_bits_per_sample(&codec_audio_config_, + p_a2dp_opus_caps->bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - try the default config + if (select_best_bits_per_sample(a2dp_opus_default_config.bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - use the best match + if (select_best_bits_per_sample(p_a2dp_opus_caps->bits_per_sample, + &result_config_cie, &codec_config_)) { + break; + } + } while (false); + if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) { + LOG_ERROR( + "cannot match bits per sample: default = 0x%x " + "user preference = 0x%x", + a2dp_opus_default_config.bits_per_sample, + codec_user_config_.bits_per_sample); + goto fail; + } + + // + // Select the channel mode + // + channelMode = p_a2dp_opus_caps->channelMode & peer_info_cie.channelMode; + codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE; + switch (codec_user_config_.channel_mode) { + case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO: + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + result_config_cie.channelMode = A2DP_OPUS_CHANNEL_MODE_MONO; + codec_capability_.channel_mode = codec_user_config_.channel_mode; + codec_config_.channel_mode = codec_user_config_.channel_mode; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO: + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + result_config_cie.channelMode = A2DP_OPUS_CHANNEL_MODE_STEREO; + codec_capability_.channel_mode = codec_user_config_.channel_mode; + codec_config_.channel_mode = codec_user_config_.channel_mode; + } + break; + case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE: + codec_capability_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE; + codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE; + break; + } + + // Select the channel mode if there is no user preference + do { + // Compute the selectable capability + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break; + + // Compute the common capability + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) + codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + // No user preference - try the codec audio config + if (select_audio_channel_mode(&codec_audio_config_, channelMode, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - try the default config + if (select_best_channel_mode( + a2dp_opus_default_config.channelMode & peer_info_cie.channelMode, + &result_config_cie, &codec_config_)) { + break; + } + + // No user preference - use the best match + if (select_best_channel_mode(channelMode, &result_config_cie, + &codec_config_)) { + break; + } + } while (false); + if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) { + LOG_ERROR( + "cannot match channel mode: local caps = 0x%x " + "peer info = 0x%x", + p_a2dp_opus_caps->channelMode, peer_info_cie.channelMode); + goto fail; + } + + // + // Select the frame size + // + frameSize = p_a2dp_opus_caps->future1 & peer_info_cie.future1; + codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE; + switch (codec_user_config_.codec_specific_1) { + case BTAV_A2DP_CODEC_FRAME_SIZE_20MS: + if (frameSize & A2DP_OPUS_20MS_FRAMESIZE) { + result_config_cie.future1 = A2DP_OPUS_20MS_FRAMESIZE; + codec_capability_.codec_specific_1 = + codec_user_config_.codec_specific_1; + codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1; + } + break; + case BTAV_A2DP_CODEC_FRAME_SIZE_10MS: + if (frameSize & A2DP_OPUS_10MS_FRAMESIZE) { + result_config_cie.future1 = A2DP_OPUS_10MS_FRAMESIZE; + codec_capability_.codec_specific_1 = + codec_user_config_.codec_specific_1; + codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1; + } + break; + case BTAV_A2DP_CODEC_FRAME_SIZE_NONE: + codec_capability_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE; + codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE; + break; + } + + // No user preference - set default value + codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_20MS; + result_config_cie.future1 = A2DP_OPUS_20MS_FRAMESIZE; + result_config_cie.future3 = 0x00; + + if (codec_config_.codec_specific_1 == BTAV_A2DP_CODEC_FRAME_SIZE_NONE) { + LOG_ERROR( + "cannot match frame size: local caps = 0x%x " + "peer info = 0x%x", + p_a2dp_opus_caps->future1, peer_info_cie.future1); + goto fail; + } + + if (A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, + p_result_codec_config) != A2DP_SUCCESS) { + LOG_ERROR("failed to BuildInfoOpus for result_config_cie"); + goto fail; + } + + // + // Copy the codec-specific fields if they are not zero + // + if (codec_user_config_.codec_specific_1 != 0) + codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1; + if (codec_user_config_.codec_specific_2 != 0) + codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2; + if (codec_user_config_.codec_specific_3 != 0) + codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3; + if (codec_user_config_.codec_specific_4 != 0) + codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4; + + // Create a local copy of the peer codec capability, and the + // result codec config. + if (is_capability) { + status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + } else { + status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_config_); + } + CHECK(status == A2DP_SUCCESS); + + status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie, + ota_codec_config_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_config_ = saved_codec_config; + codec_capability_ = saved_codec_capability; + codec_selectable_capability_ = saved_codec_selectable_capability; + codec_user_config_ = saved_codec_user_config; + codec_audio_config_ = saved_codec_audio_config; + memcpy(ota_codec_config_, saved_ota_codec_config, sizeof(ota_codec_config_)); + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + memcpy(ota_codec_peer_config_, saved_ota_codec_peer_config, + sizeof(ota_codec_peer_config_)); + return false; +} + +bool A2dpCodecConfigOpusBase::setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) { + std::lock_guard<std::recursive_mutex> lock(codec_mutex_); + tA2DP_OPUS_CIE peer_info_cie; + uint8_t channelMode; + uint8_t sampleRate; + const tA2DP_OPUS_CIE* p_a2dp_opus_caps = + (is_source_) ? &a2dp_opus_source_caps : &a2dp_opus_sink_caps; + + // Save the internal state + btav_a2dp_codec_config_t saved_codec_selectable_capability = + codec_selectable_capability_; + uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE]; + memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_, + sizeof(ota_codec_peer_capability_)); + + tA2DP_STATUS status = + A2DP_ParseInfoOpus(&peer_info_cie, p_peer_codec_capabilities, true); + if (status != A2DP_SUCCESS) { + LOG_ERROR("can't parse peer's capabilities: error = %d", status); + goto fail; + } + + // Compute the selectable capability - sample rate + sampleRate = p_a2dp_opus_caps->sampleRate & peer_info_cie.sampleRate; + if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) { + codec_selectable_capability_.sample_rate |= + BTAV_A2DP_CODEC_SAMPLE_RATE_48000; + } + + // Compute the selectable capability - bits per sample + codec_selectable_capability_.bits_per_sample = + p_a2dp_opus_caps->bits_per_sample; + + // Compute the selectable capability - channel mode + channelMode = p_a2dp_opus_caps->channelMode & peer_info_cie.channelMode; + if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; + } + if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) { + codec_selectable_capability_.channel_mode |= + BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; + } + + LOG_INFO("BuildInfoOpus for peer info cie for ota caps"); + status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie, + ota_codec_peer_capability_); + CHECK(status == A2DP_SUCCESS); + return true; + +fail: + // Restore the internal state + codec_selectable_capability_ = saved_codec_selectable_capability; + memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability, + sizeof(ota_codec_peer_capability_)); + return false; +} + +A2dpCodecConfigOpusSink::A2dpCodecConfigOpusSink( + btav_a2dp_codec_priority_t codec_priority) + : A2dpCodecConfigOpusBase(BTAV_A2DP_CODEC_INDEX_SINK_OPUS, + A2DP_VendorCodecIndexStrOpusSink(), + codec_priority, false) {} + +A2dpCodecConfigOpusSink::~A2dpCodecConfigOpusSink() {} + +bool A2dpCodecConfigOpusSink::init() { + if (!isValid()) return false; + + return true; +} + +bool A2dpCodecConfigOpusSink::useRtpHeaderMarkerBit() const { return false; } + +bool A2dpCodecConfigOpusSink::updateEncoderUserConfig( + UNUSED_ATTR const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + UNUSED_ATTR bool* p_restart_input, UNUSED_ATTR bool* p_restart_output, + UNUSED_ATTR bool* p_config_updated) { + return false; +} diff --git a/system/stack/a2dp/a2dp_vendor_opus_decoder.cc b/system/stack/a2dp/a2dp_vendor_opus_decoder.cc new file mode 100644 index 0000000000000000000000000000000000000000..20867b5a808d236f10c798005820409b6b934ab6 --- /dev/null +++ b/system/stack/a2dp/a2dp_vendor_opus_decoder.cc @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2021 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 "a2dp_opus_decoder" + +#include "a2dp_vendor_opus_decoder.h" + +#include <base/logging.h> +#include <opus.h> + +#include "a2dp_vendor_opus.h" +#include "osi/include/allocator.h" +#include "osi/include/log.h" + +typedef struct { + OpusDecoder* opus_handle = nullptr; + bool has_opus_handle; + int16_t* decode_buf = nullptr; + decoded_data_callback_t decode_callback; +} tA2DP_OPUS_DECODER_CB; + +static tA2DP_OPUS_DECODER_CB a2dp_opus_decoder_cb; + +void a2dp_vendor_opus_decoder_cleanup(void) { + if (a2dp_opus_decoder_cb.has_opus_handle) { + osi_free(a2dp_opus_decoder_cb.opus_handle); + + if (a2dp_opus_decoder_cb.decode_buf != nullptr) { + memset(a2dp_opus_decoder_cb.decode_buf, 0, + A2DP_OPUS_DECODE_BUFFER_LENGTH); + osi_free(a2dp_opus_decoder_cb.decode_buf); + a2dp_opus_decoder_cb.decode_buf = nullptr; + } + a2dp_opus_decoder_cb.has_opus_handle = false; + } + + return; +} + +bool a2dp_vendor_opus_decoder_init(decoded_data_callback_t decode_callback) { + a2dp_vendor_opus_decoder_cleanup(); + + int32_t err_val = OPUS_OK; + int32_t size = 0; + + size = opus_decoder_get_size(A2DP_OPUS_CODEC_OUTPUT_CHS); + a2dp_opus_decoder_cb.opus_handle = + static_cast<OpusDecoder*>(osi_malloc(size)); + if (a2dp_opus_decoder_cb.opus_handle == nullptr) { + LOG_ERROR("failed to allocate opus decoder handle"); + return false; + } + err_val = opus_decoder_init(a2dp_opus_decoder_cb.opus_handle, + A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE, + A2DP_OPUS_CODEC_OUTPUT_CHS); + if (err_val == OPUS_OK) { + a2dp_opus_decoder_cb.has_opus_handle = true; + + a2dp_opus_decoder_cb.decode_buf = + static_cast<int16_t*>(osi_malloc(A2DP_OPUS_DECODE_BUFFER_LENGTH)); + + memset(a2dp_opus_decoder_cb.decode_buf, 0, A2DP_OPUS_DECODE_BUFFER_LENGTH); + + a2dp_opus_decoder_cb.decode_callback = decode_callback; + LOG_INFO("decoder init success"); + return true; + } else { + LOG_ERROR("failed to initialize Opus Decoder"); + a2dp_opus_decoder_cb.has_opus_handle = false; + return false; + } + + return false; +} + +void a2dp_vendor_opus_decoder_configure(const uint8_t* p_codec_info) { return; } + +bool a2dp_vendor_opus_decoder_decode_packet(BT_HDR* p_buf) { + uint32_t frameSize; + uint32_t numChannels; + uint32_t numFrames; + int32_t ret_val = 0; + uint32_t frameLen = 0; + + if (p_buf == nullptr) { + LOG_ERROR("Dropping packet with nullptr"); + return false; + } + + auto* pBuffer = + reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset + 1); + int32_t bufferSize = p_buf->len - 1; + + numChannels = opus_packet_get_nb_channels(pBuffer); + numFrames = opus_packet_get_nb_frames(pBuffer, bufferSize); + frameSize = opus_packet_get_samples_per_frame( + pBuffer, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE); + frameLen = opus_packet_get_nb_samples(pBuffer, bufferSize, + A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE); + uint32_t num_frames = pBuffer[0] & 0xf; + + LOG_ERROR("numframes %d framesize %d framelen %d bufferSize %d", num_frames, + frameSize, frameLen, bufferSize); + LOG_ERROR("numChannels %d numFrames %d offset %d", numChannels, numFrames, + p_buf->offset); + + for (uint32_t frame = 0; frame < numFrames; ++frame) { + { + numChannels = opus_packet_get_nb_channels(pBuffer); + + ret_val = opus_decode(a2dp_opus_decoder_cb.opus_handle, + reinterpret_cast<unsigned char*>(pBuffer), + bufferSize, a2dp_opus_decoder_cb.decode_buf, + A2DP_OPUS_DECODE_BUFFER_LENGTH, 0 /* flags */); + + if (ret_val < OPUS_OK) { + LOG_ERROR("Opus DecodeFrame failed %d, applying concealment", ret_val); + ret_val = opus_decode(a2dp_opus_decoder_cb.opus_handle, NULL, 0, + a2dp_opus_decoder_cb.decode_buf, + A2DP_OPUS_DECODE_BUFFER_LENGTH, 0 /* flags */); + } + + size_t frame_len = + ret_val * numChannels * sizeof(a2dp_opus_decoder_cb.decode_buf[0]); + a2dp_opus_decoder_cb.decode_callback( + reinterpret_cast<uint8_t*>(a2dp_opus_decoder_cb.decode_buf), + frame_len); + } + } + return true; +} + +void a2dp_vendor_opus_decoder_start(void) { return; } + +void a2dp_vendor_opus_decoder_suspend(void) { + int32_t err_val = 0; + + if (a2dp_opus_decoder_cb.has_opus_handle) { + err_val = + opus_decoder_ctl(a2dp_opus_decoder_cb.opus_handle, OPUS_RESET_STATE); + if (err_val != OPUS_OK) { + LOG_ERROR("failed to reset decoder"); + } + } + return; +} diff --git a/system/stack/a2dp/a2dp_vendor_opus_encoder.cc b/system/stack/a2dp/a2dp_vendor_opus_encoder.cc new file mode 100644 index 0000000000000000000000000000000000000000..ded13e4ae9c260f29fbeb0fe5d5f08350d4ec066 --- /dev/null +++ b/system/stack/a2dp/a2dp_vendor_opus_encoder.cc @@ -0,0 +1,532 @@ +/* + * Copyright 2021 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 "a2dp_vendor_opus_encoder" +#define ATRACE_TAG ATRACE_TAG_AUDIO + +#include "a2dp_vendor_opus_encoder.h" + +#ifndef OS_GENERIC +#include <cutils/trace.h> +#endif +#include <dlfcn.h> +#include <inttypes.h> +#include <opus.h> +#include <stdio.h> +#include <string.h> + +#include "a2dp_vendor.h" +#include "a2dp_vendor_opus.h" +#include "common/time_util.h" +#include "osi/include/allocator.h" +#include "osi/include/log.h" +#include "osi/include/osi.h" +#include "stack/include/bt_hdr.h" + +typedef struct { + uint32_t sample_rate; + uint16_t bitrate; + uint16_t framesize; + uint8_t channel_mode; + uint8_t bits_per_sample; + uint8_t quality_mode_index; + int pcm_wlength; + uint8_t pcm_fmt; +} tA2DP_OPUS_ENCODER_PARAMS; + +typedef struct { + float counter; + uint32_t bytes_per_tick; + uint64_t last_frame_us; +} tA2DP_OPUS_FEEDING_STATE; + +typedef struct { + uint64_t session_start_us; + + size_t media_read_total_expected_packets; + size_t media_read_total_expected_reads_count; + size_t media_read_total_expected_read_bytes; + + size_t media_read_total_dropped_packets; + size_t media_read_total_actual_reads_count; + size_t media_read_total_actual_read_bytes; +} a2dp_opus_encoder_stats_t; + +typedef struct { + a2dp_source_read_callback_t read_callback; + a2dp_source_enqueue_callback_t enqueue_callback; + uint16_t TxAaMtuSize; + size_t TxQueueLength; + + bool use_SCMS_T; + bool is_peer_edr; // True if the peer device supports EDR + bool peer_supports_3mbps; // True if the peer device supports 3Mbps EDR + uint16_t peer_mtu; // MTU of the A2DP peer + uint32_t timestamp; // Timestamp for the A2DP frames + + OpusEncoder* opus_handle; + bool has_opus_handle; // True if opus_handle is valid + + tA2DP_FEEDING_PARAMS feeding_params; + tA2DP_OPUS_ENCODER_PARAMS opus_encoder_params; + tA2DP_OPUS_FEEDING_STATE opus_feeding_state; + + a2dp_opus_encoder_stats_t stats; +} tA2DP_OPUS_ENCODER_CB; + +static tA2DP_OPUS_ENCODER_CB a2dp_opus_encoder_cb; + +static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu, + A2dpCodecConfig* a2dp_codec_config, + bool* p_restart_input, + bool* p_restart_output, + bool* p_config_updated); +static void a2dp_opus_get_num_frame_iteration(uint8_t* num_of_iterations, + uint8_t* num_of_frames, + uint64_t timestamp_us); +static void a2dp_opus_encode_frames(uint8_t nb_frame); +static bool a2dp_opus_read_feeding(uint8_t* read_buffer, uint32_t* bytes_read); + +void a2dp_vendor_opus_encoder_cleanup(void) { + if (a2dp_opus_encoder_cb.has_opus_handle) { + osi_free(a2dp_opus_encoder_cb.opus_handle); + a2dp_opus_encoder_cb.has_opus_handle = false; + a2dp_opus_encoder_cb.opus_handle = nullptr; + } + memset(&a2dp_opus_encoder_cb, 0, sizeof(a2dp_opus_encoder_cb)); + + a2dp_opus_encoder_cb.stats.session_start_us = + bluetooth::common::time_get_os_boottime_us(); + + a2dp_opus_encoder_cb.timestamp = 0; + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) + a2dp_opus_encoder_cb.use_SCMS_T = true; +#else + a2dp_opus_encoder_cb.use_SCMS_T = false; +#endif + return; +} + +void a2dp_vendor_opus_encoder_init( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + A2dpCodecConfig* a2dp_codec_config, + a2dp_source_read_callback_t read_callback, + a2dp_source_enqueue_callback_t enqueue_callback) { + uint32_t error_val; + + a2dp_vendor_opus_encoder_cleanup(); + + a2dp_opus_encoder_cb.read_callback = read_callback; + a2dp_opus_encoder_cb.enqueue_callback = enqueue_callback; + a2dp_opus_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr; + a2dp_opus_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps; + a2dp_opus_encoder_cb.peer_mtu = p_peer_params->peer_mtu; + + // NOTE: Ignore the restart_input / restart_output flags - this initization + // happens when the connection is (re)started. + bool restart_input = false; + bool restart_output = false; + bool config_updated = false; + + uint32_t size = opus_encoder_get_size(A2DP_OPUS_CODEC_OUTPUT_CHS); + a2dp_opus_encoder_cb.opus_handle = + static_cast<OpusEncoder*>(osi_malloc(size)); + if (a2dp_opus_encoder_cb.opus_handle == nullptr) { + LOG_ERROR("failed to allocate opus encoder handle"); + return; + } + + error_val = opus_encoder_init( + a2dp_opus_encoder_cb.opus_handle, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE, + A2DP_OPUS_CODEC_OUTPUT_CHS, OPUS_APPLICATION_AUDIO); + + if (error_val != OPUS_OK) { + LOG_ERROR( + "failed to init opus encoder (handle size %d, sampling rate %d, " + "output chs %d, error %d)", + size, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE, A2DP_OPUS_CODEC_OUTPUT_CHS, + error_val); + osi_free(a2dp_opus_encoder_cb.opus_handle); + return; + } else { + a2dp_opus_encoder_cb.has_opus_handle = true; + } + + a2dp_vendor_opus_encoder_update(a2dp_opus_encoder_cb.peer_mtu, + a2dp_codec_config, &restart_input, + &restart_output, &config_updated); + + return; +} + +bool A2dpCodecConfigOpusSource::updateEncoderUserConfig( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input, + bool* p_restart_output, bool* p_config_updated) { + if (a2dp_opus_encoder_cb.peer_mtu == 0) { + LOG_ERROR( + "Cannot update the codec encoder for %s: " + "invalid peer MTU", + name().c_str()); + return false; + } + + return a2dp_vendor_opus_encoder_update(a2dp_opus_encoder_cb.peer_mtu, this, + p_restart_input, p_restart_output, + p_config_updated); +} + +static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu, + A2dpCodecConfig* a2dp_codec_config, + bool* p_restart_input, + bool* p_restart_output, + bool* p_config_updated) { + tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params = + &a2dp_opus_encoder_cb.opus_encoder_params; + uint8_t codec_info[AVDT_CODEC_SIZE]; + uint32_t error = 0; + + *p_restart_input = false; + *p_restart_output = false; + *p_config_updated = false; + + if (!a2dp_opus_encoder_cb.has_opus_handle || + a2dp_opus_encoder_cb.opus_handle == NULL) { + LOG_ERROR("Cannot get Opus encoder handle"); + return false; + } + CHECK(a2dp_opus_encoder_cb.opus_handle != nullptr); + + if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) { + LOG_ERROR( + "Cannot update the codec encoder for %s: " + "invalid codec config", + a2dp_codec_config->name().c_str()); + return false; + } + const uint8_t* p_codec_info = codec_info; + btav_a2dp_codec_config_t codec_config = a2dp_codec_config->getCodecConfig(); + + // The feeding parameters + tA2DP_FEEDING_PARAMS* p_feeding_params = &a2dp_opus_encoder_cb.feeding_params; + p_feeding_params->sample_rate = + A2DP_VendorGetTrackSampleRateOpus(p_codec_info); + p_feeding_params->bits_per_sample = + a2dp_codec_config->getAudioBitsPerSample(); + p_feeding_params->channel_count = + A2DP_VendorGetTrackChannelCountOpus(p_codec_info); + 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 = + a2dp_opus_encoder_cb.feeding_params.sample_rate; + p_encoder_params->channel_mode = + A2DP_VendorGetChannelModeCodeOpus(p_codec_info); + p_encoder_params->framesize = A2DP_VendorGetFrameSizeOpus(p_codec_info); + p_encoder_params->bitrate = A2DP_VendorGetBitRateOpus(p_codec_info); + + uint16_t mtu_size = + BT_DEFAULT_BUFFER_SIZE - A2DP_OPUS_OFFSET - sizeof(BT_HDR); + if (mtu_size < peer_mtu) { + a2dp_opus_encoder_cb.TxAaMtuSize = mtu_size; + } else { + a2dp_opus_encoder_cb.TxAaMtuSize = peer_mtu; + } + + // Set the bitrate quality mode index + if (codec_config.codec_specific_3 != 0) { + p_encoder_params->quality_mode_index = codec_config.codec_specific_3 % 10; + LOG_INFO("setting bitrate quality mode to %d", + p_encoder_params->quality_mode_index); + } else { + p_encoder_params->quality_mode_index = 5; + LOG_INFO("setting bitrate quality mode to default %d", + p_encoder_params->quality_mode_index); + } + + error = opus_encoder_ctl( + a2dp_opus_encoder_cb.opus_handle, + OPUS_SET_COMPLEXITY(p_encoder_params->quality_mode_index)); + + if (error != OPUS_OK) { + LOG_ERROR("failed to set encoder bitrate quality setting"); + return false; + } + + p_encoder_params->pcm_wlength = + a2dp_opus_encoder_cb.feeding_params.bits_per_sample >> 3; + + LOG_INFO("setting bitrate to %d", p_encoder_params->bitrate); + error = opus_encoder_ctl(a2dp_opus_encoder_cb.opus_handle, + OPUS_SET_BITRATE(p_encoder_params->bitrate)); + + if (error != OPUS_OK) { + LOG_ERROR("failed to set encoder bitrate"); + return false; + } + + // Set the Audio format from pcm_wlength + if (p_encoder_params->pcm_wlength == 2) + p_encoder_params->pcm_fmt = 16; + else if (p_encoder_params->pcm_wlength == 3) + p_encoder_params->pcm_fmt = 24; + else if (p_encoder_params->pcm_wlength == 4) + p_encoder_params->pcm_fmt = 32; + + return true; +} + +void a2dp_vendor_opus_feeding_reset(void) { + memset(&a2dp_opus_encoder_cb.opus_feeding_state, 0, + sizeof(a2dp_opus_encoder_cb.opus_feeding_state)); + + a2dp_opus_encoder_cb.opus_feeding_state.bytes_per_tick = + (a2dp_opus_encoder_cb.feeding_params.sample_rate * + a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8 * + a2dp_opus_encoder_cb.feeding_params.channel_count * + a2dp_vendor_opus_get_encoder_interval_ms()) / + 1000; + + return; +} + +void a2dp_vendor_opus_feeding_flush(void) { + a2dp_opus_encoder_cb.opus_feeding_state.counter = 0.0f; + + return; +} + +uint64_t a2dp_vendor_opus_get_encoder_interval_ms(void) { + return ((a2dp_opus_encoder_cb.opus_encoder_params.framesize * 1000) / + a2dp_opus_encoder_cb.opus_encoder_params.sample_rate); +} + +void a2dp_vendor_opus_send_frames(uint64_t timestamp_us) { + uint8_t nb_frame = 0; + uint8_t nb_iterations = 0; + + a2dp_opus_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us); + if (nb_frame == 0) return; + + for (uint8_t counter = 0; counter < nb_iterations; counter++) { + // Transcode frame and enqueue + a2dp_opus_encode_frames(nb_frame); + } + + return; +} + +// Obtains the number of frames to send and number of iterations +// to be used. |num_of_iterations| and |num_of_frames| parameters +// are used as output param for returning the respective values. +static void a2dp_opus_get_num_frame_iteration(uint8_t* num_of_iterations, + uint8_t* num_of_frames, + uint64_t timestamp_us) { + uint32_t result = 0; + uint8_t nof = 0; + uint8_t noi = 1; + + uint32_t pcm_bytes_per_frame = + a2dp_opus_encoder_cb.opus_encoder_params.framesize * + a2dp_opus_encoder_cb.feeding_params.channel_count * + a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8; + + uint32_t us_this_tick = a2dp_vendor_opus_get_encoder_interval_ms() * 1000; + uint64_t now_us = timestamp_us; + if (a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us != 0) + us_this_tick = + (now_us - a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us); + a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us = now_us; + + a2dp_opus_encoder_cb.opus_feeding_state.counter += + (float)a2dp_opus_encoder_cb.opus_feeding_state.bytes_per_tick * + us_this_tick / (a2dp_vendor_opus_get_encoder_interval_ms() * 1000); + + result = + a2dp_opus_encoder_cb.opus_feeding_state.counter / pcm_bytes_per_frame; + a2dp_opus_encoder_cb.opus_feeding_state.counter -= + result * pcm_bytes_per_frame; + nof = result; + + *num_of_frames = nof; + *num_of_iterations = noi; +} + +static void a2dp_opus_encode_frames(uint8_t nb_frame) { + tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params = + &a2dp_opus_encoder_cb.opus_encoder_params; + unsigned char* packet; + uint8_t remain_nb_frame = nb_frame; + uint16_t opus_frame_size = p_encoder_params->framesize; + uint8_t read_buffer[p_encoder_params->framesize * + p_encoder_params->pcm_wlength * + p_encoder_params->channel_mode]; + + int32_t out_frames = 0; + uint32_t written = 0; + + uint32_t bytes_read = 0; + while (nb_frame) { + BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE); + p_buf->offset = A2DP_OPUS_OFFSET; + p_buf->len = 0; + p_buf->layer_specific = 0; + a2dp_opus_encoder_cb.stats.media_read_total_expected_packets++; + + do { + // + // Read the PCM data and encode it + // + uint32_t temp_bytes_read = 0; + if (a2dp_opus_read_feeding(read_buffer, &temp_bytes_read)) { + bytes_read += temp_bytes_read; + packet = (unsigned char*)(p_buf + 1) + p_buf->offset + p_buf->len; + + if (a2dp_opus_encoder_cb.opus_handle == NULL) { + LOG_ERROR("invalid OPUS handle"); + a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++; + osi_free(p_buf); + return; + } + + written = + opus_encode(a2dp_opus_encoder_cb.opus_handle, + (const opus_int16*)&read_buffer[0], opus_frame_size, + packet, (BT_DEFAULT_BUFFER_SIZE - p_buf->offset)); + + if (written <= 0) { + LOG_ERROR("OPUS encoding error"); + a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++; + osi_free(p_buf); + return; + } else { + out_frames++; + } + p_buf->len += written; + nb_frame--; + p_buf->layer_specific += out_frames; // added a frame to the buffer + } else { + LOG_WARN("Opus src buffer underflow %d", nb_frame); + a2dp_opus_encoder_cb.opus_feeding_state.counter += + nb_frame * opus_frame_size * + a2dp_opus_encoder_cb.feeding_params.channel_count * + a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8; + + // no more pcm to read + nb_frame = 0; + } + } while ((written == 0) && nb_frame); + + if (p_buf->len) { + /* + * Timestamp of the media packet header represent the TS of the + * first frame, i.e. the timestamp before including this frame. + */ + *((uint32_t*)(p_buf + 1)) = a2dp_opus_encoder_cb.timestamp; + + a2dp_opus_encoder_cb.timestamp += p_buf->layer_specific * opus_frame_size; + + uint8_t done_nb_frame = remain_nb_frame - nb_frame; + remain_nb_frame = nb_frame; + + if (!a2dp_opus_encoder_cb.enqueue_callback(p_buf, done_nb_frame, + bytes_read)) + return; + } else { + a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++; + osi_free(p_buf); + } + } +} + +static bool a2dp_opus_read_feeding(uint8_t* read_buffer, uint32_t* bytes_read) { + uint32_t read_size = a2dp_opus_encoder_cb.opus_encoder_params.framesize * + a2dp_opus_encoder_cb.feeding_params.channel_count * + a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8; + + a2dp_opus_encoder_cb.stats.media_read_total_expected_reads_count++; + a2dp_opus_encoder_cb.stats.media_read_total_expected_read_bytes += read_size; + + /* Read Data from UIPC channel */ + uint32_t nb_byte_read = + a2dp_opus_encoder_cb.read_callback(read_buffer, read_size); + a2dp_opus_encoder_cb.stats.media_read_total_actual_read_bytes += nb_byte_read; + + if (nb_byte_read < read_size) { + if (nb_byte_read == 0) return false; + + /* Fill the unfilled part of the read buffer with silence (0) */ + memset(((uint8_t*)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read); + nb_byte_read = read_size; + } + a2dp_opus_encoder_cb.stats.media_read_total_actual_reads_count++; + + *bytes_read = nb_byte_read; + return true; +} + +void a2dp_vendor_opus_set_transmit_queue_length(size_t transmit_queue_length) { + a2dp_opus_encoder_cb.TxQueueLength = transmit_queue_length; + + return; +} + +uint64_t A2dpCodecConfigOpusSource::encoderIntervalMs() const { + return a2dp_vendor_opus_get_encoder_interval_ms(); +} + +int a2dp_vendor_opus_get_effective_frame_size() { + return a2dp_opus_encoder_cb.TxAaMtuSize; +} + +void A2dpCodecConfigOpusSource::debug_codec_dump(int fd) { + a2dp_opus_encoder_stats_t* stats = &a2dp_opus_encoder_cb.stats; + tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params = + &a2dp_opus_encoder_cb.opus_encoder_params; + + A2dpCodecConfig::debug_codec_dump(fd); + + dprintf(fd, + " Packet counts (expected/dropped) : %zu / " + "%zu\n", + stats->media_read_total_expected_packets, + stats->media_read_total_dropped_packets); + + dprintf(fd, + " PCM read counts (expected/actual) : %zu / " + "%zu\n", + stats->media_read_total_expected_reads_count, + stats->media_read_total_actual_reads_count); + + dprintf(fd, + " PCM read bytes (expected/actual) : %zu / " + "%zu\n", + stats->media_read_total_expected_read_bytes, + stats->media_read_total_actual_read_bytes); + + dprintf(fd, + " OPUS transmission bitrate (Kbps) : %d\n", + p_encoder_params->bitrate); + + dprintf(fd, + " OPUS saved transmit queue length : %zu\n", + a2dp_opus_encoder_cb.TxQueueLength); + + return; +} diff --git a/system/stack/include/a2dp_error_codes.h b/system/stack/include/a2dp_error_codes.h index 0aa7105b7acd20c4275cbed319f351eb4dfb3632..ae5e26acf5f2502fd1027eccfe7372a91b8fed4d 100644 --- a/system/stack/include/a2dp_error_codes.h +++ b/system/stack/include/a2dp_error_codes.h @@ -128,6 +128,9 @@ */ #define A2DP_BAD_CP_FORMAT 0xE1 +/* Invalid framesize */ +#define A2DP_NS_FRAMESIZE 0xE2 + typedef uint8_t tA2DP_STATUS; #endif // A2DP_ERROR_CODES_H diff --git a/system/stack/include/a2dp_vendor_opus.h b/system/stack/include/a2dp_vendor_opus.h new file mode 100644 index 0000000000000000000000000000000000000000..08a0b7b660f2ede8478d9e9aed7d540099be9df0 --- /dev/null +++ b/system/stack/include/a2dp_vendor_opus.h @@ -0,0 +1,255 @@ +/* + * Copyright 2021 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. + */ + +// +// A2DP Codec API for Opus +// + +#ifndef A2DP_VENDOR_OPUS_H +#define A2DP_VENDOR_OPUS_H + +#include "a2dp_codec_api.h" +#include "a2dp_vendor_opus_constants.h" +#include "avdt_api.h" + +class A2dpCodecConfigOpusBase : public A2dpCodecConfig { + protected: + A2dpCodecConfigOpusBase(btav_a2dp_codec_index_t codec_index, + const std::string& name, + btav_a2dp_codec_priority_t codec_priority, + bool is_source) + : A2dpCodecConfig(codec_index, name, codec_priority), + is_source_(is_source) {} + bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability, + uint8_t* p_result_codec_config) override; + bool setPeerCodecCapabilities( + const uint8_t* p_peer_codec_capabilities) override; + + private: + bool is_source_; // True if local is Source +}; + +class A2dpCodecConfigOpusSource : public A2dpCodecConfigOpusBase { + public: + A2dpCodecConfigOpusSource(btav_a2dp_codec_priority_t codec_priority); + virtual ~A2dpCodecConfigOpusSource(); + + bool init() override; + uint64_t encoderIntervalMs() const; + + private: + bool useRtpHeaderMarkerBit() const override; + bool updateEncoderUserConfig( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + bool* p_restart_input, bool* p_restart_output, bool* p_config_updated); + void debug_codec_dump(int fd) override; +}; + +class A2dpCodecConfigOpusSink : public A2dpCodecConfigOpusBase { + public: + A2dpCodecConfigOpusSink(btav_a2dp_codec_priority_t codec_priority); + virtual ~A2dpCodecConfigOpusSink(); + + bool init() override; + uint64_t encoderIntervalMs() const; + + private: + bool useRtpHeaderMarkerBit() const override; + bool updateEncoderUserConfig( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + bool* p_restart_input, bool* p_restart_output, bool* p_config_updated); +}; + +// Checks whether the codec capabilities contain a valid A2DP Opus Source +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid Opus +// codec, otherwise false. +bool A2DP_IsVendorSourceCodecValidOpus(const uint8_t* p_codec_info); + +// Checks whether the codec capabilities contain a valid A2DP Opus Sink +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid Opus +// codec, otherwise false. +bool A2DP_IsVendorSinkCodecValidOpus(const uint8_t* p_codec_info); + +// Checks whether the codec capabilities contain a valid peer A2DP Opus Sink +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid Opus +// codec, otherwise false. +bool A2DP_IsVendorPeerSinkCodecValidOpus(const uint8_t* p_codec_info); + +// Checks whether the codec capabilities contain a valid peer A2DP Opus Source +// codec. +// NOTE: only codecs that are implemented are considered valid. +// Returns true if |p_codec_info| contains information about a valid Opus +// codec, otherwise false. +bool A2DP_IsVendorPeerSourceCodecValidOpus(const uint8_t* p_codec_info); + +// Checks whether A2DP Opus Sink codec is supported. +// |p_codec_info| contains information about the codec capabilities. +// Returns true if the A2DP Opus Sink codec is supported, otherwise false. +bool A2DP_IsVendorSinkCodecSupportedOpus(const uint8_t* p_codec_info); + +// Checks whether an A2DP Opus Source codec for a peer Source device is +// supported. +// |p_codec_info| contains information about the codec capabilities of the +// peer device. +// Returns true if the A2DP Opus Source codec for a peer Source device is +// supported, otherwise false. +bool A2DP_IsPeerSourceCodecSupportedOpus(const uint8_t* p_codec_info); + +// Checks whether the A2DP data packets should contain RTP header. +// |content_protection_enabled| is true if Content Protection is +// enabled. |p_codec_info| contains information about the codec capabilities. +// Returns true if the A2DP data packets should contain RTP header, otherwise +// false. +bool A2DP_VendorUsesRtpHeaderOpus(bool content_protection_enabled, + const uint8_t* p_codec_info); + +// Gets the A2DP Opus codec name for a given |p_codec_info|. +const char* A2DP_VendorCodecNameOpus(const uint8_t* p_codec_info); + +// Checks whether two A2DP Opus codecs |p_codec_info_a| and |p_codec_info_b| +// have the same type. +// Returns true if the two codecs have the same type, otherwise false. +bool A2DP_VendorCodecTypeEqualsOpus(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b); + +// Checks whether two A2DP Opus codecs |p_codec_info_a| and |p_codec_info_b| +// are exactly the same. +// Returns true if the two codecs are exactly the same, otherwise false. +// If the codec type is not Opus, the return value is false. +bool A2DP_VendorCodecEqualsOpus(const uint8_t* p_codec_info_a, + const uint8_t* p_codec_info_b); + +// Gets the track sample rate value for the A2DP Opus codec. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the track sample rate on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackSampleRateOpus(const uint8_t* p_codec_info); + +// Gets the track bits per sample value for the A2DP Opus codec. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the track bits per sample on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackBitsPerSampleOpus(const uint8_t* p_codec_info); + +// Gets the track bitrate value for the A2DP Opus codec. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the track sample rate on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetBitRateOpus(const uint8_t* p_codec_info); + +// Gets the channel count for the A2DP Opus codec. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the channel count on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetTrackChannelCountOpus(const uint8_t* p_codec_info); + +// Gets the channel type for the A2DP Opus codec. +// 1 for mono, or 3 for dual channel/stereo. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the channel count on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetSinkTrackChannelTypeOpus(const uint8_t* p_codec_info); + +// Gets the channel mode code for the A2DP Opus codec. +// The actual value is codec-specific - see |A2DP_OPUS_CHANNEL_MODE_*|. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the channel mode code on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetChannelModeCodeOpus(const uint8_t* p_codec_info); + +// Gets the framesize value (in ms) for the A2DP Opus codec. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns the framesize on success, or -1 if |p_codec_info| +// contains invalid codec information. +int A2DP_VendorGetFrameSizeOpus(const uint8_t* p_codec_info); + +// Gets the A2DP Opus audio data timestamp from an audio packet. +// |p_codec_info| contains the codec information. +// |p_data| contains the audio data. +// The timestamp is stored in |p_timestamp|. +// Returns true on success, otherwise false. +bool A2DP_VendorGetPacketTimestampOpus(const uint8_t* p_codec_info, + const uint8_t* p_data, + uint32_t* p_timestamp); + +// Builds A2DP Opus codec header for audio data. +// |p_codec_info| contains the codec information. +// |p_buf| contains the audio data. +// |frames_per_packet| is the number of frames in this packet. +// Returns true on success, otherwise false. +bool A2DP_VendorBuildCodecHeaderOpus(const uint8_t* p_codec_info, BT_HDR* p_buf, + uint16_t frames_per_packet); + +// Decodes A2DP Opus codec info into a human readable string. +// |p_codec_info| is a pointer to the Opus codec_info to decode. +// Returns a string describing the codec information. +std::string A2DP_VendorCodecInfoStringOpus(const uint8_t* p_codec_info); + +// Gets the A2DP Opus encoder interface that can be used to encode and prepare +// A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|. +// |p_codec_info| contains the codec information. +// Returns the A2DP Opus encoder interface if the |p_codec_info| is valid and +// supported, otherwise NULL. +const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceOpus( + const uint8_t* p_codec_info); + +// Gets the current A2DP Opus decoder interface that can be used to decode +// received A2DP packets - see |tA2DP_DECODER_INTERFACE|. +// |p_codec_info| contains the codec information. +// Returns the A2DP Opus decoder interface if the |p_codec_info| is valid and +// supported, otherwise NULL. +const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceOpus( + const uint8_t* p_codec_info); + +// Adjusts the A2DP Opus codec, based on local support and Bluetooth +// specification. +// |p_codec_info| contains the codec information to adjust. +// Returns true if |p_codec_info| is valid and supported, otherwise false. +bool A2DP_VendorAdjustCodecOpus(uint8_t* p_codec_info); + +// Gets the A2DP Opus Source codec index for a given |p_codec_info|. +// Returns the corresponding |btav_a2dp_codec_index_t| on success, +// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|. +btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexOpus( + const uint8_t* p_codec_info); + +// Gets the A2DP Opus Sink codec index for a given |p_codec_info|. +// Returns the corresponding |btav_a2dp_codec_index_t| on success, +// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|. +btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexOpus( + const uint8_t* p_codec_info); + +// Gets the A2DP Opus Source codec name. +const char* A2DP_VendorCodecIndexStrOpus(void); + +// Gets the A2DP Opus Sink codec name. +const char* A2DP_VendorCodecIndexStrOpusSink(void); + +// Initializes A2DP Opus Source codec information into |AvdtpSepConfig| +// configuration entry pointed by |p_cfg|. +bool A2DP_VendorInitCodecConfigOpus(AvdtpSepConfig* p_cfg); + +// Initializes A2DP Opus Sink codec information into |AvdtpSepConfig| +// configuration entry pointed by |p_cfg|. +bool A2DP_VendorInitCodecConfigOpusSink(AvdtpSepConfig* p_cfg); + +#endif // A2DP_VENDOR_OPUS_H diff --git a/system/stack/include/a2dp_vendor_opus_constants.h b/system/stack/include/a2dp_vendor_opus_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..272b04c48d3dea7a62ac3b30e48d22864774e5ef --- /dev/null +++ b/system/stack/include/a2dp_vendor_opus_constants.h @@ -0,0 +1,66 @@ +/* + * Copyright 2021 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. + */ + +// +// A2DP constants for Opus codec +// + +#ifndef A2DP_VENDOR_OPUS_CONSTANTS_H +#define A2DP_VENDOR_OPUS_CONSTANTS_H + +#define A2DP_OPUS_CODEC_LEN 9 + +#define A2DP_OPUS_CODEC_OUTPUT_CHS 2 +#define A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE 48000 +#define A2DP_OPUS_CODEC_DEFAULT_FRAMESIZE 960 +#define A2DP_OPUS_DECODE_BUFFER_LENGTH \ + (A2DP_OPUS_CODEC_OUTPUT_CHS * A2DP_OPUS_CODEC_DEFAULT_FRAMESIZE * 4) + +// [Octet 0-3] Vendor ID +#define A2DP_OPUS_VENDOR_ID 0x000000E0 +// [Octet 4-5] Vendor Specific Codec ID +#define A2DP_OPUS_CODEC_ID 0x0001 +// [Octet 6], [Bits 0,1,2] Channel Mode +#define A2DP_OPUS_CHANNEL_MODE_MASK 0x07 +#define A2DP_OPUS_CHANNEL_MODE_MONO 0x01 +#define A2DP_OPUS_CHANNEL_MODE_STEREO 0x02 +#define A2DP_OPUS_CHANNEL_MODE_DUAL_MONO 0x04 +// [Octet 6], [Bits 3,4] Future 2, FrameSize +#define A2DP_OPUS_FRAMESIZE_MASK 0x18 +#define A2DP_OPUS_10MS_FRAMESIZE 0x08 +#define A2DP_OPUS_20MS_FRAMESIZE 0x10 +// [Octet 6], [Bits 5] Sampling Frequency +#define A2DP_OPUS_SAMPLING_FREQ_MASK 0x80 +#define A2DP_OPUS_SAMPLING_FREQ_48000 0x80 +// [Octet 6], [Bits 6,7] Reserved +#define A2DP_OPUS_FUTURE_3 0x40 +#define A2DP_OPUS_FUTURE_4 0x80 + +// Length of the Opus Media Payload header +#define A2DP_OPUS_MPL_HDR_LEN 1 + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) +#define A2DP_OPUS_OFFSET (AVDT_MEDIA_OFFSET + A2DP_OPUS_MPL_HDR_LEN + 1) +#else +#define A2DP_OPUS_OFFSET (AVDT_MEDIA_OFFSET + A2DP_OPUS_MPL_HDR_LEN) +#endif + +#define A2DP_OPUS_HDR_F_MSK 0x80 +#define A2DP_OPUS_HDR_S_MSK 0x40 +#define A2DP_OPUS_HDR_L_MSK 0x20 +#define A2DP_OPUS_HDR_NUM_MSK 0x0F + +#endif diff --git a/system/stack/include/a2dp_vendor_opus_decoder.h b/system/stack/include/a2dp_vendor_opus_decoder.h new file mode 100644 index 0000000000000000000000000000000000000000..67b5bf706f3b2e22641589cdf7731f668474f045 --- /dev/null +++ b/system/stack/include/a2dp_vendor_opus_decoder.h @@ -0,0 +1,45 @@ +/* + * Copyright 2021 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. + */ + +// +// Interface to the A2DP Opus Decoder +// + +#ifndef A2DP_VENDOR_OPUS_DECODER_H +#define A2DP_VENDOR_OPUS_DECODER_H + +#include "a2dp_codec_api.h" + +// Initialize the A2DP Opus decoder. +bool a2dp_vendor_opus_decoder_init(decoded_data_callback_t decode_callback); + +// Cleanup the A2DP Opus decoder. +void a2dp_vendor_opus_decoder_cleanup(void); + +// Decodes |p_buf|. Calls |decode_callback| passed into +// |a2dp_vendor_opus_decoder_init| if decoded frames are available. +bool a2dp_vendor_opus_decoder_decode_packet(BT_HDR* p_buf); + +// Start the A2DP Opus decoder. +void a2dp_vendor_opus_decoder_start(void); + +// Suspend the A2DP Opus decoder. +void a2dp_vendor_opus_decoder_suspend(void); + +// A2DP Opus decoder configuration. +void a2dp_vendor_opus_decoder_configure(const uint8_t* p_codec_info); + +#endif // A2DP_VENDOR_OPUS_DECODER_H diff --git a/system/stack/include/a2dp_vendor_opus_encoder.h b/system/stack/include/a2dp_vendor_opus_encoder.h new file mode 100644 index 0000000000000000000000000000000000000000..0f88265f1f94dfe8799d73c5d5c9a89ce55882a1 --- /dev/null +++ b/system/stack/include/a2dp_vendor_opus_encoder.h @@ -0,0 +1,59 @@ +/* + * Copyright 2021 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. + */ + +// +// Interface to the A2DP Opus Encoder +// + +#ifndef A2DP_VENDOR_OPUS_ENCODER_H +#define A2DP_VENDOR_OPUS_ENCODER_H + +#include "a2dp_codec_api.h" + +// Initialize the A2DP Opus encoder. +// |p_peer_params| contains the A2DP peer information +// The current A2DP codec config is in |a2dp_codec_config|. +// |read_callback| is the callback for reading the input audio data. +// |enqueue_callback| is the callback for enqueueing the encoded audio data. +void a2dp_vendor_opus_encoder_init( + const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, + A2dpCodecConfig* a2dp_codec_config, + a2dp_source_read_callback_t read_callback, + a2dp_source_enqueue_callback_t enqueue_callback); + +// Cleanup the A2DP Opus encoder. +void a2dp_vendor_opus_encoder_cleanup(void); + +// Reset the feeding for the A2DP Opus encoder. +void a2dp_vendor_opus_feeding_reset(void); + +// Flush the feeding for the A2DP Opus encoder. +void a2dp_vendor_opus_feeding_flush(void); + +// Get the A2DP Opus encoder interval (in milliseconds). +uint64_t a2dp_vendor_opus_get_encoder_interval_ms(void); + +// Prepare and send A2DP Opus encoded frames. +// |timestamp_us| is the current timestamp (in microseconds). +void a2dp_vendor_opus_send_frames(uint64_t timestamp_us); + +// Set transmit queue length for the A2DP Opus (Dynamic Bit Rate) mechanism. +void a2dp_vendor_opus_set_transmit_queue_length(size_t transmit_queue_length); + +// Get the A2DP Opus encoded maximum frame size +int a2dp_vendor_opus_get_effective_frame_size(); + +#endif // A2DP_VENDOR_OPUS_ENCODER_H diff --git a/system/stack/test/fuzzers/Android.bp b/system/stack/test/fuzzers/Android.bp index 54812c0b29871168005544ffc92df6501f661b7f..376b5ad5927c252f1ddf506c3389e154b2ad73e5 100644 --- a/system/stack/test/fuzzers/Android.bp +++ b/system/stack/test/fuzzers/Android.bp @@ -35,6 +35,7 @@ cc_defaults { "libbtdevice", "libg722codec", "liblc3", + "libopus", "libosi", "libudrv-uipc", "libbt-protos-lite", diff --git a/system/stack/test/stack_a2dp_test.cc b/system/stack/test/stack_a2dp_test.cc index fe7692a8a5f414e4b4fed7eb62ef41fac7bb38f3..84744211185a76de4f6b8a89c5065f3a9baa2dac 100644 --- a/system/stack/test/stack_a2dp_test.cc +++ b/system/stack/test/stack_a2dp_test.cc @@ -27,6 +27,7 @@ #include "stack/include/a2dp_codec_api.h" #include "stack/include/a2dp_sbc.h" #include "stack/include/a2dp_vendor.h" +#include "stack/include/a2dp_vendor_opus_constants.h" #include "stack/include/bt_hdr.h" namespace { @@ -187,6 +188,47 @@ const uint8_t codec_info_aac_sink_capability[AVDT_CODEC_SIZE] = { 9 // Fake }; +const uint8_t codec_info_opus[AVDT_CODEC_SIZE] = { + A2DP_OPUS_CODEC_LEN, // Length + AVDT_MEDIA_TYPE_AUDIO << 4, // Media Type + A2DP_MEDIA_CT_NON_A2DP, // Media Codec Type Vendor + (A2DP_OPUS_VENDOR_ID & 0x000000FF), + (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8, + (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16, + (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24, + (A2DP_OPUS_CODEC_ID & 0x00FF), + (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8, + A2DP_OPUS_CHANNEL_MODE_STEREO | A2DP_OPUS_20MS_FRAMESIZE | + A2DP_OPUS_SAMPLING_FREQ_48000}; + +const uint8_t codec_info_opus_capability[AVDT_CODEC_SIZE] = { + A2DP_OPUS_CODEC_LEN, // Length + AVDT_MEDIA_TYPE_AUDIO << 4, // Media Type + A2DP_MEDIA_CT_NON_A2DP, // Media Codec Type Vendor + (A2DP_OPUS_VENDOR_ID & 0x000000FF), + (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8, + (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16, + (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24, + (A2DP_OPUS_CODEC_ID & 0x00FF), + (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8, + A2DP_OPUS_CHANNEL_MODE_MONO | A2DP_OPUS_CHANNEL_MODE_STEREO | + A2DP_OPUS_10MS_FRAMESIZE | A2DP_OPUS_20MS_FRAMESIZE | + A2DP_OPUS_SAMPLING_FREQ_48000}; + +const uint8_t codec_info_opus_sink_capability[AVDT_CODEC_SIZE] = { + A2DP_OPUS_CODEC_LEN, // Length + AVDT_MEDIA_TYPE_AUDIO << 4, // Media Type + A2DP_MEDIA_CT_NON_A2DP, // Media Codec Type Vendor + (A2DP_OPUS_VENDOR_ID & 0x000000FF), + (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8, + (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16, + (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24, + (A2DP_OPUS_CODEC_ID & 0x00FF), + (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8, + A2DP_OPUS_CHANNEL_MODE_MONO | A2DP_OPUS_CHANNEL_MODE_STEREO | + A2DP_OPUS_10MS_FRAMESIZE | A2DP_OPUS_20MS_FRAMESIZE | + A2DP_OPUS_SAMPLING_FREQ_48000}; + const uint8_t codec_info_non_a2dp[AVDT_CODEC_SIZE] = { 8, // Length 0, // Media Type: AVDT_MEDIA_TYPE_AUDIO @@ -265,10 +307,11 @@ class StackA2dpTest : public ::testing::Test { supported = has_shared_library(LDAC_DECODER_LIB_NAME); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: + break; case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: - // TODO(b/226441860): in-progress case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: - // TODO(b/226441860): in-progress + supported = true; + break; case BTAV_A2DP_CODEC_INDEX_MAX: // Needed to avoid using "default:" case so we can capture when // a new codec is added, and it can be included here. @@ -386,6 +429,32 @@ TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_aac) { EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_aac_invalid)); } +TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_opus) { + ASSERT_TRUE(A2DP_IsVendorSourceCodecValid(codec_info_opus)); + ASSERT_TRUE(A2DP_IsVendorSourceCodecValid(codec_info_opus_capability)); + ASSERT_TRUE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus)); + ASSERT_TRUE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_capability)); + + ASSERT_TRUE(A2DP_IsVendorSinkCodecValid(codec_info_opus_sink_capability)); + ASSERT_TRUE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_sink_capability)); + + // Test with invalid Opus configuration + uint8_t codec_info_opus_invalid[AVDT_CODEC_SIZE]; + memcpy(codec_info_opus_invalid, codec_info_opus, sizeof(codec_info_opus)); + codec_info_opus_invalid[0] = 0; // Corrupt the Length field + ASSERT_FALSE(A2DP_IsVendorSourceCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorSinkCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_invalid)); + + memcpy(codec_info_opus_invalid, codec_info_opus, sizeof(codec_info_opus)); + codec_info_opus_invalid[1] = 0xff; // Corrupt the Media Type field + ASSERT_FALSE(A2DP_IsVendorSourceCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorSinkCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_invalid)); + ASSERT_FALSE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_invalid)); +} + TEST_F(StackA2dpTest, test_a2dp_get_codec_type) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(codec_info_sbc); EXPECT_EQ(codec_type, A2DP_MEDIA_CT_SBC); @@ -393,6 +462,9 @@ TEST_F(StackA2dpTest, test_a2dp_get_codec_type) { codec_type = A2DP_GetCodecType(codec_info_aac); EXPECT_EQ(codec_type, A2DP_MEDIA_CT_AAC); + codec_type = A2DP_GetCodecType(codec_info_opus); + ASSERT_EQ(codec_type, A2DP_MEDIA_CT_NON_A2DP); + codec_type = A2DP_GetCodecType(codec_info_non_a2dp); EXPECT_EQ(codec_type, A2DP_MEDIA_CT_NON_A2DP); } @@ -443,6 +515,9 @@ TEST_F(StackA2dpTest, test_a2dp_uses_rtp_header) { EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_aac)); EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_aac)); + ASSERT_TRUE(A2DP_VendorUsesRtpHeader(true, codec_info_opus)); + ASSERT_TRUE(A2DP_VendorUsesRtpHeader(false, codec_info_opus)); + EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_non_a2dp)); EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_non_a2dp)); } @@ -473,6 +548,9 @@ TEST_F(StackA2dpTest, test_a2dp_codec_name) { EXPECT_STREQ(A2DP_CodecName(codec_info_aac), "AAC"); EXPECT_STREQ(A2DP_CodecName(codec_info_aac_capability), "AAC"); EXPECT_STREQ(A2DP_CodecName(codec_info_aac_sink_capability), "AAC"); + ASSERT_STREQ(A2DP_CodecName(codec_info_opus), "Opus"); + ASSERT_STREQ(A2DP_CodecName(codec_info_opus_capability), "Opus"); + ASSERT_STREQ(A2DP_CodecName(codec_info_opus_sink_capability), "Opus"); EXPECT_STREQ(A2DP_CodecName(codec_info_non_a2dp), "UNKNOWN VENDOR CODEC"); // Test all unknown codecs @@ -496,11 +574,19 @@ TEST_F(StackA2dpTest, test_a2dp_codec_type_equals) { EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_capability)); EXPECT_TRUE( A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_sink_capability)); + EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_capability)); EXPECT_TRUE( A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_sink_capability)); + + ASSERT_TRUE( + A2DP_VendorCodecTypeEquals(codec_info_opus, codec_info_opus_capability)); + ASSERT_TRUE(A2DP_VendorCodecTypeEquals(codec_info_opus, + codec_info_opus_sink_capability)); + EXPECT_TRUE( A2DP_CodecTypeEquals(codec_info_non_a2dp, codec_info_non_a2dp_fake)); + EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_non_a2dp)); EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_non_a2dp)); EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_aac)); @@ -509,6 +595,7 @@ TEST_F(StackA2dpTest, test_a2dp_codec_type_equals) { TEST_F(StackA2dpTest, test_a2dp_codec_equals) { uint8_t codec_info_sbc_test[AVDT_CODEC_SIZE]; uint8_t codec_info_aac_test[AVDT_CODEC_SIZE]; + uint8_t codec_info_opus_test[AVDT_CODEC_SIZE]; uint8_t codec_info_non_a2dp_test[AVDT_CODEC_SIZE]; // Test two identical SBC codecs @@ -521,6 +608,11 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) { memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac)); EXPECT_TRUE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test)); + // Test two identical Opus codecs + memset(codec_info_opus_test, 0xAB, sizeof(codec_info_opus_test)); + memcpy(codec_info_opus_test, codec_info_opus, sizeof(codec_info_opus)); + ASSERT_TRUE(A2DP_VendorCodecEquals(codec_info_opus, codec_info_opus_test)); + // Test two identical non-A2DP codecs that are not recognized memset(codec_info_non_a2dp_test, 0xAB, sizeof(codec_info_non_a2dp_test)); memcpy(codec_info_non_a2dp_test, codec_info_non_a2dp, @@ -529,7 +621,8 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) { // Test two codecs that have different types EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_non_a2dp)); - EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_aac)); + ASSERT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_aac)); + ASSERT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_opus)); // Test two SBC codecs that are slightly different memset(codec_info_sbc_test, 0xAB, sizeof(codec_info_sbc_test)); @@ -567,12 +660,14 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) { TEST_F(StackA2dpTest, test_a2dp_get_track_sample_rate) { EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_sbc), 44100); EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_aac), 44100); + ASSERT_EQ(A2DP_VendorGetTrackSampleRate(codec_info_opus), 48000); EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_non_a2dp), -1); } TEST_F(StackA2dpTest, test_a2dp_get_track_channel_count) { EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_sbc), 2); EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_aac), 2); + ASSERT_EQ(A2DP_VendorGetTrackChannelCount(codec_info_opus), 2); EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_non_a2dp), -1); } @@ -625,6 +720,7 @@ TEST_F(StackA2dpTest, test_a2dp_get_max_bitpool_sbc) { TEST_F(StackA2dpTest, test_a2dp_get_sink_track_channel_type) { EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_sbc), 3); EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_aac), 3); + ASSERT_EQ(A2DP_VendorGetSinkTrackChannelType(codec_info_opus), 2); EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_non_a2dp), -1); } @@ -669,6 +765,13 @@ TEST_F(StackA2dpTest, test_a2dp_get_packet_timestamp) { EXPECT_TRUE(A2DP_GetPacketTimestamp(codec_info_aac, a2dp_data, ×tamp)); EXPECT_EQ(timestamp, static_cast<uint32_t>(0x12345678)); + memset(a2dp_data, 0xAB, sizeof(a2dp_data)); + *p_ts = 0x12345678; + timestamp = 0xFFFFFFFF; + ASSERT_TRUE( + A2DP_VendorGetPacketTimestamp(codec_info_opus, a2dp_data, ×tamp)); + ASSERT_EQ(timestamp, static_cast<uint32_t>(0x12345678)); + memset(a2dp_data, 0xAB, sizeof(a2dp_data)); *p_ts = 0x12345678; timestamp = 0xFFFFFFFF; @@ -761,6 +864,12 @@ TEST_F(StackA2dpTest, test_a2dp_source_codec_index) { BTAV_A2DP_CODEC_INDEX_SOURCE_AAC); EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_aac_sink_capability), BTAV_A2DP_CODEC_INDEX_SOURCE_AAC); + ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus), + BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS); + ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus_capability), + BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS); + ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus_sink_capability), + BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS); EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_non_a2dp), BTAV_A2DP_CODEC_INDEX_MAX); } @@ -779,6 +888,12 @@ TEST_F(StackA2dpTest, test_a2dp_sink_codec_index) { BTAV_A2DP_CODEC_INDEX_SINK_AAC); EXPECT_EQ(A2DP_SinkCodecIndex(codec_info_aac_sink_capability), BTAV_A2DP_CODEC_INDEX_SINK_AAC); + ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus), + BTAV_A2DP_CODEC_INDEX_SINK_OPUS); + ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus_capability), + BTAV_A2DP_CODEC_INDEX_SINK_OPUS); + ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus_sink_capability), + BTAV_A2DP_CODEC_INDEX_SINK_OPUS); EXPECT_EQ(A2DP_SinkCodecIndex(codec_info_non_a2dp), BTAV_A2DP_CODEC_INDEX_MAX); } @@ -788,6 +903,10 @@ TEST_F(StackA2dpTest, test_a2dp_codec_index_str) { EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC), "SBC"); EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_SBC), "SBC SINK"); EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC), "AAC"); + ASSERT_STREQ(A2DP_VendorCodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS), + "Opus"); + ASSERT_STREQ(A2DP_VendorCodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_OPUS), + "Opus SINK"); // Test that the unknown codec string has not changed EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_MAX), diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp index 92c6302ccf89993d4caa5ec5508edd1cce9b5b0e..5e009487acf3b7d248eb7b62dde2ad7381f75c06 100644 --- a/system/test/headless/Android.bp +++ b/system/test/headless/Android.bp @@ -66,6 +66,7 @@ cc_test { "libFraunhoferAAC", "libg722codec", "liblc3", + "libopus", "libosi", "libprotobuf-cpp-lite", "libudrv-uipc", diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp index c39b1eb204820c0698b4d1cde5845cbdec633c7f..30f6f20c0bce72878b314eca0e3f2421d3aeaf2a 100644 --- a/system/test/suite/Android.bp +++ b/system/test/suite/Android.bp @@ -90,6 +90,7 @@ cc_defaults { "libg722codec", "libgmock", "liblc3", + "libopus", "libosi", "libstatslog_bt", "libc++fs",