mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	bluez5: allow codecs to produce multiple packets from same data
Codecs may need to fragment a single encoder frame across multiple packets that are sent consecutively. Allow codec->encode() to set need_flush=NEED_FLUSH_FRAGMENT, so that sink should immediately call start_encode + encode with NULL input data, to produce the next packet. Previously, other return values than need_flush=1 were unused, so no need to bump codec ABI for this.
This commit is contained in:
		
							parent
							
								
									0cab700c06
								
							
						
					
					
						commit
						3d4eafcb0f
					
				
					 7 changed files with 66 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue