summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/bluetooth/bt-codec-msbc.c80
1 files changed, 47 insertions, 33 deletions
diff --git a/src/modules/bluetooth/bt-codec-msbc.c b/src/modules/bluetooth/bt-codec-msbc.c
index 7df837e76..7a26e3c61 100644
--- a/src/modules/bluetooth/bt-codec-msbc.c
+++ b/src/modules/bluetooth/bt-codec-msbc.c
@@ -112,7 +112,15 @@ static size_t get_read_block_size(void *codec_info, size_t link_mtu) {
block_size = pa_frame_align(block_size, &info->sample_spec);
}
- return block_size;
+ /* If MTU exceeds mSBC frame size there could be up to 1 + MTU / (mSBC frame size)
+ * frames decoded for single incoming packet.
+ * See also pa_bluetooth_transport::last_read_size handling
+ * and comment about MTU size in bt_prepare_encoder_buffer()
+ */
+ if (link_mtu <= MSBC_PACKET_SIZE)
+ return block_size;
+
+ return block_size * (1 + link_mtu / MSBC_PACKET_SIZE);
}
static size_t get_write_block_size(void *codec_info, size_t link_mtu) {
@@ -204,10 +212,10 @@ static inline bool is_all_zero(const uint8_t *ptr, size_t len) {
/*
* We build a msbc frame up in the sbc_info buffer until we have a whole one
*/
-static struct msbc_frame *msbc_find_frame(struct sbc_info *si, ssize_t *len,
+static struct msbc_frame *msbc_find_frame(struct sbc_info *si, size_t *len,
const uint8_t *buf, int *pseq)
{
- int i;
+ size_t i;
uint8_t *p = si->input_buffer;
/* skip input if it has all zero bytes
@@ -241,7 +249,7 @@ static struct msbc_frame *msbc_find_frame(struct sbc_info *si, ssize_t *len,
id1.b = p[1];
*pseq = (id1.s.sn0 & 0x1) | (id1.s.sn1 & 0x2);
si->msbc_push_offset = 0;
- *len = *len - i;
+ *len -= i + 1;
return (struct msbc_frame *)p;
}
continue;
@@ -255,49 +263,55 @@ static struct msbc_frame *msbc_find_frame(struct sbc_info *si, ssize_t *len,
static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) {
struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
- ssize_t remaining;
+ size_t save_input_size;
ssize_t decoded;
size_t written = 0;
+ size_t total_written = 0;
+ size_t total_processed = 0;
struct msbc_frame *frame;
int seq;
- remaining = input_size;
- frame = msbc_find_frame(sbc_info, &remaining, input_buffer, &seq);
+ while (input_size > 0) {
- /* only process when we have a full frame */
- if (!frame) {
- *processed = input_size - remaining;
- return 0;
- }
+ save_input_size = input_size;
+ frame = msbc_find_frame(sbc_info, &input_size, input_buffer + total_processed, &seq);
- uint8_t lost_packets = (4 + seq - sbc_info->msbc_seq++) % 4;
+ total_processed += save_input_size - input_size;
- if (lost_packets) {
- pa_log_debug("Lost %d input audio packet(s)", lost_packets);
- sbc_info->msbc_seq = seq + 1;
- }
+ /* Only full mSBC frame should be decoded */
+ if (!frame)
+ break;
+
+ uint8_t lost_packets = (4 + seq - sbc_info->msbc_seq++) % 4;
- decoded = sbc_decode(&sbc_info->sbc, frame->payload, MSBC_FRAME_SIZE, output_buffer, output_size, &written);
+ if (lost_packets) {
+ pa_log_debug("Lost %d input audio packet(s)", lost_packets);
+ sbc_info->msbc_seq = seq + 1;
+ }
- /* now we've consumed the sbc_info buffer, start a new one with
- * the partial frame we have */
- if (remaining > 0)
- msbc_find_frame(sbc_info, &remaining, input_buffer + input_size - remaining, &seq);
+ /* pa_bt_codec::get_read_block_size must provide space for all decoded frames */
+ pa_assert_fp(output_size >= sbc_info->codesize);
- pa_assert_fp(remaining == 0);
+ decoded = sbc_decode(&sbc_info->sbc, frame->payload, MSBC_FRAME_SIZE, output_buffer, output_size, &written);
- if (PA_UNLIKELY(decoded <= 0)) {
- pa_log_error("mSBC decoding error (%li)", (long) decoded);
- pa_silence_memory(output_buffer, sbc_info->codesize, &sbc_info->sample_spec);
- decoded = sbc_info->frame_length;
- written = sbc_info->codesize;
- }
+ if (PA_UNLIKELY(decoded <= 0)) {
+ pa_log_error("mSBC decoding error (%li)", (long) decoded);
+ pa_silence_memory(output_buffer, sbc_info->codesize, &sbc_info->sample_spec);
+ decoded = sbc_info->frame_length;
+ written = sbc_info->codesize;
+ }
- pa_assert_fp((size_t)decoded == sbc_info->frame_length);
- pa_assert_fp((size_t)written == sbc_info->codesize);
+ pa_assert_fp((size_t)decoded == sbc_info->frame_length);
+ pa_assert_fp((size_t)written == sbc_info->codesize);
+
+ output_buffer += written;
+ output_size -= written;
+
+ total_written += written;
+ }
- *processed = input_size - remaining;
- return written;
+ *processed = total_processed;
+ return total_written;
}
/* Modified SBC codec for HFP Wideband Speech*/