From 3902cee4a5bcbffd1e3f486bf127880e48949cc2 Mon Sep 17 00:00:00 2001 From: "Igor V. Kovalenko" Date: Wed, 3 Mar 2021 08:12:08 +0300 Subject: [PATCH] bluetooth: unify encoder code paths Part-of: --- src/modules/bluetooth/a2dp-codec-api.h | 3 + src/modules/bluetooth/a2dp-codec-aptx-gst.c | 18 + src/modules/bluetooth/a2dp-codec-ldac-gst.c | 8 + src/modules/bluetooth/a2dp-codec-sbc.c | 14 + src/modules/bluetooth/backend-native.c | 47 +++ src/modules/bluetooth/backend-ofono.c | 47 +++ src/modules/bluetooth/bluez5-util.c | 43 +++ src/modules/bluetooth/bluez5-util.h | 3 + src/modules/bluetooth/module-bluez5-device.c | 330 +++++++++---------- 9 files changed, 330 insertions(+), 183 deletions(-) diff --git a/src/modules/bluetooth/a2dp-codec-api.h b/src/modules/bluetooth/a2dp-codec-api.h index a90664e17..ed7bbe081 100644 --- a/src/modules/bluetooth/a2dp-codec-api.h +++ b/src/modules/bluetooth/a2dp-codec-api.h @@ -83,6 +83,9 @@ typedef struct pa_a2dp_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); + /* Get encoded block size for codec to hold one encoded frame. + * Note HFP mSBC codec encoded block may not fit into one MTU and is sent out in chunks. */ + size_t (*get_encoded_block_size)(void *codec_info, size_t input_size); /* Reduce encoder bitrate for codec, returns new write block size or zero * if not changed, called when socket is not accepting encoded data fast diff --git a/src/modules/bluetooth/a2dp-codec-aptx-gst.c b/src/modules/bluetooth/a2dp-codec-aptx-gst.c index f3d6cc76d..2e4afaacb 100644 --- a/src/modules/bluetooth/a2dp-codec-aptx-gst.c +++ b/src/modules/bluetooth/a2dp-codec-aptx-gst.c @@ -459,6 +459,13 @@ static size_t get_block_size(void *codec_info, size_t link_mtu) { return frame_count * 4 * 6; } +static size_t get_encoded_block_size(void *codec_info, size_t input_size) { + /* input size should be aligned to codec input block size */ + pa_assert_fp(input_size % (4 * 6) == 0); + + return (input_size / (4 * 6)) * 4; +} + static size_t get_block_size_hd(void *codec_info, size_t link_mtu) { /* aptX HD compression ratio is 4:1 and we need to process one aptX HD frame (6 bytes) at once, plus aptX HD frames are encapsulated in RTP */ size_t rtp_size = sizeof(struct rtp_header); @@ -467,6 +474,15 @@ static size_t get_block_size_hd(void *codec_info, size_t link_mtu) { return frame_count * 6 * 4; } +static size_t get_encoded_block_size_hd(void *codec_info, size_t input_size) { + size_t rtp_size = sizeof(struct rtp_header); + + /* input size should be aligned to codec input block size */ + pa_assert_fp(input_size % (4 * 6) == 0); + + return (input_size / (4 * 6)) * 6 + rtp_size; +} + static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { return 0; } @@ -554,6 +570,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer, .decode_buffer = decode_buffer, @@ -575,6 +592,7 @@ const pa_a2dp_codec pa_a2dp_codec_aptx_hd = { .reset = reset_hd, .get_read_block_size = get_block_size_hd, .get_write_block_size = get_block_size_hd, + .get_encoded_block_size = get_encoded_block_size_hd, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer_hd, .decode_buffer = decode_buffer_hd, diff --git a/src/modules/bluetooth/a2dp-codec-ldac-gst.c b/src/modules/bluetooth/a2dp-codec-ldac-gst.c index ace7948fb..261443356 100644 --- a/src/modules/bluetooth/a2dp-codec-ldac-gst.c +++ b/src/modules/bluetooth/a2dp-codec-ldac-gst.c @@ -405,6 +405,11 @@ static size_t get_block_size(void *codec_info, size_t link_mtu) { return get_ldac_num_samples(codec_info) * get_ldac_num_frames(codec_info, info->codec_type) * pa_frame_size(info->ss); } +static size_t get_encoded_block_size(void *codec_info, size_t input_size) { + /* encoded block size is not exactly known, report input_size */ + return input_size; +} + static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { return 0; } @@ -435,6 +440,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_hq = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer, }; @@ -455,6 +461,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_sq = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer, }; @@ -475,6 +482,7 @@ const pa_a2dp_codec pa_a2dp_codec_ldac_eqmid_mq = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer, }; diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c index a066b86e6..3ab334040 100644 --- a/src/modules/bluetooth/a2dp-codec-sbc.c +++ b/src/modules/bluetooth/a2dp-codec-sbc.c @@ -714,6 +714,16 @@ static size_t get_block_size(void *codec_info, size_t link_mtu) { return frame_count * sbc_info->codesize; } +static size_t get_encoded_block_size(void *codec_info, size_t input_size) { + struct sbc_info *sbc_info = (struct sbc_info *) codec_info; + size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_sbc_payload); + + /* input size should be aligned to codec input block size */ + pa_assert_fp(input_size % sbc_info->codesize == 0); + + return (input_size / sbc_info->codesize) * sbc_info->frame_length + rtp_size; +} + static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; uint8_t bitpool; @@ -902,6 +912,7 @@ const pa_a2dp_codec pa_a2dp_codec_sbc = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .increase_encoder_bitrate = increase_encoder_bitrate, .encode_buffer = encode_buffer, @@ -937,6 +948,7 @@ const pa_a2dp_codec pa_a2dp_codec_sbc_xq_453 = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .increase_encoder_bitrate = increase_encoder_bitrate, .encode_buffer = encode_buffer, @@ -959,6 +971,7 @@ const pa_a2dp_codec pa_a2dp_codec_sbc_xq_512 = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .increase_encoder_bitrate = increase_encoder_bitrate, .encode_buffer = encode_buffer, @@ -981,6 +994,7 @@ const pa_a2dp_codec pa_a2dp_codec_sbc_xq_552 = { .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, + .get_encoded_block_size = get_encoded_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .increase_encoder_bitrate = increase_encoder_bitrate, .encode_buffer = encode_buffer, diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index d866f4b28..fbed290e9 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -334,6 +334,52 @@ static void sco_release_cb(pa_bluetooth_transport *t) { /* device will close the SCO socket for us */ } +static ssize_t sco_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) { + ssize_t l = 0; + size_t written = 0; + size_t write_size; + + pa_assert(t); + + /* if encoder buffer has less data than required to make complete packet */ + if (size < write_mtu) + return 0; + + /* write out MTU sized chunks only */ + while (written < size) { + write_size = PA_MIN(size - written, write_mtu); + if (write_size < write_mtu) + break; + l = pa_write(fd, buffer + written, write_size, &t->stream_write_type); + if (l < 0) + break; + written += l; + } + + if (l < 0) { + if (errno == EAGAIN) { + /* Hmm, apparently the socket was not writable, give up for now */ + pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); + /* Drain write buffer */ + written = size; + } else { + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + /* Report error from write call */ + return -1; + } + } + + /* if too much data left discard it all */ + if (size - written >= write_mtu) { + pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu", + written, size, write_mtu); + /* Drain write buffer */ + written = size; + } + + return written; +} + static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { pa_bluetooth_transport *t = userdata; @@ -730,6 +776,7 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, t->acquire = sco_acquire_cb; t->release = sco_release_cb; + t->write = sco_transport_write; t->destroy = transport_destroy; /* If PA is the HF/HS we are in control of volume attenuation and diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c index 368efc113..7c8cbbf62 100644 --- a/src/modules/bluetooth/backend-ofono.c +++ b/src/modules/bluetooth/backend-ofono.c @@ -83,6 +83,52 @@ struct pa_bluetooth_backend { PA_LLIST_HEAD(pa_dbus_pending, pending); }; +static ssize_t sco_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) { + ssize_t l = 0; + size_t written = 0; + size_t write_size; + + pa_assert(t); + + /* if encoder buffer has less data than required to make complete packet */ + if (size < write_mtu) + return 0; + + /* write out MTU sized chunks only */ + while (written < size) { + write_size = PA_MIN(size - written, write_mtu); + if (write_size < write_mtu) + break; + l = pa_write(fd, buffer + written, write_size, &t->stream_write_type); + if (l < 0) + break; + written += l; + } + + if (l < 0) { + if (errno == EAGAIN) { + /* Hmm, apparently the socket was not writable, give up for now */ + pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); + /* Drain write buffer */ + written = size; + } else { + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + /* Report error from write call */ + return -1; + } + } + + /* if too much data left discard it all */ + if (size - written >= write_mtu) { + pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu", + written, size, write_mtu); + /* Drain write buffer */ + written = size; + } + + return written; +} + static pa_dbus_pending* hf_dbus_send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data) { pa_dbus_pending *p; @@ -354,6 +400,7 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, p, NULL, 0); card->transport->acquire = hf_audio_agent_transport_acquire; card->transport->release = hf_audio_agent_transport_release; + card->transport->write = sco_transport_write; card->transport->userdata = card; pa_bluetooth_transport_put(card->transport); diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index b1df0a977..8086f0ac8 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -22,11 +22,14 @@ #include #endif +#include + #include #include #include #include +#include #include #include #include @@ -582,6 +585,45 @@ static void bluez5_transport_release_cb(pa_bluetooth_transport *t) { pa_log_info("Transport %s released", t->path); } +static ssize_t a2dp_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) { + ssize_t l = 0; + size_t written = 0; + size_t write_size; + + pa_assert(t); + + while (written < size) { + write_size = PA_MIN(size - written, write_mtu); + l = pa_write(fd, buffer + written, write_size, &t->stream_write_type); + if (l < 0) + break; + written += l; + } + + if (l < 0) { + if (errno == EAGAIN) { + /* Hmm, apparently the socket was not writable, give up for now */ + pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); + /* Drain write buffer */ + written = size; + } else { + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + /* Report error from write call */ + return -1; + } + } + + /* if too much data left discard it all */ + if (size - written >= write_mtu) { + pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu", + written, size, write_mtu); + /* Drain write buffer */ + written = size; + } + + return written; +} + bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) { unsigned i; @@ -1906,6 +1948,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage t->a2dp_codec = a2dp_codec; t->acquire = bluez5_transport_acquire_cb; t->release = bluez5_transport_release_cb; + t->write = a2dp_transport_write; pa_bluetooth_transport_put(t); pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index 2121d3162..14885b13f 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -88,6 +88,7 @@ typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t); typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t); typedef pa_volume_t (*pa_bluetooth_transport_set_volume_cb)(pa_bluetooth_transport *t, pa_volume_t volume); +typedef ssize_t (*pa_bluetooth_transport_write_cb)(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu); struct pa_bluetooth_transport { pa_bluetooth_device *device; @@ -101,6 +102,7 @@ struct pa_bluetooth_transport { size_t config_size; const pa_a2dp_codec *a2dp_codec; + int stream_write_type; pa_volume_t source_volume; pa_volume_t sink_volume; @@ -109,6 +111,7 @@ struct pa_bluetooth_transport { pa_bluetooth_transport_acquire_cb acquire; pa_bluetooth_transport_release_cb release; + pa_bluetooth_transport_write_cb write; pa_bluetooth_transport_destroy_cb destroy; pa_bluetooth_transport_set_volume_cb set_sink_volume; pa_bluetooth_transport_set_volume_cb set_source_volume; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index bf655b987..9ed9faaae 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -128,7 +128,6 @@ struct userdata { bluetooth_msg *msg; int stream_fd; - int stream_write_type; size_t read_link_mtu; size_t write_link_mtu; size_t read_block_size; @@ -139,12 +138,13 @@ struct userdata { pa_smoother *read_smoother; pa_memchunk write_memchunk; - const pa_a2dp_codec *a2dp_codec; + const pa_a2dp_codec *bt_codec; void *encoder_info; pa_sample_spec encoder_sample_spec; void *encoder_buffer; /* Codec transfer buffer */ size_t encoder_buffer_size; /* Size of the buffer */ + size_t encoder_buffer_used; /* Used space in the buffer */ void *decoder_info; pa_sample_spec decoder_sample_spec; @@ -255,71 +255,109 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir } } +static void bt_prepare_encoder_buffer(struct userdata *u) +{ + size_t size; + pa_assert(u); + pa_assert(u->bt_codec); + + /* If socket write MTU is less than encoded frame size, there could be + * up to one write MTU of data left in encoder buffer from previous round. + * + * Reserve space for 2 encoded frames to cover that. + * + * Note for A2DP codecs it is expected that size of encoded frame is less + * than write link MTU therefore each encoded frame is sent out completely. + */ + if (u->bt_codec->get_encoded_block_size) + size = 2 * u->bt_codec->get_encoded_block_size(u->encoder_info, u->write_block_size); + else + size = 2 * u->write_block_size; + + if (u->encoder_buffer_size < size) { + u->encoder_buffer = pa_xrealloc(u->encoder_buffer, size); + u->encoder_buffer_size = size; + } +} + /* Run from IO thread */ -static int sco_process_render(struct userdata *u) { - ssize_t l; - pa_memchunk memchunk; - int saved_errno; +static int bt_write_buffer(struct userdata *u) { + ssize_t written = 0; pa_assert(u); - pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || - u->profile == PA_BLUETOOTH_PROFILE_HSP_AG || - u->profile == PA_BLUETOOTH_PROFILE_HFP_HF || - u->profile == PA_BLUETOOTH_PROFILE_HFP_AG); - pa_assert(u->sink); + pa_assert(u->transport); - pa_sink_render_full(u->sink, u->write_block_size, &memchunk); + written = u->transport->write(u->transport, u->stream_fd, u->encoder_buffer, u->encoder_buffer_used, u->write_link_mtu); - pa_assert(memchunk.length == u->write_block_size); - - for (;;) { - const void *p; - - /* Now write that data to the socket. The socket is of type - * SEQPACKET, and we generated the data of the MTU size, so this - * should just work. */ - - p = (const uint8_t *) pa_memblock_acquire_chunk(&memchunk); - l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type); - pa_memblock_release(memchunk.memblock); - - pa_assert(l != 0); - - if (l > 0) - break; - - saved_errno = errno; - - pa_memblock_unref(memchunk.memblock); - - if (saved_errno == EAGAIN) { - /* Hmm, apparently the socket was not writable, give up for now. - * Because the data was already rendered, let's discard the block. */ - pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); - return 1; - } - - pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(saved_errno)); + if (written < 0) return -1; - } - pa_assert((size_t) l <= memchunk.length); + /* calculate remainder */ + u->encoder_buffer_used -= written; - if ((size_t) l != memchunk.length) { - pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", - (unsigned long long) l, - (unsigned long long) memchunk.length); - - pa_memblock_unref(memchunk.memblock); - return -1; - } - - u->write_index += (uint64_t) memchunk.length; - pa_memblock_unref(memchunk.memblock); + /* move any remainder back to start of u->encoder_buffer */ + if (u->encoder_buffer_used) + memmove(u->encoder_buffer, u->encoder_buffer + written, u->encoder_buffer_used); return 1; } +/* Run from IO thread */ +static int bt_process_render(struct userdata *u) { + int ret; + + const uint8_t *ptr; + size_t processed; + size_t length; + + pa_assert(u); + pa_assert(u->sink); + pa_assert(u->bt_codec); + + /* First, render some data */ + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); + + pa_assert(u->write_memchunk.length == u->write_block_size); + + bt_prepare_encoder_buffer(u); + + ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); + + length = u->bt_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer + u->encoder_buffer_used, u->encoder_buffer_size - u->encoder_buffer_used, &processed); + + pa_memblock_release(u->write_memchunk.memblock); + + if (processed != u->write_memchunk.length) { + pa_log_error("Encoding error"); + return -1; + } + + /* Encoder function of BT codec may provide empty buffer, in this case do + * not post any empty buffer via BT 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_LIKELY(length)) { + u->encoder_buffer_used += length; + + ret = bt_write_buffer(u); + + if (ret < 0) { + /* Reset encoder sequence number and buffer positions */ + u->bt_codec->reset(u->encoder_info); + u->encoder_buffer_used = 0; + } + } else + ret = 0; + + u->write_index += (uint64_t) u->write_memchunk.length; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + return ret; +} + /* Run from IO thread */ static int sco_process_push(struct userdata *u) { ssize_t l; @@ -447,97 +485,6 @@ static void a2dp_prepare_decoder_buffer(struct userdata *u) { 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; - - l = pa_write(u->stream_fd, u->encoder_buffer, nbytes, &u->stream_write_type); - - pa_assert(l != 0); - - if (l < 0) { - - if (errno == EAGAIN) { - /* Hmm, apparently the socket was not writable, give up for now */ - pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); - break; - } - - pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); - ret = -1; - break; - } - - pa_assert((size_t) l <= nbytes); - - if ((size_t) l != nbytes) { - pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", - (unsigned long long) l, - (unsigned long long) nbytes); - ret = -1; - break; - } - - u->write_index += (uint64_t) u->write_memchunk.length; - pa_memblock_unref(u->write_memchunk.memblock); - pa_memchunk_reset(&u->write_memchunk); - - ret = 1; - - break; - } - - return ret; -} - -/* Run from IO thread */ -static int a2dp_process_render(struct userdata *u) { - const uint8_t *ptr; - size_t processed; - size_t length; - - pa_assert(u); - pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK); - pa_assert(u->sink); - pa_assert(u->a2dp_codec); - - /* First, render some data */ - if (!u->write_memchunk.memblock) - pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); - - pa_assert(u->write_memchunk.length == u->write_block_size); - - a2dp_prepare_encoder_buffer(u); - - /* Try to create a packet of the full MTU */ - ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); - - length = u->a2dp_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer, u->encoder_buffer_size, &processed); - - pa_memblock_release(u->write_memchunk.memblock); - - if (processed != u->write_memchunk.length) { - pa_log_error("Encoding error"); - return -1; - } - - return a2dp_write_buffer(u, length); -} - /* Run from IO thread */ static int a2dp_process_push(struct userdata *u) { int ret = 0; @@ -547,7 +494,7 @@ static int a2dp_process_push(struct userdata *u) { pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE); pa_assert(u->source); pa_assert(u->read_smoother); - pa_assert(u->a2dp_codec); + pa_assert(u->bt_codec); memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); memchunk.index = memchunk.length = 0; @@ -618,7 +565,7 @@ static int a2dp_process_push(struct userdata *u) { ptr = pa_memblock_acquire(memchunk.memblock); 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); + memchunk.length = u->bt_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed); pa_memblock_release(memchunk.memblock); @@ -789,11 +736,11 @@ static void transport_config_mtu(struct userdata *u) { u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec); } } else { - pa_assert(u->a2dp_codec); + pa_assert(u->bt_codec); if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { - u->write_block_size = u->a2dp_codec->get_write_block_size(u->encoder_info, u->write_link_mtu); + u->write_block_size = u->bt_codec->get_write_block_size(u->encoder_info, u->write_link_mtu); } else { - u->read_block_size = u->a2dp_codec->get_read_block_size(u->decoder_info, u->read_link_mtu); + u->read_block_size = u->bt_codec->get_read_block_size(u->decoder_info, u->read_link_mtu); } } @@ -821,12 +768,12 @@ static int setup_stream(struct userdata *u) { pa_log_info("Transport %s resuming", u->transport->path); if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { - pa_assert(u->a2dp_codec); - if (u->a2dp_codec->reset(u->encoder_info) < 0) + pa_assert(u->bt_codec); + if (u->bt_codec->reset(u->encoder_info) < 0) return -1; } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) { - pa_assert(u->a2dp_codec); - if (u->a2dp_codec->reset(u->decoder_info) < 0) + pa_assert(u->bt_codec); + if (u->bt_codec->reset(u->decoder_info) < 0) return -1; } @@ -1078,8 +1025,8 @@ static int add_source(struct userdata *u) { data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); data.namereg_fail = false; pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); - if (u->a2dp_codec) - pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->a2dp_codec->name); + if (u->bt_codec) + pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec); if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) @@ -1294,8 +1241,8 @@ static int add_sink(struct userdata *u) { data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); data.namereg_fail = false; pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); - if (u->a2dp_codec) - pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->a2dp_codec->name); + if (u->bt_codec) + pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec); if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) @@ -1344,6 +1291,9 @@ static int add_sink(struct userdata *u) { /* Run from main thread */ static int transport_config(struct userdata *u) { + /* reset encoder buffer contents */ + u->encoder_buffer_used = 0; + if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF @@ -1361,14 +1311,14 @@ static int transport_config(struct userdata *u) { pa_assert(u->transport); - pa_assert(!u->a2dp_codec); + pa_assert(!u->bt_codec); pa_assert(!u->encoder_info); pa_assert(!u->decoder_info); - u->a2dp_codec = u->transport->a2dp_codec; - pa_assert(u->a2dp_codec); + u->bt_codec = u->transport->a2dp_codec; + pa_assert(u->bt_codec); - info = u->a2dp_codec->init(is_a2dp_sink, false, u->transport->config, u->transport->config_size, is_a2dp_sink ? &u->encoder_sample_spec : &u->decoder_sample_spec, u->core); + info = u->bt_codec->init(is_a2dp_sink, false, u->transport->config, u->transport->config_size, is_a2dp_sink ? &u->encoder_sample_spec : &u->decoder_sample_spec, u->core); if (is_a2dp_sink) u->encoder_info = info; else @@ -1457,13 +1407,10 @@ static int write_block(struct userdata *u) { if (u->write_index <= 0) u->started_at = pa_rtclock_now(); - if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { - if ((n_written = a2dp_process_render(u)) < 0) - return -1; - } else { - if ((n_written = sco_process_render(u)) < 0) - return -1; - } + n_written = bt_process_render(u); + + if (n_written < 0) + n_written = -1; return n_written; } @@ -1635,7 +1582,7 @@ static void thread_func(void *userdata) { } if (u->write_index > 0 && u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { - size_t new_write_block_size = u->a2dp_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu); + size_t new_write_block_size = u->bt_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu); if (new_write_block_size) { u->write_block_size = new_write_block_size; handle_sink_block_size_change(u); @@ -1676,9 +1623,9 @@ static void thread_func(void *userdata) { if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && u->write_memchunk.memblock == NULL) { /* write_block() is keeping up with input, try increasing bitrate */ - if (u->a2dp_codec->increase_encoder_bitrate + if (u->bt_codec->increase_encoder_bitrate && pa_timeval_age(&tv_last_output_rate_change) >= u->device->output_rate_refresh_interval_ms * PA_USEC_PER_MSEC) { - size_t new_write_block_size = u->a2dp_codec->increase_encoder_bitrate(u->encoder_info, u->write_link_mtu); + size_t new_write_block_size = u->bt_codec->increase_encoder_bitrate(u->encoder_info, u->write_link_mtu); if (new_write_block_size) { u->write_block_size = new_write_block_size; handle_sink_block_size_change(u); @@ -1779,8 +1726,8 @@ static int start_thread(struct userdata *u) { } if (u->sink || u->source) - if (u->a2dp_codec) - pa_proplist_sets(u->card->proplist, PA_PROP_BLUETOOTH_CODEC, u->a2dp_codec->name); + if (u->bt_codec) + pa_proplist_sets(u->card->proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); return 0; } @@ -1845,19 +1792,34 @@ static void stop_thread(struct userdata *u) { u->read_smoother = NULL; } - if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) { + if (u->bt_codec) { if (u->encoder_info) { - u->a2dp_codec->deinit(u->encoder_info); + u->bt_codec->deinit(u->encoder_info); u->encoder_info = NULL; } if (u->decoder_info) { - u->a2dp_codec->deinit(u->decoder_info); + u->bt_codec->deinit(u->decoder_info); u->decoder_info = NULL; } - u->a2dp_codec = NULL; + u->bt_codec = NULL; } + + if (u->encoder_buffer) { + pa_xfree(u->encoder_buffer); + u->encoder_buffer = NULL; + } + + u->encoder_buffer_size = 0; + u->encoder_buffer_used = 0; + + if (u->decoder_buffer) { + pa_xfree(u->decoder_buffer); + u->decoder_buffer = NULL; + } + + u->decoder_buffer_size = 0; } /* Run from main thread */ @@ -2446,7 +2408,7 @@ static void switch_codec_cb_handler(bool success, pa_bluetooth_profile_t profile } pa_log_info("Codec successfully switched to %s with profile: %s", - u->a2dp_codec->name, pa_bluetooth_profile_to_string(u->profile)); + u->bt_codec->name, pa_bluetooth_profile_to_string(u->profile)); return; @@ -2549,7 +2511,7 @@ static int bluez5_device_message_handler(const char *object_path, const char *me codec_name = pa_json_object_get_string(parameters); - if (u->a2dp_codec && pa_streq(codec_name, u->a2dp_codec->name)) { + if (u->bt_codec && pa_streq(codec_name, u->bt_codec->name)) { pa_log_info("Requested codec is currently selected codec"); return -PA_ERR_INVALID; } @@ -2608,8 +2570,10 @@ static int bluez5_device_message_handler(const char *object_path, const char *me pa_json_encoder *encoder; encoder = pa_json_encoder_new(); - if (u->a2dp_codec) - pa_json_encoder_add_element_string(encoder, u->a2dp_codec->name); + if (u->bt_codec) + pa_json_encoder_add_element_string(encoder, u->bt_codec->name); + else + pa_json_encoder_add_element_null(encoder); *response = pa_json_encoder_to_string_free(encoder);