#include <sys/queue.h>
#include <sys/filio.h>
#include <sys/soundcard.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
#include <sdp.h>
#include "backend.h"
#include "int.h"
#include "avdtp_signal.h"
#include "bt.h"
#define DPRINTF(...) printf("backend_bt: " __VA_ARGS__)
struct l2cap_info {
bdaddr_t laddr;
bdaddr_t raddr;
};
static struct bt_config bt_play_cfg;
static struct bt_config bt_rec_cfg;
int
bt_receive(struct bt_config *cfg, void *ptr, int len, int use_delay)
{
struct sbc_header *phdr = (struct sbc_header *)cfg->mtu_data;
struct sbc_encode *sbc = cfg->handle.sbc_enc;
uint8_t *tmp = ptr;
int old_len = len;
int delta;
int err;
if (use_delay)
virtual_oss_wait();
switch (cfg->blocks) {
case BLOCKS_4:
sbc->blocks = 4;
break;
case BLOCKS_8:
sbc->blocks = 8;
break;
case BLOCKS_12:
sbc->blocks = 12;
break;
default:
sbc->blocks = 16;
break;
}
switch (cfg->bands) {
case BANDS_4:
sbc->bands = 4;
break;
default:
sbc->bands = 8;
break;
}
if (cfg->chmode != MODE_MONO) {
sbc->channels = 2;
} else {
sbc->channels = 1;
}
while (1) {
delta = len & ~1;
if (delta > (int)(2 * sbc->rem_len))
delta = (2 * sbc->rem_len);
memcpy(tmp, (char *)sbc->music_data + sbc->rem_off, delta);
tmp += delta;
len -= delta;
sbc->rem_off += delta / 2;
sbc->rem_len -= delta / 2;
if (len == 0)
break;
if (sbc->rem_len == 0 &&
sbc->rem_data_frames != 0) {
err = sbc_decode_frame(cfg, sbc->rem_data_len * 8);
sbc->rem_data_frames--;
sbc->rem_data_ptr += err;
sbc->rem_data_len -= err;
continue;
}
err = read(cfg->fd, cfg->mtu_data, cfg->mtu);
if (err == 0) {
break;
} else if (err < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
else
return (-1);
}
if (err < (int)sizeof(*phdr) || phdr->id != 0x80)
continue;
sbc->rem_data_frames = phdr->numFrames;
sbc->rem_data_ptr = (uint8_t *)(phdr + 1);
sbc->rem_data_len = err - sizeof(*phdr);
}
return (old_len - len);
}
static int
bt_set_format(int *format)
{
int value;
value = *format & AFMT_S16_NE;
if (value != 0) {
*format = value;
return (0);
}
return (-1);
}
static void
bt_close(struct voss_backend *pbe)
{
struct bt_config *cfg = pbe->arg;
if (cfg->hc > 0) {
avdtpAbort(cfg->hc, cfg->sep);
avdtpClose(cfg->hc, cfg->sep);
close(cfg->hc);
cfg->hc = -1;
}
if (cfg->fd > 0) {
close(cfg->fd);
cfg->fd = -1;
}
}
static void
bt_play_close(struct voss_backend *pbe)
{
struct bt_config *cfg = pbe->arg;
switch (cfg->codec) {
case CODEC_SBC:
if (cfg->handle.sbc_enc == NULL)
break;
free(cfg->handle.sbc_enc);
cfg->handle.sbc_enc = NULL;
break;
#ifdef HAVE_LIBAV
case CODEC_AAC:
if (cfg->handle.av.context == NULL)
break;
av_free(cfg->rem_in_data);
av_frame_free(&cfg->handle.av.frame);
avcodec_close(cfg->handle.av.context);
avformat_free_context(cfg->handle.av.format);
cfg->handle.av.context = NULL;
break;
#endif
default:
break;
}
return (bt_close(pbe));
}
static void
bt_rec_close(struct voss_backend *pbe)
{
struct bt_config *cfg = pbe->arg;
switch (cfg->codec) {
case CODEC_SBC:
break;
#ifdef HAVE_LIBAV
case CODEC_AAC:
break;
#endif
default:
break;
}
return (bt_close(pbe));
}
static const uint32_t bt_attrs[] = {
SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
};
#define BT_NUM_VALUES 32
#define BT_BUF_SIZE 32
static int
bt_find_psm(const uint8_t *start, const uint8_t *end)
{
uint32_t type;
uint32_t len;
int protover = 0;
int psm = -1;
if ((end - start) < 2)
return (-1);
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
return (-1);
}
while (start < end) {
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
return (-1);
}
if (len > (uint32_t)(end - start))
break;
if (len >= 6) {
const uint8_t *ptr = start;
SDP_GET8(type, ptr);
if (type == SDP_DATA_UUID16) {
uint16_t temp;
SDP_GET16(temp, ptr);
switch (temp) {
case SDP_UUID_PROTOCOL_L2CAP:
SDP_GET8(type, ptr);
SDP_GET16(psm, ptr);
break;
case SDP_UUID_PROTOCOL_AVDTP:
SDP_GET8(type, ptr);
SDP_GET16(protover, ptr);
break;
default:
break;
}
}
}
start += len;
if (protover >= 0x0100 && psm > -1)
return (htole16(psm));
}
return (-1);
}
static int
bt_query(struct l2cap_info *info, uint16_t service_class)
{
sdp_attr_t values[BT_NUM_VALUES];
uint8_t buffer[BT_NUM_VALUES][BT_BUF_SIZE];
void *ss;
int psm = -1;
int n;
memset(buffer, 0, sizeof(buffer));
memset(values, 0, sizeof(values));
ss = sdp_open(&info->laddr, &info->raddr);
if (ss == NULL || sdp_error(ss) != 0) {
DPRINTF("Could not open SDP\n");
sdp_close(ss);
return (psm);
}
for (n = 0; n != BT_NUM_VALUES; n++) {
values[n].flags = SDP_ATTR_INVALID;
values[n].vlen = BT_BUF_SIZE;
values[n].value = buffer[n];
}
n = sdp_search(ss, 1, &service_class, 1, bt_attrs, BT_NUM_VALUES, values);
if (n != 0) {
DPRINTF("SDP search failed\n");
goto done;
}
for (n = 0; n != BT_NUM_VALUES; n++) {
if (values[n].flags != SDP_ATTR_OK)
break;
if (values[n].attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
continue;
psm = bt_find_psm(values[n].value, values[n].value + values[n].vlen);
if (psm > -1)
break;
}
done:
sdp_close(ss);
return (psm);
}
static int
bt_open(struct voss_backend *pbe __unused, const char *devname, int samplerate,
int bufsize __unused, int *pchannels, int *pformat, struct bt_config *cfg,
int service_class, int isSink)
{
struct sockaddr_l2cap addr;
struct l2cap_info info;
socklen_t mtusize = sizeof(uint16_t);
int tmpbitpool;
int l2cap_psm;
int temp;
memset(&info, 0, sizeof(info));
if (strstr(devname, "/dev/bluetooth/") != devname) {
printf("Invalid device name '%s'", devname);
goto error;
}
devname += sizeof("/dev/bluetooth/") - 1;
if (!bt_aton(devname, &info.raddr)) {
struct hostent *he = NULL;
if ((he = bt_gethostbyname(devname)) == NULL) {
DPRINTF("Could not get host by name\n");
goto error;
}
bdaddr_copy(&info.raddr, (bdaddr_t *)he->h_addr);
}
switch (samplerate) {
case 8000:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0x80;
cfg->aacMode2 = 0x0C;
break;
case 11025:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0x40;
cfg->aacMode2 = 0x0C;
break;
case 12000:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0x20;
cfg->aacMode2 = 0x0C;
break;
case 16000:
cfg->freq = FREQ_16K;
cfg->aacMode1 = 0x10;
cfg->aacMode2 = 0x0C;
break;
case 22050:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0x08;
cfg->aacMode2 = 0x0C;
break;
case 24000:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0x04;
cfg->aacMode2 = 0x0C;
break;
case 32000:
cfg->freq = FREQ_32K;
cfg->aacMode1 = 0x02;
cfg->aacMode2 = 0x0C;
break;
case 44100:
cfg->freq = FREQ_44_1K;
cfg->aacMode1 = 0x01;
cfg->aacMode2 = 0x0C;
break;
case 48000:
cfg->freq = FREQ_48K;
cfg->aacMode1 = 0;
cfg->aacMode2 = 0x8C;
break;
case 64000:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0;
cfg->aacMode2 = 0x4C;
break;
case 88200:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0;
cfg->aacMode2 = 0x2C;
break;
case 96000:
cfg->freq = FREQ_UNDEFINED;
cfg->aacMode1 = 0;
cfg->aacMode2 = 0x1C;
break;
default:
DPRINTF("Invalid samplerate %d", samplerate);
goto error;
}
cfg->bands = BANDS_8;
cfg->bitpool = 0;
switch (*pchannels) {
case 1:
cfg->aacMode2 &= 0xF8;
cfg->chmode = MODE_MONO;
break;
default:
cfg->aacMode2 &= 0xF4;
cfg->chmode = MODE_STEREO;
break;
}
cfg->allocm = ALLOC_LOUDNESS;
if (cfg->chmode == MODE_MONO || cfg->chmode == MODE_DUAL)
tmpbitpool = 16;
else
tmpbitpool = 32;
if (cfg->bands == BANDS_8)
tmpbitpool *= 8;
else
tmpbitpool *= 4;
if (tmpbitpool > DEFAULT_MAXBPOOL)
tmpbitpool = DEFAULT_MAXBPOOL;
cfg->bitpool = tmpbitpool;
if (bt_set_format(pformat)) {
DPRINTF("Unsupported sample format\n");
goto error;
}
l2cap_psm = bt_query(&info, service_class);
DPRINTF("PSM=0x%02x\n", l2cap_psm);
if (l2cap_psm < 0) {
DPRINTF("PSM not found\n");
goto error;
}
cfg->hc = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
if (cfg->hc < 0) {
DPRINTF("Could not create BT socket\n");
goto error;
}
memset(&addr, 0, sizeof(addr));
addr.l2cap_len = sizeof(addr);
addr.l2cap_family = AF_BLUETOOTH;
bdaddr_copy(&addr.l2cap_bdaddr, &info.laddr);
if (bind(cfg->hc, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
DPRINTF("Could not bind to HC\n");
goto error;
}
bdaddr_copy(&addr.l2cap_bdaddr, &info.raddr);
addr.l2cap_psm = l2cap_psm;
if (connect(cfg->hc, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
DPRINTF("Could not connect to HC: %d\n", errno);
goto error;
}
if (avdtpDiscoverAndConfig(cfg, isSink)) {
DPRINTF("DISCOVER FAILED\n");
goto error;
}
if (avdtpOpen(cfg->hc, cfg->sep)) {
DPRINTF("OPEN FAILED\n");
goto error;
}
cfg->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
if (cfg->fd < 0) {
DPRINTF("Could not create BT socket\n");
goto error;
}
memset(&addr, 0, sizeof(addr));
addr.l2cap_len = sizeof(addr);
addr.l2cap_family = AF_BLUETOOTH;
bdaddr_copy(&addr.l2cap_bdaddr, &info.laddr);
if (bind(cfg->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
DPRINTF("Could not bind\n");
goto error;
}
bdaddr_copy(&addr.l2cap_bdaddr, &info.raddr);
addr.l2cap_psm = l2cap_psm;
if (connect(cfg->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
DPRINTF("Could not connect: %d\n", errno);
goto error;
}
if (isSink) {
if (getsockopt(cfg->fd, SOL_L2CAP, SO_L2CAP_OMTU, &cfg->mtu, &mtusize) == -1) {
DPRINTF("Could not get MTU\n");
goto error;
}
temp = cfg->mtu * 16;
if (setsockopt(cfg->fd, SOL_SOCKET, SO_SNDBUF, &temp, sizeof(temp)) == -1) {
DPRINTF("Could not set send buffer size\n");
goto error;
}
temp = cfg->mtu;
if (setsockopt(cfg->fd, SOL_SOCKET, SO_SNDLOWAT, &temp, sizeof(temp)) == -1) {
DPRINTF("Could not set low water mark\n");
goto error;
}
} else {
if (getsockopt(cfg->fd, SOL_L2CAP, SO_L2CAP_IMTU, &cfg->mtu, &mtusize) == -1) {
DPRINTF("Could not get MTU\n");
goto error;
}
temp = cfg->mtu * 16;
if (setsockopt(cfg->fd, SOL_SOCKET, SO_RCVBUF, &temp, sizeof(temp)) == -1) {
DPRINTF("Could not set receive buffer size\n");
goto error;
}
temp = 1;
if (setsockopt(cfg->fd, SOL_SOCKET, SO_RCVLOWAT, &temp, sizeof(temp)) == -1) {
DPRINTF("Could not set low water mark\n");
goto error;
}
temp = 1;
if (ioctl(cfg->fd, FIONBIO, &temp) == -1) {
DPRINTF("Could not set non-blocking I/O for receive direction\n");
goto error;
}
}
if (avdtpStart(cfg->hc, cfg->sep)) {
DPRINTF("START FAILED\n");
goto error;
}
switch (cfg->chmode) {
case MODE_MONO:
*pchannels = 1;
break;
default:
*pchannels = 2;
break;
}
return (0);
error:
if (cfg->hc > 0) {
close(cfg->hc);
cfg->hc = -1;
}
if (cfg->fd > 0) {
close(cfg->fd);
cfg->fd = -1;
}
return (-1);
}
static void
bt_init_cfg(struct bt_config *cfg)
{
memset(cfg, 0, sizeof(*cfg));
}
static int
bt_rec_open(struct voss_backend *pbe, const char *devname, int samplerate,
int bufsize, int *pchannels, int *pformat)
{
struct bt_config *cfg = pbe->arg;
int retval;
bt_init_cfg(cfg);
retval = bt_open(pbe, devname, samplerate, bufsize, pchannels, pformat,
cfg, SDP_SERVICE_CLASS_AUDIO_SOURCE, 0);
if (retval != 0)
return (retval);
return (0);
}
static int
bt_play_open(struct voss_backend *pbe, const char *devname, int samplerate,
int bufsize, int *pchannels, int *pformat)
{
struct bt_config *cfg = pbe->arg;
int retval;
bt_init_cfg(cfg);
retval = bt_open(pbe, devname, samplerate, bufsize, pchannels, pformat,
cfg, SDP_SERVICE_CLASS_AUDIO_SINK, 1);
if (retval != 0)
return (retval);
switch (cfg->codec) {
case CODEC_SBC:
cfg->handle.sbc_enc =
malloc(sizeof(*cfg->handle.sbc_enc));
if (cfg->handle.sbc_enc == NULL)
return (-1);
memset(cfg->handle.sbc_enc, 0, sizeof(*cfg->handle.sbc_enc));
break;
#ifdef HAVE_LIBAV
case CODEC_AAC:
cfg->handle.av.codec = __DECONST(AVCodec *,
avcodec_find_encoder_by_name("aac"));
if (cfg->handle.av.codec == NULL) {
DPRINTF("Codec AAC encoder not found\n");
goto av_error_0;
}
cfg->handle.av.format = avformat_alloc_context();
if (cfg->handle.av.format == NULL) {
DPRINTF("Could not allocate format context\n");
goto av_error_0;
}
cfg->handle.av.format->oformat =
av_guess_format("latm", NULL, NULL);
if (cfg->handle.av.format->oformat == NULL) {
DPRINTF("Could not guess output format\n");
goto av_error_1;
}
cfg->handle.av.stream = avformat_new_stream(
cfg->handle.av.format, cfg->handle.av.codec);
if (cfg->handle.av.stream == NULL) {
DPRINTF("Could not create new stream\n");
goto av_error_1;
}
cfg->handle.av.context = avcodec_alloc_context3(cfg->handle.av.codec);
if (cfg->handle.av.context == NULL) {
DPRINTF("Could not allocate audio context\n");
goto av_error_1;
}
cfg->handle.av.context->bit_rate = 128000;
cfg->handle.av.context->sample_fmt = AV_SAMPLE_FMT_FLTP;
cfg->handle.av.context->sample_rate = samplerate;
switch (*pchannels) {
case 1:
cfg->handle.av.context->ch_layout = *(AVChannelLayout *)AV_CH_LAYOUT_MONO;
break;
default:
cfg->handle.av.context->ch_layout = *(AVChannelLayout *)AV_CH_LAYOUT_STEREO;
break;
}
cfg->handle.av.context->profile = FF_PROFILE_AAC_LOW;
if (1) {
AVDictionary *opts = NULL;
av_dict_set(&opts, "strict", "-2", 0);
av_dict_set_int(&opts, "latm", 1, 0);
if (avcodec_open2(cfg->handle.av.context,
cfg->handle.av.codec, &opts) < 0) {
av_dict_free(&opts);
DPRINTF("Could not open codec\n");
goto av_error_1;
}
av_dict_free(&opts);
}
cfg->handle.av.frame = av_frame_alloc();
if (cfg->handle.av.frame == NULL) {
DPRINTF("Could not allocate audio frame\n");
goto av_error_2;
}
cfg->handle.av.frame->nb_samples = cfg->handle.av.context->frame_size;
cfg->handle.av.frame->format = cfg->handle.av.context->sample_fmt;
cfg->handle.av.frame->ch_layout = cfg->handle.av.context->ch_layout;
cfg->rem_in_size = av_samples_get_buffer_size(NULL,
cfg->handle.av.context->ch_layout.nb_channels,
cfg->handle.av.context->frame_size,
cfg->handle.av.context->sample_fmt, 0);
cfg->rem_in_data = av_malloc(cfg->rem_in_size);
if (cfg->rem_in_data == NULL) {
DPRINTF("Could not allocate %u bytes sample buffer\n",
(unsigned)cfg->rem_in_size);
goto av_error_3;
}
retval = avcodec_fill_audio_frame(cfg->handle.av.frame,
cfg->handle.av.context->ch_layout.nb_channels,
cfg->handle.av.context->sample_fmt,
cfg->rem_in_data, cfg->rem_in_size, 0);
if (retval < 0) {
DPRINTF("Could not setup audio frame\n");
goto av_error_4;
}
break;
av_error_4:
av_free(cfg->rem_in_data);
av_error_3:
av_frame_free(&cfg->handle.av.frame);
av_error_2:
avcodec_close(cfg->handle.av.context);
av_error_1:
avformat_free_context(cfg->handle.av.format);
cfg->handle.av.context = NULL;
av_error_0:
bt_close(pbe);
return (-1);
#endif
default:
bt_close(pbe);
return (-1);
}
return (0);
}
static int
bt_rec_transfer(struct voss_backend *pbe, void *ptr, int len)
{
return (bt_receive(pbe->arg, ptr, len, 1));
}
static int
bt_play_sbc_transfer(struct voss_backend *pbe, void *ptr, int len)
{
struct bt_config *cfg = pbe->arg;
struct sbc_encode *sbc = cfg->handle.sbc_enc;
int rem_size = 1;
int old_len = len;
int err = 0;
switch (cfg->blocks) {
case BLOCKS_4:
sbc->blocks = 4;
rem_size *= 4;
break;
case BLOCKS_8:
sbc->blocks = 8;
rem_size *= 8;
break;
case BLOCKS_12:
sbc->blocks = 12;
rem_size *= 12;
break;
default:
sbc->blocks = 16;
rem_size *= 16;
break;
}
switch (cfg->bands) {
case BANDS_4:
rem_size *= 4;
sbc->bands = 4;
break;
default:
rem_size *= 8;
sbc->bands = 8;
break;
}
sbc->framesamples = rem_size;
if (cfg->chmode != MODE_MONO) {
rem_size *= 2;
sbc->channels = 2;
} else {
sbc->channels = 1;
}
rem_size *= 2;
while (len > 0) {
int delta = len;
if (delta > (int)(rem_size - sbc->rem_len))
delta = (int)(rem_size - sbc->rem_len);
memcpy((char *)sbc->music_data + sbc->rem_len, ptr, delta);
ptr = (char *)ptr + delta;
len -= delta;
sbc->rem_len += delta;
if ((int)sbc->rem_len == rem_size) {
struct sbc_header *phdr = (struct sbc_header *)cfg->mtu_data;
uint32_t pkt_len;
uint32_t rem;
if (cfg->chmode == MODE_MONO)
sbc->channels = 1;
else
sbc->channels = 2;
pkt_len = sbc_encode_frame(cfg);
retry:
if (cfg->mtu_offset == 0) {
phdr->id = 0x80;
phdr->id2 = 0x60;
phdr->seqnumMSB = (uint8_t)(cfg->mtu_seqnumber >> 8);
phdr->seqnumLSB = (uint8_t)(cfg->mtu_seqnumber);
phdr->ts3 = (uint8_t)(cfg->mtu_timestamp >> 24);
phdr->ts2 = (uint8_t)(cfg->mtu_timestamp >> 16);
phdr->ts1 = (uint8_t)(cfg->mtu_timestamp >> 8);
phdr->ts0 = (uint8_t)(cfg->mtu_timestamp);
phdr->reserved0 = 0x01;
phdr->numFrames = 0;
cfg->mtu_seqnumber++;
cfg->mtu_offset += sizeof(*phdr);
}
rem = cfg->mtu - cfg->mtu_offset;
if (phdr->numFrames == 255 || rem < pkt_len) {
int xlen;
if (phdr->numFrames == 0)
return (-1);
do {
xlen = write(cfg->fd, cfg->mtu_data, cfg->mtu_offset);
} while (xlen < 0 && errno == EAGAIN);
if (xlen < 0)
return (-1);
cfg->mtu_offset = 0;
goto retry;
}
memcpy(cfg->mtu_data + cfg->mtu_offset, sbc->data, pkt_len);
memset(sbc->data, 0, pkt_len);
cfg->mtu_offset += pkt_len;
cfg->mtu_timestamp += sbc->framesamples;
phdr->numFrames++;
sbc->rem_len = 0;
}
}
if (err == 0)
return (old_len);
return (err);
}
#ifdef HAVE_LIBAV
static int
bt_play_aac_transfer(struct voss_backend *pbe, void *ptr, int len)
{
struct bt_config *cfg = pbe->arg;
struct aac_header {
uint8_t id;
uint8_t id2;
uint8_t seqnumMSB;
uint8_t seqnumLSB;
uint8_t ts3;
uint8_t ts2;
uint8_t ts1;
uint8_t ts0;
uint8_t sync3;
uint8_t sync2;
uint8_t sync1;
uint8_t sync0;
uint8_t fixed[8];
};
int old_len = len;
int err = 0;
while (len > 0) {
int delta = len;
int rem;
if (delta > (int)(cfg->rem_in_size - cfg->rem_in_len))
delta = (int)(cfg->rem_in_size - cfg->rem_in_len);
memcpy(cfg->rem_in_data + cfg->rem_in_len, ptr, delta);
ptr = (char *)ptr + delta;
len -= delta;
cfg->rem_in_len += delta;
if (cfg->rem_in_len == cfg->rem_in_size) {
struct aac_header *phdr = (struct aac_header *)cfg->mtu_data;
AVPacket *pkt;
uint8_t *pkt_buf;
int pkt_len;
pkt = av_packet_alloc();
err = avcodec_send_frame(cfg->handle.av.context,
cfg->handle.av.frame);
if (err < 0) {
DPRINTF("Error encoding audio frame\n");
return (-1);
}
phdr->id = 0x80;
phdr->id2 = 0x60;
phdr->seqnumMSB = (uint8_t)(cfg->mtu_seqnumber >> 8);
phdr->seqnumLSB = (uint8_t)(cfg->mtu_seqnumber);
phdr->ts3 = (uint8_t)(cfg->mtu_timestamp >> 24);
phdr->ts2 = (uint8_t)(cfg->mtu_timestamp >> 16);
phdr->ts1 = (uint8_t)(cfg->mtu_timestamp >> 8);
phdr->ts0 = (uint8_t)(cfg->mtu_timestamp);
phdr->sync3 = 0;
phdr->sync2 = 0;
phdr->sync1 = 0;
phdr->sync0 = 0;
phdr->fixed[0] = 0xfc;
phdr->fixed[1] = 0x00;
phdr->fixed[2] = 0x00;
phdr->fixed[3] = 0xb0;
phdr->fixed[4] = 0x90;
phdr->fixed[5] = 0x80;
phdr->fixed[6] = 0x03;
phdr->fixed[7] = 0x00;
cfg->mtu_seqnumber++;
cfg->mtu_offset = sizeof(*phdr);
rem = cfg->mtu - cfg->mtu_offset;
if (avio_open_dyn_buf(&cfg->handle.av.format->pb) == 0) {
static int once = 0;
if (!once++)
(void)avformat_write_header(cfg->handle.av.format, NULL);
av_write_frame(cfg->handle.av.format, pkt);
av_packet_unref(pkt);
pkt_len = avio_close_dyn_buf(cfg->handle.av.format->pb, &pkt_buf);
if (rem < pkt_len)
DPRINTF("Out of buffer space\n");
if (pkt_len >= 3 && rem >= pkt_len) {
int xlen;
memcpy(cfg->mtu_data + cfg->mtu_offset, pkt_buf + 3, pkt_len - 3);
av_free(pkt_buf);
cfg->mtu_offset += pkt_len - 3;
if (cfg->chmode != MODE_MONO)
cfg->mtu_timestamp += cfg->rem_in_size / 4;
else
cfg->mtu_timestamp += cfg->rem_in_size / 2;
do {
xlen = write(cfg->fd, cfg->mtu_data, cfg->mtu_offset);
} while (xlen < 0 && errno == EAGAIN);
if (xlen < 0)
return (-1);
} else {
av_free(pkt_buf);
}
} else {
av_packet_unref(pkt);
}
cfg->rem_in_len = 0;
}
}
if (err == 0)
return (old_len);
return (err);
}
#endif
static int
bt_play_transfer(struct voss_backend *pbe, void *ptr, int len)
{
struct bt_config *cfg = pbe->arg;
switch (cfg->codec) {
case CODEC_SBC:
return (bt_play_sbc_transfer(pbe, ptr, len));
#ifdef HAVE_LIBAV
case CODEC_AAC:
return (bt_play_aac_transfer(pbe, ptr, len));
#endif
default:
return (-1);
}
}
static void
bt_rec_delay(struct voss_backend *pbe __unused, int *pdelay)
{
*pdelay = -1;
}
static void
bt_play_delay(struct voss_backend *pbe __unused, int *pdelay)
{
*pdelay = -1;
}
struct voss_backend voss_backend_bt_rec = {
.open = bt_rec_open,
.close = bt_rec_close,
.transfer = bt_rec_transfer,
.delay = bt_rec_delay,
.arg = &bt_rec_cfg,
};
struct voss_backend voss_backend_bt_play = {
.open = bt_play_open,
.close = bt_play_close,
.transfer = bt_play_transfer,
.delay = bt_play_delay,
.arg = &bt_play_cfg,
};