⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcm_bluetooth.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 3 页
字号:
		if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)			cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;	}	if (!cap->channel_mode) {		DBG("No supported channel modes");		return -1;	}	if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)		cap->block_length = BT_A2DP_BLOCK_LENGTH_16;	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)		cap->block_length = BT_A2DP_BLOCK_LENGTH_12;	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)		cap->block_length = BT_A2DP_BLOCK_LENGTH_8;	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)		cap->block_length = BT_A2DP_BLOCK_LENGTH_4;	else {		DBG("No supported block lengths");		return -1;	}	if (cap->subbands & BT_A2DP_SUBBANDS_8)		cap->subbands = BT_A2DP_SUBBANDS_8;	else if (cap->subbands & BT_A2DP_SUBBANDS_4)		cap->subbands = BT_A2DP_SUBBANDS_4;	else {		DBG("No supported subbands");		return -1;	}	if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)		cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;	else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)		cap->allocation_method = BT_A2DP_ALLOCATION_SNR;	min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),							cap->max_bitpool);	cap->min_bitpool = min_bitpool;	cap->max_bitpool = max_bitpool;	return 0;}static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,					snd_pcm_hw_params_t *params){	struct bluetooth_data *data = io->private_data;	struct bluetooth_a2dp *a2dp = &data->a2dp;	char buf[BT_AUDIO_IPC_PACKET_SIZE];	bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;	struct bt_setconfiguration_req *setconf_req = (void*) buf;	struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf;	unsigned int rate, channels;	int err, dir;	sbc_capabilities_t active_capabilities;	DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",					io->period_size, io->buffer_size);	/* FIXME: this needs to be really implemented (take into account	real asoundrc settings + ALSA hw settings ) once server side sends us	more than one possible configuration */	snd_pcm_hw_params_get_rate(params, &rate, &dir);	snd_pcm_hw_params_get_channels(params, &channels);	err = select_sbc_params(&a2dp->sbc_capabilities, rate, channels);	if (err < 0)		return err;	active_capabilities = a2dp->sbc_capabilities;	memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);	setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;	strncpy(setconf_req->device, data->alsa_config.device, 18);	setconf_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP;	setconf_req->sbc_capabilities = active_capabilities;	setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ?			BT_CAPABILITIES_ACCESS_MODE_WRITE :			BT_CAPABILITIES_ACCESS_MODE_READ);	err = audioservice_send(data->server.fd, &setconf_req->h);	if (err < 0)		return err;	err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h,					BT_SETCONFIGURATION_RSP);	if (err < 0)		return err;	if (rsp_hdr->posix_errno != 0) {		SNDERR("BT_SETCONFIGURATION failed : %s(%d)",					strerror(rsp_hdr->posix_errno),					rsp_hdr->posix_errno);		return -rsp_hdr->posix_errno;	}	data->transport = setconf_rsp->transport;	data->link_mtu = setconf_rsp->link_mtu;	/* Setup SBC encoder now we agree on parameters */	if (a2dp->sbc_initialized)		sbc_reinit(&a2dp->sbc, 0);	else		sbc_init(&a2dp->sbc, 0);	a2dp->sbc_initialized = 1;	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000)		a2dp->sbc.rate = 16000;	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000)		a2dp->sbc.rate = 32000;	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100)		a2dp->sbc.rate = 44100;	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000)		a2dp->sbc.rate = 48000;	if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)		a2dp->sbc.channels = 1;	else		a2dp->sbc.channels = 2;	if (active_capabilities.channel_mode &			(BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO))		a2dp->sbc.joint = 1;	else		a2dp->sbc.joint = 0;	a2dp->sbc.allocation = active_capabilities.allocation_method					== BT_A2DP_ALLOCATION_SNR ? 0x01 : 0x00;	switch (active_capabilities.subbands) {	case BT_A2DP_SUBBANDS_4:		a2dp->sbc.subbands = 4;		break;	case BT_A2DP_SUBBANDS_8:		a2dp->sbc.subbands = 8;		break;	}	switch (active_capabilities.block_length) {	case BT_A2DP_BLOCK_LENGTH_4:		a2dp->sbc.blocks = 4;		break;	case BT_A2DP_BLOCK_LENGTH_8:		a2dp->sbc.blocks = 8;		break;	case BT_A2DP_BLOCK_LENGTH_12:		a2dp->sbc.blocks = 12;		break;	case BT_A2DP_BLOCK_LENGTH_16:		a2dp->sbc.blocks = 16;		break;	}	a2dp->sbc.bitpool = active_capabilities.max_bitpool;	a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks *						a2dp->sbc.channels * 2;	a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);	DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",		a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks,		a2dp->sbc.bitpool);	return 0;}static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io,					struct pollfd *pfd, unsigned int space){	struct bluetooth_data *data = io->private_data;	assert(io);	if (space < 1)		return 0;	pfd[0].fd = data->stream.fd;	pfd[0].events = POLLIN;	pfd[0].revents = 0;	return 1;}static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED,					struct pollfd *pfds, unsigned int nfds,					unsigned short *revents){	assert(pfds && nfds == 1 && revents);	*revents = pfds[0].revents;	return 0;}static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io,					struct pollfd *pfd, unsigned int space){	struct bluetooth_data *data = io->private_data;	DBG("");	assert(data->pipefd[0] >= 0);	if (space < 1)		return 0;	pfd[0].fd = data->pipefd[0];	pfd[0].events = POLLIN;	pfd[0].revents = 0;	return 1;}static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io,					struct pollfd *pfds, unsigned int nfds,					unsigned short *revents){	static char buf[1];	int ret;	DBG("");	assert(pfds);	assert(nfds == 1);	assert(revents);	assert(pfds[0].fd >= 0);	if (io->state != SND_PCM_STATE_PREPARED)		ret = read(pfds[0].fd, buf, 1);	*revents = (pfds[0].revents & ~POLLIN) | POLLOUT;	return 0;}static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,				const snd_pcm_channel_area_t *areas,				snd_pcm_uframes_t offset,				snd_pcm_uframes_t size){	struct bluetooth_data *data = io->private_data;	snd_pcm_uframes_t frames_to_write, ret;	unsigned char *buff;	int nrecv, frame_size = 0;	DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u",			areas->step, areas->first, offset, size, io->nonblock);	if (data->count > 0)		goto proceed;	frame_size = areas->step / 8;	nrecv = recv(data->stream.fd, data->buffer, data->link_mtu,			MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0));	if (nrecv < 0) {		ret = (errno == EPIPE) ? -EIO : -errno;		goto done;	}	if (nrecv != data->link_mtu) {		ret = -EIO;		SNDERR(strerror(-ret));		goto done;	}	/* Increment hardware transmition pointer */	data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) %				io->buffer_size;proceed:	buff = (unsigned char *) areas->addr +			(areas->first + areas->step * offset) / 8;	if ((data->count + size * frame_size) <= data->link_mtu)		frames_to_write = size;	else		frames_to_write = (data->link_mtu - data->count) / frame_size;	memcpy(buff, data->buffer + data->count, frame_size * frames_to_write);	data->count += (frame_size * frames_to_write);	data->count %= data->link_mtu;	/* Return written frames count */	ret = frames_to_write;done:	DBG("returning %lu", ret);	return ret;}static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,				const snd_pcm_channel_area_t *areas,				snd_pcm_uframes_t offset,				snd_pcm_uframes_t size){	struct bluetooth_data *data = io->private_data;	snd_pcm_sframes_t ret = 0;	snd_pcm_uframes_t frames_to_read;	uint8_t *buff;	int rsend, frame_size;	DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u",			areas->step, areas->first, offset, size, io->nonblock);	if (io->hw_ptr > io->appl_ptr) {		ret = bluetooth_playback_stop(io);		if (ret == 0)			ret = -EPIPE;		goto done;	}	frame_size = areas->step / 8;	if ((data->count + size * frame_size) <= data->link_mtu)		frames_to_read = size;	else		frames_to_read = (data->link_mtu - data->count) / frame_size;	DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);	/* Ready for more data */	buff = (uint8_t *) areas->addr +			(areas->first + areas->step * offset) / 8;	memcpy(data->buffer + data->count, buff, frame_size * frames_to_read);	/* Remember we have some frames in the pipe now */	data->count += frames_to_read * frame_size;	if (data->count != data->link_mtu) {		ret = frames_to_read;		goto done;	}	rsend = send(data->stream.fd, data->buffer, data->link_mtu,			io->nonblock ? MSG_DONTWAIT : 0);	if (rsend > 0) {		/* Reset count pointer */		data->count = 0;		ret = frames_to_read;	} else if (rsend < 0)		ret = (errno == EPIPE) ? -EIO : -errno;	else		ret = -EIO;done:	DBG("returning %ld", ret);	return ret;}static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io,				const snd_pcm_channel_area_t *areas,				snd_pcm_uframes_t offset,				snd_pcm_uframes_t size){	snd_pcm_uframes_t ret = 0;	return ret;}static int avdtp_write(struct bluetooth_data *data){	int ret = 0;	struct rtp_header *header;	struct rtp_payload *payload;	struct bluetooth_a2dp *a2dp = &data->a2dp;	header = (void *) a2dp->buffer;	payload = (void *) (a2dp->buffer + sizeof(*header));	memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));	payload->frame_count = a2dp->frame_count;	header->v = 2;	header->pt = 1;	header->sequence_number = htons(a2dp->seq_num);	header->timestamp = htonl(a2dp->nsamples);	header->ssrc = htonl(1);        ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);	if (ret < 0) {		DBG("send returned %d errno %s.", ret, strerror(errno));		ret = -errno;	}	/* Reset buffer of data to send */	a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);	a2dp->frame_count = 0;	a2dp->samples = 0;	a2dp->seq_num++;	return ret;}static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,				const snd_pcm_channel_area_t *areas,				snd_pcm_uframes_t offset, snd_pcm_uframes_t size){	struct bluetooth_data *data = io->private_data;	struct bluetooth_a2dp *a2dp = &data->a2dp;	snd_pcm_sframes_t ret = 0;	snd_pcm_uframes_t frames_to_read, frames_left = size;	int frame_size, encoded, written;	uint8_t *buff;	DBG("areas->step=%u areas->first=%u offset=%lu size=%lu",				areas->step, areas->first, offset, size);	DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr,			io->appl_ptr - io->hw_ptr);	if (io->hw_ptr > io->appl_ptr) {		ret = bluetooth_playback_stop(io);		if (ret == 0)			ret = -EPIPE;		data->reset = 1;		goto done;	}	/* Check if we should autostart */	if (io->state == SND_PCM_STATE_PREPARED) {		snd_pcm_sw_params_t *swparams;		snd_pcm_uframes_t threshold;		snd_pcm_sw_params_malloc(&swparams);		if (!snd_pcm_sw_params_current(io->pcm, swparams) &&				!snd_pcm_sw_params_get_start_threshold(swparams,								&threshold)) {			if (io->appl_ptr >= threshold) {				ret = snd_pcm_start(io->pcm);				if (ret != 0)					goto done;			}		}		snd_pcm_sw_params_free(swparams);	}	while (frames_left > 0) {		frame_size = areas->step / 8;		if ((data->count + frames_left * frame_size) <= a2dp->codesize)			frames_to_read = frames_left;		else			frames_to_read = (a2dp->codesize - data->count) / frame_size;		DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);		DBG("a2dp.count=%d data.link_mtu=%d", a2dp->count, data->link_mtu);		/* FIXME: If state is not streaming then return */		/* Ready for more data */		buff = (uint8_t *) areas->addr +			(areas->first + areas->step * (offset + ret)) / 8;		memcpy(data->buffer + data->count, buff,				frame_size * frames_to_read);		/* Remember we have some frames in the pipe now */		data->count += frames_to_read * frame_size;		if (data->count != a2dp->codesize) {			ret = frames_to_read;			goto done;		}		/* Enough data to encode (sbc wants 1k blocks) */		encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize,					a2dp->buffer + a2dp->count,					sizeof(a2dp->buffer) - a2dp->count,					&written);		if (encoded <= 0) {			DBG("Encoding error %d", encoded);			goto done;		}		data->count -= encoded;		a2dp->count += written;		a2dp->frame_count++;		a2dp->samples += encoded / frame_size;		a2dp->nsamples += encoded / frame_size;		DBG("encoded=%d  written=%d count=%d", encoded,				written, a2dp->count);		/* No space left for another frame then send */		if (a2dp->count + written >= data->link_mtu) {			avdtp_write(data);			DBG("sending packet %d, count %d, link_mtu %u",					a2dp->seq_num, a2dp->count,					data->link_mtu);		}		ret += frames_to_read;		frames_left -= frames_to_read;	}	/* note: some ALSA apps will get confused otherwise */	if (ret > size)		ret = size;done:	DBG("returning %ld", ret);	return ret;}static int bluetooth_playback_delay(snd_pcm_ioplug_t *io,					snd_pcm_sframes_t *delayp){	DBG("");	/* This updates io->hw_ptr value using pointer() function */	snd_pcm_hwsync(io->pcm);	*delayp = io->appl_ptr - io->hw_ptr;	if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) {		io->callback->stop(io);		io->state = SND_PCM_STATE_XRUN;		*delayp = 0;	}	/* This should never fail, ALSA API is really not	prepared to handle a non zero return value */	return 0;}static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = {	.start			= bluetooth_playback_start,	.stop			= bluetooth_playback_stop,	.pointer		= bluetooth_pointer,	.close			= bluetooth_close,	.hw_params		= bluetooth_hsp_hw_params,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -