diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index e2c6aed18..1195def30 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -493,7 +493,7 @@ static int codec_encode(void *data, return -EINVAL; *dst_out = out_args.numOutBytes; - *need_flush = 1; + *need_flush = NEED_FLUSH_ALL; /* RFC6416: It is set to 1 to indicate that the RTP packet contains a complete * audioMuxElement or the last fragment of an audioMuxElement */ diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index 7249d50ad..9a80134fd 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -404,7 +404,7 @@ static int codec_encode(void *data, avail_dst_size = (this->max_frames - this->frame_count) * this->frame_length; if (SPA_UNLIKELY(dst_size < avail_dst_size)) { - *need_flush = 1; + *need_flush = NEED_FLUSH_ALL; return 0; } @@ -414,7 +414,7 @@ static int codec_encode(void *data, return -EINVAL; this->frame_count += *dst_out / this->frame_length; - *need_flush = this->frame_count >= this->max_frames; + *need_flush = (this->frame_count >= this->max_frames) ? NEED_FLUSH_ALL : NEED_FLUSH_NO; return res; } diff --git a/spa/plugins/bluez5/a2dp-codec-faststream.c b/spa/plugins/bluez5/a2dp-codec-faststream.c index 681725ae0..ddcf331d6 100644 --- a/spa/plugins/bluez5/a2dp-codec-faststream.c +++ b/spa/plugins/bluez5/a2dp-codec-faststream.c @@ -316,7 +316,7 @@ static int codec_encode(void *data, } this->frame_count += res / this->codesize; - *need_flush = this->frame_count >= this->max_frames; + *need_flush = (this->frame_count >= this->max_frames) ? NEED_FLUSH_ALL : NEED_FLUSH_NO; return res; } diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index 946ee7477..ee6bc2ebf 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -565,7 +565,7 @@ static int codec_encode(void *data, *dst_out = dst_used; this->payload->frame_count += frame_num; - *need_flush = this->payload->frame_count > 0; + *need_flush = (this->payload->frame_count > 0) ? NEED_FLUSH_ALL : NEED_FLUSH_NO; return src_used; } diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index 6da9af70b..6a43c666a 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -605,7 +605,7 @@ static int codec_encode(void *data, spa_assert(res == this->codesize); this->payload->frame_count += res / this->codesize; - *need_flush = this->payload->frame_count >= this->max_frames; + *need_flush = (this->payload->frame_count >= this->max_frames) ? NEED_FLUSH_ALL : NEED_FLUSH_NO; return res; } diff --git a/spa/plugins/bluez5/a2dp-codecs.h b/spa/plugins/bluez5/a2dp-codecs.h index 672bcc080..bd041b29e 100644 --- a/spa/plugins/bluez5/a2dp-codecs.h +++ b/spa/plugins/bluez5/a2dp-codecs.h @@ -66,6 +66,12 @@ extern const char *codec_plugin_factory_name; #define A2DP_CODEC_DEFAULT_RATE 48000 #define A2DP_CODEC_DEFAULT_CHANNELS 2 +enum { + NEED_FLUSH_NO = 0, + NEED_FLUSH_ALL = 1, + NEED_FLUSH_FRAGMENT = 2, +}; + struct a2dp_codec_audio_info { uint32_t rate; uint32_t channels; diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c index 3c1ab5007..4460695bf 100644 --- a/spa/plugins/bluez5/a2dp-sink.c +++ b/spa/plugins/bluez5/a2dp-sink.c @@ -157,6 +157,8 @@ struct impl { struct spa_audio_info codec_format; int need_flush; + bool fragment; + uint64_t fragment_timeout; uint32_t block_size; uint8_t buffer[BUFFER_SIZE]; uint32_t buffer_used; @@ -436,6 +438,7 @@ static int reset_buffer(struct impl *this) } this->need_flush = 0; this->frame_count = 0; + this->fragment = false; this->buffer_used = this->codec->start_encode(this->codec_data, this->buffer, sizeof(this->buffer), this->seqnum++, this->timestamp); @@ -533,6 +536,37 @@ static int encode_buffer(struct impl *this, const void *data, uint32_t size) return processed; } +static int encode_fragment(struct impl *this) +{ + int res; + size_t out_encoded; + struct port *port = &this->port; + + spa_log_trace(this->log, "%p: encode fragment used %d, %d %d %d", + this, this->buffer_used, port->frame_size, this->block_size, + this->frame_count); + + if (this->need_flush) + return 0; + + res = this->codec->encode(this->codec_data, + NULL, 0, + this->buffer + this->buffer_used, + sizeof(this->buffer) - this->buffer_used, + &out_encoded, &this->need_flush); + if (res < 0) + return res; + if (res != 0) + return -EINVAL; + + this->buffer_used += out_encoded; + + spa_log_trace(this->log, "%p: processed fragment %zd used %d", + this, out_encoded, this->buffer_used); + + return 0; +} + static int flush_buffer(struct impl *this) { spa_log_trace(this->log, "%p: used:%d block_size:%d", this, @@ -596,6 +630,15 @@ static int flush_data(struct impl *this, uint64_t now_time) total_frames = 0; again: written = 0; + if (this->fragment && !this->need_flush) { + int res; + this->fragment = false; + if ((res = encode_fragment(this)) < 0) { + /* Error */ + reset_buffer(this); + return res; + } + } while (!spa_list_is_empty(&port->ready) && !this->need_flush) { uint8_t *src; uint32_t n_bytes, n_frames; @@ -706,6 +749,17 @@ again: uint64_t timeout = (quantum > max_excess) ? (packet_time * (quantum - max_excess) / quantum) : 0; + if (this->need_flush == NEED_FLUSH_FRAGMENT) { + reset_buffer(this); + this->fragment = true; + this->fragment_timeout = (packet_samples > 0) ? timeout : this->fragment_timeout; + goto again; + } + if (this->fragment_timeout > 0) { + timeout = this->fragment_timeout; + this->fragment_timeout = 0; + } + reset_buffer(this); if (now_time - this->last_error > SPA_NSEC_PER_SEC) { if (get_transport_unused_size(this) == (int)this->fd_buffer_size) {