Skip to content
This repository has been archived by the owner on Jan 25, 2021. It is now read-only.

Commit

Permalink
Merge pull request #49 from EHfive/ldac-slice-rc
Browse files Browse the repository at this point in the history
Improve LDAC ABR performance
  • Loading branch information
EHfive authored Apr 7, 2019
2 parents bff80fd + 487f7ba commit d5b87e8
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 24 deletions.
5 changes: 5 additions & 0 deletions src/modules/bluetooth/a2dp/a2dp-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,17 @@ typedef struct pa_a2dp_source {

void (*get_block_size)(size_t write_link_mtu, size_t *write_block_size, void **codec_data);

size_t (*handle_update_buffer_size)(void **codec_data);

void (*setup_stream)(void **codec_data);

/* Pass read_cb_data to pa_a2dp_source_read_cb, pa_a2dp_source_read_buf_free_cb */
size_t (*encode)(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *encoded,
void *read_cb_data, void **codec_data);

/* Optional, return size of bytes to skip */
size_t (*handle_skipping)(size_t bytes_to_send, void **codec_data);

/* Optional */
void (*set_tx_length)(size_t len, void **codec_data);

Expand Down
91 changes: 74 additions & 17 deletions src/modules/bluetooth/a2dp/a2dp_ldac.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <arpa/inet.h>
#include <string.h>
#include <errno.h>

#ifdef HAVE_CONFIG_H

Expand All @@ -29,6 +30,7 @@

#include <pulse/xmalloc.h>
#include <pulsecore/once.h>
#include <pulsecore/sample-util.h>

#include <ldacBT.h>
#include <ldacBT_abr.h>
Expand All @@ -39,11 +41,12 @@

#define streq(a, b) (!strcmp((a),(b)))

#define AVDT_MEDIA_HDR_SIZE 12
#define LDAC_ABR_THRESHOLD_CRITICAL 5
#define LDAC_ABR_THRESHOLD_DANGEROUSTREND 3
#define LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ 1

#define LDAC_ABR_THRESHOLD_CRITICAL 6
#define LDAC_ABR_THRESHOLD_DANGEROUSTREND 4
#define LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ 2

#define LDAC_ABR_INTERVAL_MS 5


typedef struct ldac_info {
Expand Down Expand Up @@ -74,6 +77,12 @@ typedef struct ldac_info {
uint32_t written;
size_t tx_length;

size_t interval_bytes;
size_t read_bytes;

uint8_t buf_index;
void *buf;

size_t mtu;

} ldac_info_t;
Expand Down Expand Up @@ -189,14 +198,33 @@ pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_
pa_assert(ldac_info);
pa_assert(ldac_info->hLdacBt);

if(PA_UNLIKELY(ldac_info->buf != write_buf && ldac_info->buf)){
int ret;
ldac_info->buf_index = 0;
ldacBT_close_handle_func(ldac_info->hLdacBt);
ret = ldacBT_init_handle_encode_func(ldac_info->hLdacBt,
(int) ldac_info->mtu,
ldac_info->eqmid,
ldac_info->channel_mode,
ldac_info->pcm_fmt,
ldac_info->pcm_frequency);
if (ret != 0) {
pa_log_warn("Failed to init ldacBT handle");
return 0;
}
}

if (ldac_info->hLdacAbr && ldac_info->enable_abr) {
if (!ldac_info->buf_index && ldac_info->hLdacAbr && ldac_info->enable_abr &&
ldac_info->read_bytes >= ldac_info->interval_bytes) {
ldac_ABR_Proc_func(ldac_info->hLdacBt, ldac_info->hLdacAbr,
(unsigned int) (ldac_info->tx_length / ldac_info->q_write_block_size),
(unsigned int) ldac_info->enable_abr);
(unsigned int) (ldac_info->tx_length / ldac_info->q_write_block_size),
(unsigned int) ldac_info->enable_abr);
ldac_info->tx_length = 0;
ldac_info->read_bytes = 0;
}

ldac_info->buf = write_buf;


ldac_enc_read = (pa_frame_size(&ldac_info->sample_spec) * LDACBT_ENC_LSU);

Expand All @@ -205,12 +233,10 @@ pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_

frame_count = 0;

/* MAX pcm size for 1 LDAC packet (LDAC MQ) */
to_encode = (ldac_info->mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
/ 110 * ldac_info->pcm_read_size;
to_encode = ldac_info->q_write_block_size;

d = (uint8_t *) write_buf + sizeof(*header) + sizeof(*payload);
to_write = write_buf_size - sizeof(*header) - sizeof(*payload);
d = (uint8_t *) write_buf + sizeof(*header) + sizeof(*payload) + ldac_info->buf_index;
to_write = write_buf_size - sizeof(*header) - sizeof(*payload) - ldac_info->buf_index;

*_encoded = 0;
while (PA_LIKELY(to_encode > 0 && to_write > 0 && frame_count == 0)) {
Expand Down Expand Up @@ -242,12 +268,15 @@ pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_
to_encode -= encoded;

d = (uint8_t *) d + written;
ldac_info->buf_index += written;
to_write -= written;

frame_count += ldac_frame_num;

}

ldac_info->read_bytes += *_encoded;


PA_ONCE_BEGIN
{
Expand All @@ -260,6 +289,10 @@ pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_
}
PA_ONCE_END;

if(frame_count == 0)
return -EINPROGRESS;
ldac_info->buf_index = 0;

/* write it to the fifo */
memset(write_buf, 0, sizeof(*header) + sizeof(*payload));
header->v = 2;
Expand Down Expand Up @@ -397,9 +430,16 @@ pa_ldac_config_transport(pa_sample_spec default_sample_spec, const void *configu

ldac_info->sample_spec = *sample_spec;
ldac_info->pcm_read_size = (ldac_info->pcm_lsu * pa_frame_size(&ldac_info->sample_spec));
ldac_info->interval_bytes = pa_usec_to_bytes(LDAC_ABR_INTERVAL_MS * 1000, &ldac_info->sample_spec);

};

static size_t pa_ldac_handle_update_buffer_size(void **codec_data) {
ldac_info_t *ldac_info = *codec_data;
pa_assert(ldac_info);
return ldac_info->q_write_block_size * ldac_info->abr_t3;
}

static void pa_ldac_get_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) {
ldac_info_t *ldac_info = *codec_data;
pa_assert(ldac_info);
Expand All @@ -408,7 +448,7 @@ static void pa_ldac_get_block_size(size_t write_link_mtu, size_t *write_block_si

ldac_info->q_write_block_size = ((write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
/ ldac_info->ldac_frame_size * ldac_info->pcm_read_size);
*write_block_size = LDACBT_MAX_LSU * pa_frame_size(&ldac_info->sample_spec);
*write_block_size = ldac_info->q_write_block_size;
};


Expand All @@ -419,13 +459,15 @@ static void pa_ldac_setup_stream(void **codec_data) {

ldac_info->layer_specific = 0;
ldac_info->written = 0;
ldac_info->buf = NULL;
ldac_info->buf_index = 0;
if (ldac_info->hLdacBt)
ldacBT_free_handle_func(ldac_info->hLdacBt);
ldac_info->hLdacBt = ldacBT_get_handle_func();


ret = ldacBT_init_handle_encode_func(ldac_info->hLdacBt,
(int) ldac_info->mtu + AVDT_MEDIA_HDR_SIZE,
(int) ldac_info->mtu,
ldac_info->eqmid,
ldac_info->channel_mode,
ldac_info->pcm_fmt,
Expand All @@ -442,8 +484,7 @@ static void pa_ldac_setup_stream(void **codec_data) {
ldac_ABR_free_handle_func(ldac_info->hLdacAbr);
ldac_info->hLdacAbr = ldac_ABR_get_handle_func();

ret = ldac_ABR_Init_func(ldac_info->hLdacAbr,
(unsigned int) pa_bytes_to_usec(ldac_info->q_write_block_size, &ldac_info->sample_spec) / 1000);
ret = ldac_ABR_Init_func(ldac_info->hLdacAbr, LDAC_ABR_INTERVAL_MS);
if (ret != 0) {
pa_log_warn("Failed to init ldacBT_ABR handle");
goto fail1;
Expand All @@ -463,10 +504,24 @@ static void pa_ldac_setup_stream(void **codec_data) {
ldac_info->enable_abr = false;
};

static size_t pa_ldac_handle_skipping(size_t bytes_to_send, void **codec_data) {
ldac_info_t *info = *codec_data;
size_t skip_bytes;
pa_assert(info);
skip_bytes = pa_frame_align(bytes_to_send - ((bytes_to_send / 2) % info->q_write_block_size),
&info->sample_spec);
if(!info->enable_abr){
if(bytes_to_send > 2 * info->q_write_block_size)
return skip_bytes;
} else if (bytes_to_send / info->q_write_block_size > info->abr_t3)
return skip_bytes;
return 0;
}

static void pa_ldac_set_tx_length(size_t len, void **codec_data) {
ldac_info_t *ldac_info = *codec_data;
pa_assert(ldac_info);
ldac_info->tx_length += len;
ldac_info->tx_length += PA_MAX(ldac_info->tx_length, len);
};

static void pa_ldac_free(void **codec_data) {
Expand Down Expand Up @@ -598,8 +653,10 @@ static pa_a2dp_source_t pa_ldac_source = {
.update_user_config = pa_ldac_update_user_config,
.encode = pa_ldac_encode,
.config_transport = pa_ldac_config_transport,
.handle_update_buffer_size = pa_ldac_handle_update_buffer_size,
.get_block_size = pa_ldac_get_block_size,
.setup_stream = pa_ldac_setup_stream,
.handle_skipping = pa_ldac_handle_skipping,
.set_tx_length = pa_ldac_set_tx_length,
.decrease_quality = NULL,
.free = pa_ldac_free
Expand Down
29 changes: 22 additions & 7 deletions src/modules/bluetooth/module-bluez5-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ static int a2dp_process_render(struct userdata *u) {
&encoded, u, &u->a2dp_info.a2dp_source_data);
if(nbytes == 0)
return -1;
else if(nbytes == -EINPROGRESS){
u->write_index += encoded;
return 1;
}


for (;;) {
ssize_t l;
Expand Down Expand Up @@ -611,9 +616,12 @@ static void update_buffer_size(struct userdata *u) {
* there should at least be room for two chunks in the buffer. Generally, write_block_size
* is larger than 512. If not, use the next multiple of write_block_size which is larger
* than 1024. */
new_bufsize = 2 * u->write_block_size;
if (u->transport->a2dp_source && u->transport->a2dp_source->handle_update_buffer_size)
new_bufsize = (int) u->transport->a2dp_source->handle_update_buffer_size(&u->a2dp_info.a2dp_source_data);
else
new_bufsize = (int) (2 * u->write_block_size);
if (new_bufsize < 1024)
new_bufsize = (1024 / u->write_block_size + 1) * u->write_block_size;
new_bufsize = (int) ((1024 / u->write_block_size + 1) * u->write_block_size);

/* The kernel internally doubles the buffer size that was set by setsockopt and getsockopt
* returns the doubled value. */
Expand Down Expand Up @@ -1450,7 +1458,8 @@ static void thread_func(void *userdata) {
/* A new block needs to be sent. */
if (audio_sent <= time_passed) {
size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->sample_spec);

uint64_t skip_bytes;
bool flag = false;
if (u->transport->a2dp_source && u->transport->a2dp_source->set_tx_length){
u->transport->a2dp_source->set_tx_length(bytes_to_send, &u->a2dp_info.a2dp_source_data);
u->transport->a2dp_source->get_block_size(u->write_link_mtu, &u->write_block_size, &u->a2dp_info.a2dp_source_data);
Expand All @@ -1460,14 +1469,20 @@ static void thread_func(void *userdata) {
* the socket has not been accepting data fast enough (could be due to
* hiccups in the wireless transmission). We need to discard everything
* older than two block sizes to keep the latency from growing. */
if (bytes_to_send > 2 * u->write_block_size) {
uint64_t skip_bytes;
if (u->transport->a2dp_source && (flag = u->transport->a2dp_source->handle_skipping)) {
skip_bytes = u->transport->a2dp_source->handle_skipping(bytes_to_send,
&u->a2dp_info.a2dp_source_data);
} else
skip_bytes = pa_frame_align(bytes_to_send - ((bytes_to_send / 2) % u->write_block_size),
&u->sample_spec);

if ((flag && skip_bytes > 0) || (!flag && bytes_to_send > 2 * u->write_block_size)) {

pa_memchunk tmp;
size_t mempool_max_block_size = pa_mempool_block_size_max(u->core->mempool);
pa_usec_t skip_usec;

skip_bytes = pa_frame_align(bytes_to_send - ((bytes_to_send / 2) % bytes_to_send),
&u->sample_spec);

skip_usec = pa_bytes_to_usec(skip_bytes, &u->sample_spec);

pa_log_debug("Skipping %llu us (= %llu bytes) in audio stream",
Expand Down

0 comments on commit d5b87e8

Please sign in to comment.