/* Reset internal state of codec info data in codec_info */
void (*reset)(void *codec_info);
- /* Get read block size for codec */
+ /* Get read block size for codec, it is minimal size of buffer
+ * needed to decode read_link_mtu bytes of encoded data */
size_t (*get_read_block_size)(void *codec_info, size_t read_link_mtu);
- /* Get write block size for codec */
+ /* Get write block size for codec, it is maximal size of buffer
+ * which can produce at most write_link_mtu bytes of encoded data */
size_t (*get_write_block_size)(void *codec_info, size_t write_link_mtu);
/* Reduce encoder bitrate for codec, returns new write block size or zero
sbc_info->min_bitpool = config->min_bitpool;
sbc_info->max_bitpool = config->max_bitpool;
- /* Set minimum bitpool for source to get the maximum possible block_size */
+ /* Set minimum bitpool for source to get the maximum possible block_size
+ * in get_block_size() function. This block_size is length of buffer used
+ * for decoded audio data and so is inversely proportional to frame length
+ * which depends on bitpool value. Bitpool is controlled by other side from
+ * range [min_bitpool, max_bitpool]. */
sbc_info->initial_bitpool = for_encoding ? sbc_info->max_bitpool : sbc_info->min_bitpool;
set_params(sbc_info);
static size_t get_block_size(void *codec_info, size_t link_mtu) {
struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+ size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ size_t frame_count = (link_mtu - rtp_size) / sbc_info->frame_length;
- return (link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
- / sbc_info->frame_length * sbc_info->codesize;
+ return frame_count * sbc_info->codesize;
}
static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {
if (PA_UNLIKELY(encoded <= 0)) {
pa_log_error("SBC encoding error (%li)", (long) encoded);
- *processed = p - input_buffer;
- return 0;
+ break;
+ }
+
+ if (PA_UNLIKELY(written < 0)) {
+ pa_log_error("SBC encoding error (%li)", (long) written);
+ break;
}
pa_assert_fp((size_t) encoded <= to_encode);
pa_log_debug("Using SBC codec implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc)));
} PA_ONCE_END;
+ if (PA_UNLIKELY(frame_count == 0)) {
+ *processed = 0;
+ return 0;
+ }
+
/* write it to the fifo */
memset(output_buffer, 0, sizeof(*header) + sizeof(*payload));
header->v = 2;
d = output_buffer;
to_write = output_size;
- while (PA_LIKELY(to_decode > 0)) {
+ while (PA_LIKELY(to_decode > 0 && to_write > 0)) {
size_t written;
ssize_t decoded;
if (PA_UNLIKELY(decoded <= 0)) {
pa_log_error("SBC decoding error (%li)", (long) decoded);
- *processed = p - input_buffer;
- return 0;
+ break;
}
/* Reset frame length, it can be changed due to bitpool change */
pa_assert_fp((size_t) decoded <= to_decode);
pa_assert_fp((size_t) decoded == sbc_info->frame_length);
+ pa_assert_fp((size_t) written <= to_write);
pa_assert_fp((size_t) written == sbc_info->codesize);
p += decoded;
static void a2dp_prepare_encoder_buffer(struct userdata *u) {
pa_assert(u);
- if (u->encoder_buffer_size >= u->write_link_mtu)
- return;
+ if (u->encoder_buffer_size < u->write_link_mtu) {
+ pa_xfree(u->encoder_buffer);
+ u->encoder_buffer = pa_xmalloc(u->write_link_mtu);
+ }
- u->encoder_buffer_size = 2 * u->write_link_mtu;
- pa_xfree(u->encoder_buffer);
- u->encoder_buffer = pa_xmalloc(u->encoder_buffer_size);
+ /* Encoder buffer cannot be larger then link MTU, otherwise
+ * encode method would produce larger packets then link MTU */
+ u->encoder_buffer_size = u->write_link_mtu;
}
/* Run from IO thread */
static void a2dp_prepare_decoder_buffer(struct userdata *u) {
pa_assert(u);
- if (u->decoder_buffer_size >= u->read_link_mtu)
- return;
+ if (u->decoder_buffer_size < u->read_link_mtu) {
+ pa_xfree(u->decoder_buffer);
+ u->decoder_buffer = pa_xmalloc(u->read_link_mtu);
+ }
- u->decoder_buffer_size = 2 * u->read_link_mtu;
- pa_xfree(u->decoder_buffer);
- u->decoder_buffer = pa_xmalloc(u->decoder_buffer_size);
+ /* Decoder buffer cannot be larger then link MTU, otherwise
+ * decode method would produce larger output then read_block_size */
+ u->decoder_buffer_size = u->read_link_mtu;
}
/* Run from IO thread */
static int a2dp_write_buffer(struct userdata *u, size_t nbytes) {
int ret = 0;
+ /* Encoder function of A2DP codec may provide empty buffer, in this case do
+ * not post any empty buffer via A2DP socket. It may be because of codec
+ * internal state, e.g. encoder is waiting for more samples so it can
+ * provide encoded data. */
+ if (PA_UNLIKELY(!nbytes)) {
+ u->write_index += (uint64_t) u->write_memchunk.length;
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ return 0;
+ }
+
for (;;) {
ssize_t l;
pa_memblock_release(u->write_memchunk.memblock);
- if (length == 0)
+ if (processed != u->write_memchunk.length) {
+ pa_log_error("Encoding error");
return -1;
-
- pa_assert(processed == u->write_memchunk.length);
+ }
return a2dp_write_buffer(u, length);
}
memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
memchunk.index = memchunk.length = 0;
+ a2dp_prepare_decoder_buffer(u);
+
for (;;) {
bool found_tstamp = false;
pa_usec_t tstamp;
ssize_t l;
size_t processed;
- a2dp_prepare_decoder_buffer(u);
-
l = pa_read(u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &u->stream_write_type);
if (l <= 0) {
memchunk.length = pa_memblock_get_length(memchunk.memblock);
memchunk.length = u->a2dp_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed);
- if (memchunk.length == 0) {
- pa_memblock_release(memchunk.memblock);
- ret = 0;
+
+ pa_memblock_release(memchunk.memblock);
+
+ if (processed != (size_t) l) {
+ pa_log_error("Decoding error");
+ ret = -1;
break;
}
pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
pa_smoother_resume(u->read_smoother, tstamp, true);
- pa_memblock_release(memchunk.memblock);
-
- pa_source_post(u->source, &memchunk);
+ /* Decoding of A2DP codec data may result in empty buffer, in this case
+ * do not post empty audio samples. It may happen due to algorithmic
+ * delay of audio codec. */
+ if (PA_LIKELY(memchunk.length))
+ pa_source_post(u->source, &memchunk);
ret = l;
break;