[pulseaudio-discuss] [PATCH v13 03/10] bluetooth: Parse remote timestamp from A2DP RTP packets when available
Pali Rohár
pali.rohar at gmail.com
Sun Nov 10 08:54:37 UTC 2019
On Saturday 09 November 2019 13:08:34 Georg Chini wrote:
> On 06.10.19 19:58, Pali Rohár wrote:
> > Some A2DP codecs (e.g. SBC or aptX-HD) use RTP packets. For sources use
> > timestamps from RTP packets to calculate read index and therefore remote
> > timestamp for synchronization.
> > ---
> > src/modules/bluetooth/a2dp-codec-api.h | 4 ++--
> > src/modules/bluetooth/a2dp-codec-sbc.c | 3 ++-
> > src/modules/bluetooth/module-bluez5-device.c | 14 +++++++++++---
> > 3 files changed, 15 insertions(+), 6 deletions(-)
> >
> > diff --git a/src/modules/bluetooth/a2dp-codec-api.h b/src/modules/bluetooth/a2dp-codec-api.h
> > index a3123f4ca..1fd8e81d0 100644
> > --- a/src/modules/bluetooth/a2dp-codec-api.h
> > +++ b/src/modules/bluetooth/a2dp-codec-api.h
> > @@ -91,8 +91,8 @@ typedef struct pa_a2dp_codec {
> > size_t (*encode_buffer)(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed);
> > /* Decode input_buffer of input_size to output_buffer of output_size,
> > * returns size of filled ouput_buffer and set processed to size of
> > - * processed input_buffer */
> > - 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);
> > + * processed input_buffer and set timestamp */
> > + size_t (*decode_buffer)(void *codec_info, uint32_t *timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed);
> > } pa_a2dp_codec;
> > #endif
> > diff --git a/src/modules/bluetooth/a2dp-codec-sbc.c b/src/modules/bluetooth/a2dp-codec-sbc.c
> > index 89c647fbe..733c1a9ab 100644
> > --- a/src/modules/bluetooth/a2dp-codec-sbc.c
> > +++ b/src/modules/bluetooth/a2dp-codec-sbc.c
> > @@ -597,7 +597,7 @@ static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t
> > return d - output_buffer;
> > }
> > -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) {
> > +static size_t decode_buffer(void *codec_info, uint32_t *timestamp, 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;
> > struct rtp_header *header;
> > @@ -657,6 +657,7 @@ static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_
> > frame_count--;
> > }
> > + *timestamp = ntohl(header->timestamp);
> > *processed = p - input_buffer;
> > return d - output_buffer;
> > }
> > diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
> > index 9da5d1ac3..fb77afaad 100644
> > --- a/src/modules/bluetooth/module-bluez5-device.c
> > +++ b/src/modules/bluetooth/module-bluez5-device.c
> > @@ -556,6 +556,7 @@ static int a2dp_process_push(struct userdata *u) {
> > struct msghdr m;
> > bool found_tstamp = false;
> > pa_usec_t tstamp;
> > + uint32_t timestamp;
> > uint8_t *ptr;
> > ssize_t l;
> > size_t processed;
> > @@ -591,8 +592,6 @@ static int a2dp_process_push(struct userdata *u) {
> > pa_assert((size_t) l <= u->decoder_buffer_size);
> > - /* 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);
> > @@ -613,7 +612,8 @@ 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);
> > + timestamp = 0; /* Decoder does not have to fill RTP timestamp */
> > + memchunk.length = u->a2dp_codec->decode_buffer(u->decoder_info, ×tamp, u->decoder_buffer, l, ptr, memchunk.length, &processed);
> > pa_memblock_release(memchunk.memblock);
> > @@ -623,6 +623,14 @@ static int a2dp_process_push(struct userdata *u) {
> > break;
> > }
> > + /* Some codecs may provide RTP timestamp, so use it to update read_index for calculation of remote tstamp */
> > + if (timestamp) {
> > + /* RTP timestamp is only 32bit and may overflow, avoid it by calculating high 32bits from the last read_index */
> > + size_t frame_size = pa_frame_size(&u->decoder_sample_spec);
> > + uint64_t timestamp_hi = (u->read_index / frame_size) & 0xFFFFFFFF00000000ULL;
> > + u->read_index = (timestamp_hi | timestamp) * frame_size;
> > + }
> > +
> > 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);
>
> This patch has three issues:
>
> 1) According to the specification, the RTP timestamp header may have an
> arbitrary offset.
>
> 2) I do not see how timestamp_hi could be anything but 0. Even if
> u->read_index is above
> the 32 bit border, the division by the frame size will always make it
> smaller because you
> start with a 32 bit number multiplied by frame size. So the logic for
> calculating the index
> is wrong.
>
> 3) As far as I understand, BT packets may be re-transmitted and therefore
> delivered out
> of order. Decrementing the read index while incrementing the system time
> will confuse the
> smoother, so if the index of the packet received is smaller than the current
> index, it should
> not be used. Due to the wrap-around of the RTP timestamp, this may be
> difficult to detect.
>
> Given the problems arising from the use of the RTP timestamp I wonder if we
> should
> use it at all. Or maybe we should use it to discard out-of-order packets,
> though as said
> above it is difficult to detect.
Thank you for comments. Seems that it is really not so obvious how to
correctly process RTP packets in bluetooth A2DP transfer. So I would
rater drop this patch for now and wait until we decide and come up with
the way how to properly handle RTP packets via bluetooth.
--
Pali Rohár
pali.rohar at gmail.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20191110/4c26d11b/attachment.sig>
More information about the pulseaudio-discuss
mailing list