mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	bluetooth: unify encoder code paths
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/507>
This commit is contained in:
		
							parent
							
								
									8a87af380a
								
							
						
					
					
						commit
						3902cee4a5
					
				
					 9 changed files with 330 additions and 183 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,14 @@
 | 
			
		|||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <pulse/rtclock.h>
 | 
			
		||||
#include <pulse/timeval.h>
 | 
			
		||||
#include <pulse/xmalloc.h>
 | 
			
		||||
 | 
			
		||||
#include <pulsecore/core.h>
 | 
			
		||||
#include <pulsecore/core-error.h>
 | 
			
		||||
#include <pulsecore/core-util.h>
 | 
			
		||||
#include <pulsecore/dbus-shared.h>
 | 
			
		||||
#include <pulsecore/log.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -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));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,69 +255,107 @@ 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->transport);
 | 
			
		||||
 | 
			
		||||
    written = u->transport->write(u->transport, u->stream_fd, u->encoder_buffer, u->encoder_buffer_used, u->write_link_mtu);
 | 
			
		||||
 | 
			
		||||
    if (written < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    /* calculate remainder */
 | 
			
		||||
    u->encoder_buffer_used -= written;
 | 
			
		||||
 | 
			
		||||
    /* 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->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->bt_codec);
 | 
			
		||||
 | 
			
		||||
    pa_sink_render_full(u->sink, u->write_block_size, &memchunk);
 | 
			
		||||
    /* First, render some data */
 | 
			
		||||
    if (!u->write_memchunk.memblock)
 | 
			
		||||
        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
 | 
			
		||||
 | 
			
		||||
    pa_assert(memchunk.length == u->write_block_size);
 | 
			
		||||
    pa_assert(u->write_memchunk.length == u->write_block_size);
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        const void *p;
 | 
			
		||||
    bt_prepare_encoder_buffer(u);
 | 
			
		||||
 | 
			
		||||
        /* 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. */
 | 
			
		||||
    ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
    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_assert(l != 0);
 | 
			
		||||
    pa_memblock_release(u->write_memchunk.memblock);
 | 
			
		||||
 | 
			
		||||
        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 (processed != u->write_memchunk.length) {
 | 
			
		||||
        pa_log_error("Encoding error");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_assert((size_t) l <= memchunk.length);
 | 
			
		||||
    /* 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 ((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);
 | 
			
		||||
    if (PA_LIKELY(length)) {
 | 
			
		||||
        u->encoder_buffer_used += length;
 | 
			
		||||
 | 
			
		||||
        pa_memblock_unref(memchunk.memblock);
 | 
			
		||||
        return -1;
 | 
			
		||||
        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) memchunk.length;
 | 
			
		||||
    pa_memblock_unref(memchunk.memblock);
 | 
			
		||||
    u->write_index += (uint64_t) u->write_memchunk.length;
 | 
			
		||||
    pa_memblock_unref(u->write_memchunk.memblock);
 | 
			
		||||
    pa_memchunk_reset(&u->write_memchunk);
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Run from IO thread */
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue