summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor V. Kovalenko <igor.v.kovalenko@gmail.com>2022-05-18 20:16:13 +0300
committerPulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org>2022-05-27 17:42:38 +0000
commit9916f0eace6ab1825af74a5f9b166918a06ce50e (patch)
tree5f3aec634fdde7a1f2644a501b6a5e9204076a0d
parent65889fbdee15c7a664670540f59e8f3c5c20c640 (diff)
downloadpulseaudio-9916f0eace6ab1825af74a5f9b166918a06ce50e.tar.gz
bluetooth: mSBC: Decode packets larger than mSBC frame
Bluetooth transport layer already allows for packets larger than mSBC frame, and there are up to 1 + MTU / (mSBC packet size) complete frames to be decoded from each incoming SCO packet. Now decoder fails when there is more than one complete frame available, which could happen if MTU size is larger than 1.5 * (mSBC packet size) = 90 Fix this by adding a loop over avialable frames, and adjust decoded buffer size to allow decoding up to 1 + MTU / (mSBC packet size) frames at once. Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/706>
-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*/