| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- // SPDX-License-Identifier: Apache-2.0
- /*
- * Copyright (C) 2013 Intel Corporation
- *
- */
- #define _GNU_SOURCE
- #include <stdbool.h>
- #include <string.h>
- #include <malloc.h>
- #include <stdlib.h>
- #include <sbc/sbc.h>
- #include "audio-msg.h"
- #include "hal-audio.h"
- #include "hal-log.h"
- #include "../profiles/audio/a2dp-codecs.h"
- #define MAX_FRAMES_IN_PAYLOAD 15
- #define SBC_QUALITY_MIN_BITPOOL 33
- #define SBC_QUALITY_STEP 5
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- struct rtp_payload {
- unsigned frame_count:4;
- unsigned rfa0:1;
- unsigned is_last_fragment:1;
- unsigned is_first_fragment:1;
- unsigned is_fragmented:1;
- } __attribute__ ((packed));
- #elif __BYTE_ORDER == __BIG_ENDIAN
- struct rtp_payload {
- unsigned is_fragmented:1;
- unsigned is_first_fragment:1;
- unsigned is_last_fragment:1;
- unsigned rfa0:1;
- unsigned frame_count:4;
- } __attribute__ ((packed));
- #else
- #error "Unknown byte order"
- #endif
- struct media_packet_sbc {
- struct media_packet_rtp hdr;
- struct rtp_payload payload;
- uint8_t data[0];
- };
- struct sbc_data {
- a2dp_sbc_t sbc;
- sbc_t enc;
- uint16_t payload_len;
- size_t in_frame_len;
- size_t in_buf_size;
- size_t out_frame_len;
- unsigned frame_duration;
- unsigned frames_per_packet;
- };
- static const a2dp_sbc_t sbc_presets[] = {
- {
- .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
- .channel_mode = SBC_CHANNEL_MODE_MONO |
- SBC_CHANNEL_MODE_DUAL_CHANNEL |
- SBC_CHANNEL_MODE_STEREO |
- SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_SNR |
- SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
- SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
- .min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
- },
- {
- .frequency = SBC_SAMPLING_FREQ_44100,
- .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
- },
- {
- .frequency = SBC_SAMPLING_FREQ_48000,
- .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_48000,
- },
- };
- static int sbc_get_presets(struct audio_preset *preset, size_t *len)
- {
- int i;
- int count;
- size_t new_len = 0;
- uint8_t *ptr = (uint8_t *) preset;
- size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
- count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
- for (i = 0; i < count; i++) {
- preset = (struct audio_preset *) ptr;
- if (new_len + preset_size > *len)
- break;
- preset->len = sizeof(a2dp_sbc_t);
- memcpy(preset->data, &sbc_presets[i], preset->len);
- new_len += preset_size;
- ptr += preset_size;
- }
- *len = new_len;
- return i;
- }
- static int sbc_freq2int(uint8_t freq)
- {
- switch (freq) {
- case SBC_SAMPLING_FREQ_16000:
- return 16000;
- case SBC_SAMPLING_FREQ_32000:
- return 32000;
- case SBC_SAMPLING_FREQ_44100:
- return 44100;
- case SBC_SAMPLING_FREQ_48000:
- return 48000;
- default:
- return 0;
- }
- }
- static const char *sbc_mode2str(uint8_t mode)
- {
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- return "Mono";
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return "DualChannel";
- case SBC_CHANNEL_MODE_STEREO:
- return "Stereo";
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return "JointStereo";
- default:
- return "(unknown)";
- }
- }
- static int sbc_blocks2int(uint8_t blocks)
- {
- switch (blocks) {
- case SBC_BLOCK_LENGTH_4:
- return 4;
- case SBC_BLOCK_LENGTH_8:
- return 8;
- case SBC_BLOCK_LENGTH_12:
- return 12;
- case SBC_BLOCK_LENGTH_16:
- return 16;
- default:
- return 0;
- }
- }
- static int sbc_subbands2int(uint8_t subbands)
- {
- switch (subbands) {
- case SBC_SUBBANDS_4:
- return 4;
- case SBC_SUBBANDS_8:
- return 8;
- default:
- return 0;
- }
- }
- static const char *sbc_allocation2str(uint8_t allocation)
- {
- switch (allocation) {
- case SBC_ALLOCATION_SNR:
- return "SNR";
- case SBC_ALLOCATION_LOUDNESS:
- return "Loudness";
- default:
- return "(unknown)";
- }
- }
- static void sbc_init_encoder(struct sbc_data *sbc_data)
- {
- a2dp_sbc_t *in = &sbc_data->sbc;
- sbc_t *out = &sbc_data->enc;
- sbc_init_a2dp(out, 0L, in, sizeof(*in));
- out->endian = SBC_LE;
- out->bitpool = in->max_bitpool;
- DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
- sbc_freq2int(in->frequency),
- sbc_mode2str(in->channel_mode),
- sbc_blocks2int(in->block_length),
- sbc_subbands2int(in->subbands),
- sbc_allocation2str(in->allocation_method),
- in->min_bitpool, in->max_bitpool);
- }
- static void sbc_codec_calculate(struct sbc_data *sbc_data)
- {
- size_t in_frame_len;
- size_t out_frame_len;
- size_t num_frames;
- in_frame_len = sbc_get_codesize(&sbc_data->enc);
- out_frame_len = sbc_get_frame_length(&sbc_data->enc);
- num_frames = sbc_data->payload_len / out_frame_len;
- if (num_frames > MAX_FRAMES_IN_PAYLOAD)
- num_frames = MAX_FRAMES_IN_PAYLOAD;
- sbc_data->in_frame_len = in_frame_len;
- sbc_data->in_buf_size = num_frames * in_frame_len;
- sbc_data->out_frame_len = out_frame_len;
- sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
- sbc_data->frames_per_packet = num_frames;
- DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
- in_frame_len, out_frame_len, num_frames);
- }
- static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
- void **codec_data)
- {
- struct sbc_data *sbc_data;
- if (preset->len != sizeof(a2dp_sbc_t)) {
- error("SBC: preset size mismatch");
- return false;
- }
- sbc_data = calloc(sizeof(struct sbc_data), 1);
- if (!sbc_data)
- return false;
- memcpy(&sbc_data->sbc, preset->data, preset->len);
- sbc_init_encoder(sbc_data);
- sbc_data->payload_len = payload_len;
- sbc_codec_calculate(sbc_data);
- *codec_data = sbc_data;
- return true;
- }
- static bool sbc_cleanup(void *codec_data)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- sbc_finish(&sbc_data->enc);
- free(codec_data);
- return true;
- }
- static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- switch (sbc_data->sbc.frequency) {
- case SBC_SAMPLING_FREQ_16000:
- config->rate = 16000;
- break;
- case SBC_SAMPLING_FREQ_32000:
- config->rate = 32000;
- break;
- case SBC_SAMPLING_FREQ_44100:
- config->rate = 44100;
- break;
- case SBC_SAMPLING_FREQ_48000:
- config->rate = 48000;
- break;
- default:
- return false;
- }
- config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
- AUDIO_CHANNEL_OUT_MONO :
- AUDIO_CHANNEL_OUT_STEREO;
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- return true;
- }
- static size_t sbc_get_buffer_size(void *codec_data)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- return sbc_data->in_buf_size;
- }
- static size_t sbc_get_mediapacket_duration(void *codec_data)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- return sbc_data->frame_duration * sbc_data->frames_per_packet;
- }
- static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
- size_t len, struct media_packet *mp,
- size_t mp_data_len, size_t *written)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
- size_t consumed = 0;
- size_t encoded = 0;
- uint8_t frame_count = 0;
- mp_data_len -= sizeof(mp_sbc->payload);
- while (len - consumed >= sbc_data->in_frame_len &&
- mp_data_len - encoded >= sbc_data->out_frame_len &&
- frame_count < sbc_data->frames_per_packet) {
- ssize_t read;
- ssize_t written = 0;
- read = sbc_encode(&sbc_data->enc, buffer + consumed,
- sbc_data->in_frame_len, mp_sbc->data + encoded,
- mp_data_len - encoded, &written);
- if (read < 0) {
- error("SBC: failed to encode block at frame %d (%zd)",
- frame_count, read);
- break;
- }
- frame_count++;
- consumed += read;
- encoded += written;
- }
- *written = encoded + sizeof(mp_sbc->payload);
- mp_sbc->payload.frame_count = frame_count;
- return consumed;
- }
- static bool sbc_update_qos(void *codec_data, uint8_t op)
- {
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- uint8_t curr_bitpool = sbc_data->enc.bitpool;
- uint8_t new_bitpool = curr_bitpool;
- switch (op) {
- case QOS_POLICY_DEFAULT:
- new_bitpool = sbc_data->sbc.max_bitpool;
- break;
- case QOS_POLICY_DECREASE:
- if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
- new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
- if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
- new_bitpool = SBC_QUALITY_MIN_BITPOOL;
- }
- break;
- }
- if (new_bitpool == curr_bitpool)
- return false;
- sbc_data->enc.bitpool = new_bitpool;
- sbc_codec_calculate(sbc_data);
- info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
- return true;
- }
- static const struct audio_codec codec = {
- .type = A2DP_CODEC_SBC,
- .use_rtp = true,
- .get_presets = sbc_get_presets,
- .init = sbc_codec_init,
- .cleanup = sbc_cleanup,
- .get_config = sbc_get_config,
- .get_buffer_size = sbc_get_buffer_size,
- .get_mediapacket_duration = sbc_get_mediapacket_duration,
- .encode_mediapacket = sbc_encode_mediapacket,
- .update_qos = sbc_update_qos,
- };
- const struct audio_codec *codec_sbc(void)
- {
- return &codec;
- }
|