/*- * Copyright (c) 2015-2019 Hans Petter Selasky * Copyright (c) 2015 Nathanial Sloss * Copyright (c) 2006 Itronix Inc * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #define L2CAP_SOCKET_CHECKED #include #include #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; /* wait for service interval, if any */ 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); /* copy out samples, if any */ 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; } /* TODO: Support fragmented SBC frames */ 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); /* disconnected */ } /* verify RTP header */ 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); } /* check range */ 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); } /* Initialize attribute values array */ 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]; } /* Do SDP Service Search Attribute Request */ n = sdp_search(ss, 1, &service_class, 1, bt_attrs, BT_NUM_VALUES, values); if (n != 0) { DPRINTF("SDP search failed\n"); goto done; } /* Print attributes values */ 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; } /* skip prefix */ 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); /* setup codec */ 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; } /*avcodec_get_context_defaults3(cfg->handle.av.context,*/ /*cfg->handle.av.codec);*/ 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; } /* store number of samples per frame */ sbc->framesamples = rem_size; if (cfg->chmode != MODE_MONO) { rem_size *= 2; sbc->channels = 2; } else { sbc->channels = 1; } rem_size *= 2; /* 16-bit samples */ while (len > 0) { int delta = len; if (delta > (int)(rem_size - sbc->rem_len)) delta = (int)(rem_size - sbc->rem_len); /* copy in samples */ memcpy((char *)sbc->music_data + sbc->rem_len, ptr, delta); ptr = (char *)ptr + delta; len -= delta; sbc->rem_len += delta; /* check if buffer is full */ 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; /* RTP v2 */ phdr->id2 = 0x60; /* payload type 96. */ 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); } /* compute bytes left */ 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; /* check if buffer is full */ 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;/* RTP v2 */ phdr->id2 = 0x60; /* payload type 96. */ 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); /* compute bytes left */ 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); } /* reset remaining length */ 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) { /* TODO */ *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, };