diff options
author | Igor V. Kovalenko <igor.v.kovalenko@gmail.com> | 2021-03-03 08:45:14 +0300 |
---|---|---|
committer | PulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org> | 2021-04-05 15:43:32 +0000 |
commit | 913e7767d60df64e20d0268efe479e1e79d6a5ba (patch) | |
tree | 293c96048ec0ebdfa233cdaed90ba4c99a6f367d /src/modules | |
parent | 3902cee4a5bcbffd1e3f486bf127880e48949cc2 (diff) | |
download | pulseaudio-913e7767d60df64e20d0268efe479e1e79d6a5ba.tar.gz |
bluetooth: unify decoder code paths
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/507>
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/bluetooth/module-bluez5-device.c | 271 |
1 files changed, 88 insertions, 183 deletions
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 9ed9faaae..89bacf425 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -358,121 +358,7 @@ static int bt_process_render(struct userdata *u) { return ret; } -/* Run from IO thread */ -static int sco_process_push(struct userdata *u) { - ssize_t l; - pa_memchunk memchunk; - struct cmsghdr *cm; - struct msghdr m; - bool found_tstamp = false; - pa_usec_t tstamp = 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->source); - pa_assert(u->read_smoother); - - memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); - memchunk.index = memchunk.length = 0; - - for (;;) { - void *p; - uint8_t aux[1024]; - struct iovec iov; - - pa_zero(m); - pa_zero(aux); - pa_zero(iov); - - m.msg_iov = &iov; - m.msg_iovlen = 1; - m.msg_control = aux; - m.msg_controllen = sizeof(aux); - - p = pa_memblock_acquire(memchunk.memblock); - iov.iov_base = p; - iov.iov_len = pa_memblock_get_length(memchunk.memblock); - l = recvmsg(u->stream_fd, &m, 0); - pa_memblock_release(memchunk.memblock); - - if (l > 0) - break; - - if (l < 0 && errno == EINTR) - /* Retry right away if we got interrupted */ - continue; - - pa_memblock_unref(memchunk.memblock); - - if (l < 0 && errno == EAGAIN) - /* Hmm, apparently the socket was not readable, give up for now. */ - return 0; - - pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); - return -1; - } - - pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); - - /* In some rare occasions, we might receive packets of a very strange - * size. This could potentially be possible if the SCO packet was - * received partially over-the-air, or more probably due to hardware - * issues in our Bluetooth adapter. In these cases, in order to avoid - * an assertion failure due to unaligned data, just discard the whole - * packet */ - if (!pa_frame_aligned(l, &u->decoder_sample_spec)) { - pa_log_warn("SCO packet received of unaligned size: %zu", l); - pa_memblock_unref(memchunk.memblock); - return -1; - } - - memchunk.length = (size_t) l; - u->read_index += (uint64_t) l; - - for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) - if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { - struct timeval *tv = (struct timeval*) CMSG_DATA(cm); - pa_rtclock_from_wallclock(tv); - tstamp = pa_timeval_load(tv); - found_tstamp = true; - break; - } - - if (!found_tstamp) { - PA_ONCE_BEGIN { - pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); - } PA_ONCE_END; - tstamp = pa_rtclock_now(); - } - - 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_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); - - return l; -} - -/* Run from IO thread */ -static void a2dp_prepare_encoder_buffer(struct userdata *u) { - pa_assert(u); - - if (u->encoder_buffer_size < u->write_link_mtu) { - pa_xfree(u->encoder_buffer); - u->encoder_buffer = pa_xmalloc(u->write_link_mtu); - } - - /* 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) { +static void bt_prepare_decoder_buffer(struct userdata *u) { pa_assert(u); if (u->decoder_buffer_size < u->read_link_mtu) { @@ -486,31 +372,16 @@ static void a2dp_prepare_decoder_buffer(struct userdata *u) { } /* Run from IO thread */ -static int a2dp_process_push(struct userdata *u) { - int ret = 0; - pa_memchunk memchunk; - - pa_assert(u); - pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE); - pa_assert(u->source); - pa_assert(u->read_smoother); - pa_assert(u->bt_codec); - - memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); - memchunk.index = memchunk.length = 0; - - a2dp_prepare_decoder_buffer(u); +static ssize_t bt_transport_read(pa_bluetooth_transport *t, int fd, void *buffer, size_t size, pa_usec_t *p_timestamp) { + ssize_t received = 0; + pa_assert(t); for (;;) { uint8_t aux[1024]; struct iovec iov; struct cmsghdr *cm; struct msghdr m; bool found_tstamp = false; - pa_usec_t tstamp; - uint8_t *ptr; - ssize_t l; - size_t processed; pa_zero(m); pa_zero(aux); @@ -521,77 +392,110 @@ static int a2dp_process_push(struct userdata *u) { m.msg_control = aux; m.msg_controllen = sizeof(aux); - iov.iov_base = u->decoder_buffer; - iov.iov_len = u->decoder_buffer_size; + iov.iov_base = buffer; + iov.iov_len = size; - l = recvmsg(u->stream_fd, &m, 0); + received = recvmsg(fd, &m, 0); - if (l <= 0) { + if (received <= 0) { - if (l < 0 && errno == EINTR) + if (received < 0 && errno == EINTR) /* Retry right away if we got interrupted */ continue; - else if (l < 0 && errno == EAGAIN) + else if (received < 0 && errno == EAGAIN) /* Hmm, apparently the socket was not readable, give up for now. */ - break; + return 0; - pa_log_error("Failed to read data from socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); - ret = -1; - break; + pa_log_error("Failed to read data from socket: %s", received < 0 ? pa_cstrerror(errno) : "EOF"); + return -1; } - pa_assert((size_t) l <= u->decoder_buffer_size); + pa_assert((size_t) received <= size); - /* TODO: get timestamp from rtp */ + if (p_timestamp) { + /* TODO: get timestamp from rtp */ - for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) { - if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { - struct timeval *tv = (struct timeval*) CMSG_DATA(cm); - pa_rtclock_from_wallclock(tv); - tstamp = pa_timeval_load(tv); - found_tstamp = true; - break; + for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) { + if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { + struct timeval *tv = (struct timeval*) CMSG_DATA(cm); + pa_rtclock_from_wallclock(tv); + *p_timestamp = pa_timeval_load(tv); + found_tstamp = true; + break; + } } - } - if (!found_tstamp) { - PA_ONCE_BEGIN { - pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); - } PA_ONCE_END; - tstamp = pa_rtclock_now(); + if (!found_tstamp) { + PA_ONCE_BEGIN { + pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); + } PA_ONCE_END; + *p_timestamp = pa_rtclock_now(); + } } - ptr = pa_memblock_acquire(memchunk.memblock); - memchunk.length = pa_memblock_get_length(memchunk.memblock); + break; + } - memchunk.length = u->bt_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed); + return received; +} - pa_memblock_release(memchunk.memblock); +/* Run from IO thread */ +/* Read incoming data, decode it and post result (if any) to source output. + * Returns number of bytes posted to source output. */ +static int bt_process_push(struct userdata *u) { + pa_usec_t tstamp; + uint8_t *ptr; + ssize_t received; + size_t processed = 0; - if (processed != (size_t) l) { - pa_log_error("Decoding error"); - ret = -1; - break; - } + pa_assert(u); + pa_assert(u->source); + pa_assert(u->read_smoother); + pa_assert(u->bt_codec); + pa_assert(u->transport); - u->read_index += (uint64_t) memchunk.length; - 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); + bt_prepare_decoder_buffer(u); - /* 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); + received = bt_transport_read(u->transport, u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &tstamp); - ret = l; - break; + if (received <= 0) { + return received; + } + + pa_memchunk memchunk; + + memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); + memchunk.index = memchunk.length = 0; + + ptr = pa_memblock_acquire(memchunk.memblock); + memchunk.length = pa_memblock_get_length(memchunk.memblock); + + memchunk.length = u->bt_codec->decode_buffer(u->decoder_info, u->decoder_buffer, received, ptr, memchunk.length, &processed); + + pa_memblock_release(memchunk.memblock); + + if (processed != (size_t) received) { + pa_log_error("Decoding error"); + return -1; } + u->read_index += (uint64_t) memchunk.length; + 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); + + /* Decoding of 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); + + /* report decoded size */ + received = memchunk.length; + pa_memblock_unref(memchunk.memblock); - return ret; + return received; } static void update_sink_buffer_size(struct userdata *u) { @@ -1481,16 +1385,17 @@ static void thread_func(void *userdata) { if (pollfd->revents & POLLIN) { int n_read; - if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) - n_read = a2dp_process_push(u); - else - n_read = sco_process_push(u); + n_read = bt_process_push(u); if (n_read < 0) goto fail; if (have_sink && n_read > 0) { - /* We just read something, so we are supposed to write something, too */ + /* We just read something, so we are supposed to write something, too + * + * If source and sink sample specifications are not equal, + * expected write size needs to be adjusted accordingly. + */ bytes_to_write += n_read; blocks_to_write += bytes_to_write / u->write_block_size; bytes_to_write = bytes_to_write % u->write_block_size; |