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

📄 pcm_bluetooth.c

📁 实现bluez蓝牙profile需要的库
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* supported formats */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,					ARRAY_NELEMS(format_list), format_list);	if (err < 0)		return err;	/* supported channels */	channels = cfg.mode == CFG_MODE_MONO ? 1 : 2;	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,							channels, channels);	if (err < 0)		return err;	/* supported rate */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,							cfg.rate, cfg.rate);	if (err < 0)		return err;	/* supported block size */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,						a2dp->codesize, a2dp->codesize);	if (err < 0)		return err;	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,									2, 50);	if (err < 0)		return err;	return 0;}static int bluetooth_recvmsg_fd(struct bluetooth_data *data){	char cmsg_b[CMSG_SPACE(sizeof(int))], m;	int err, ret;	struct iovec iov = { &m, sizeof(m) };	struct msghdr msgh;	struct cmsghdr *cmsg;	memset(&msgh, 0, sizeof(msgh));	msgh.msg_iov = &iov;	msgh.msg_iovlen = 1;	msgh.msg_control = &cmsg_b;	msgh.msg_controllen = CMSG_LEN(sizeof(int));	ret = recvmsg(data->server.fd, &msgh, 0);	if (ret < 0) {		err = errno;		SNDERR("Unable to receive fd: %s (%d)", strerror(err), err);		return -err;	}	/* Receive auxiliary data in msgh */	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;			cmsg = CMSG_NXTHDR(&msgh, cmsg)) {		if (cmsg->cmsg_level == SOL_SOCKET				&& cmsg->cmsg_type == SCM_RIGHTS) {			data->stream.fd = (*(int *) CMSG_DATA(cmsg));			DBG("stream_fd=%d", data->stream.fd);			return 0;		}	}	return -EINVAL;}static int bluetooth_a2dp_init(struct bluetooth_data *data,				struct ipc_codec_sbc *sbc){	struct bluetooth_a2dp *a2dp = &data->a2dp;	struct ipc_data_cfg *cfg = &data->cfg;	if (cfg == NULL) {		SNDERR("Error getting codec parameters");		return -1;	}	if (cfg->codec != CFG_CODEC_SBC)		return -1;	/* FIXME: init using flags? */	sbc_init(&a2dp->sbc, 0);	a2dp->sbc.rate = cfg->rate;	a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2;	if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO)		a2dp->sbc.joint = 1;	a2dp->sbc.allocation = sbc->allocation;	a2dp->sbc.subbands = sbc->subbands;	a2dp->sbc.blocks = sbc->blocks;	a2dp->sbc.bitpool = sbc->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_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,				snd_config_t *conf){	struct ipc_data_cfg *cfg = (void *) pkt->data;	struct ipc_codec_sbc *sbc = (void *) cfg->data;	snd_config_iterator_t i, next;	const char *addr, *pref;	const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool;	switch (stream) {	case SND_PCM_STREAM_PLAYBACK:		cfg->fd_opt = CFG_FD_OPT_WRITE;		break;	case SND_PCM_STREAM_CAPTURE:		cfg->fd_opt = CFG_FD_OPT_READ;		break;	}	snd_config_for_each(i, next, conf) {		snd_config_t *n = snd_config_iterator_entry(i);		const char *id;		if (snd_config_get_id(n, &id) < 0)			continue;		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)			continue;		if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) {			if (snd_config_get_string(n, &addr) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			strncpy(pkt->device, addr, 18);			continue;		}		if (strcmp(id, "profile") == 0) {			if (snd_config_get_string(n, &pref) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0)				pkt->role = PKT_ROLE_AUTO;			else if (strcmp(pref, "voice") == 0 ||						strcmp(pref, "hfp") == 0) {				pkt->role = PKT_ROLE_VOICE;			} else if (strcmp(pref, "hifi") == 0 ||						strcmp(pref, "a2dp") == 0)				pkt->role = PKT_ROLE_HIFI;			continue;		}		if (strcmp(id, "rate") == 0) {			if (snd_config_get_string(n, &rate) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			cfg->rate = atoi(rate);			continue;		}		if (strcmp(id, "mode") == 0) {			if (snd_config_get_string(n, &mode) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0)				cfg->mode = CFG_MODE_AUTO;			else if (strcmp(pref, "mono") == 0)				cfg->mode = CFG_MODE_MONO;			else if (strcmp(pref, "dual") == 0)				cfg->mode = CFG_MODE_DUAL_CHANNEL;			else if (strcmp(pref, "stereo") == 0)				cfg->mode = CFG_MODE_STEREO;			else if (strcmp(pref, "joint") == 0)				cfg->mode = CFG_MODE_JOINT_STEREO;			continue;		}		if (strcmp(id, "allocation") == 0) {			if (snd_config_get_string(n, &allocation) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0)				sbc->allocation = CFG_ALLOCATION_AUTO;			else if (strcmp(pref, "loudness") == 0)				sbc->allocation = CFG_ALLOCATION_LOUDNESS;			else if (strcmp(pref, "snr") == 0)				sbc->allocation = CFG_ALLOCATION_SNR;			continue;		}		if (strcmp(id, "subbands") == 0) {			if (snd_config_get_string(n, &subbands) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			sbc->subbands = atoi(subbands);			continue;		}		if (strcmp(id, "blocks") == 0) {			if (snd_config_get_string(n, &blocks) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			sbc->blocks = atoi(blocks);			continue;		}		if (strcmp(id, "bitpool") == 0) {			if (snd_config_get_string(n, &bitpool) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			sbc->bitpool = atoi(bitpool);			continue;		}		SNDERR("Unknown field %s", id);		return -EINVAL;	}	pkt->length = sizeof(*cfg) + sizeof(*sbc);	pkt->type = PKT_TYPE_CFG_REQ;	pkt->error = PKT_ERROR_NONE;	return 0;}static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream,				snd_config_t *conf){	int ret, total;	char buf[IPC_MTU];	struct ipc_packet *pkt = (void *) buf;	struct ipc_data_cfg *cfg = (void *) pkt->data;	struct ipc_codec_sbc *sbc = (void *) cfg->data;	DBG("Sending PKT_TYPE_CFG_REQ...");	memset(buf, 0, sizeof(buf));	ret = bluetooth_cfg_init(pkt, stream, conf);	if (ret < 0)		return -ret;	ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0);	if (ret < 0)		return -errno;	else if (ret == 0)		return -EIO;	DBG("OK - %d bytes sent. Waiting for response...", ret);	memset(buf, 0, sizeof(buf));	ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*cfg), 0);	if (ret < 0)		return -errno;	else if (ret == 0)		return -EIO;	total = ret;	if (pkt->type != PKT_TYPE_CFG_RSP) {		SNDERR("Unexpected packet type %d received", pkt->type);		return -EINVAL;	}	if (pkt->error != PKT_ERROR_NONE) {		SNDERR("Error %d while configuring device", pkt->error);		return -pkt->error;	}	if (cfg->codec != CFG_CODEC_SBC)		goto done;	ret = recv(data->server.fd, sbc, sizeof(*sbc), 0);	if (ret < 0)		return -errno;	else if (ret == 0)		return -EIO;	total += ret;done:	DBG("OK - %d bytes received", total);	if (pkt->length != (total - sizeof(struct ipc_packet))) {		SNDERR("Error while configuring device: packet size doesn't match");		return -EINVAL;	}	memcpy(&data->cfg, cfg, sizeof(*cfg));	DBG("Device configuration:");	DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u",			data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len,					data->cfg.sample_size, data->cfg.rate);	if (data->cfg.codec == CFG_CODEC_SBC) {		ret = bluetooth_a2dp_init(data, sbc);		if (ret < 0)			return ret;	}	ret = bluetooth_recvmsg_fd(data);	if (ret < 0)		return ret;	if (data->stream.fd == -1) {		SNDERR("Error while configuring device: could not acquire audio socket");		return -EINVAL;	}	/* It is possible there is some outstanding	data in the pipe - we have to empty it */	while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len,				MSG_DONTWAIT) > 0);	memset(data->buffer, 0, sizeof(data->buffer));	return 0;}static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,				snd_config_t *conf){	int sk, err;	struct sockaddr_un addr = {		AF_UNIX, IPC_SOCKET_NAME	};	if (!data)		return -EINVAL;	memset(data, 0, sizeof(struct bluetooth_data));	data->server.fd = -1;	data->stream.fd = -1;	sk = socket(PF_LOCAL, SOCK_STREAM, 0);	if (sk < 0) {		err = errno;		SNDERR("Cannot open socket: %s (%d)", strerror(err), err);		return -err;	}	DBG("Connecting to address: %s", addr.sun_path + 1);	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		err = errno;		SNDERR("Connection fail", strerror(err), err);		close(sk);		return -err;	}	data->server.fd = sk;	data->server.events = POLLIN;	data->pipefd[0] = -1;	data->pipefd[1] = -1;	if (pipe(data->pipefd) < 0)		return -errno;	if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0)		return -errno;	if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0)		return -errno;	return bluetooth_cfg(data, stream, conf);}SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth){	struct bluetooth_data *data;	int err;	DBG("Bluetooth PCM plugin (%s)",		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");	data = malloc(sizeof(struct bluetooth_data));	if (!data) {		err = -ENOMEM;		goto error;	}	err = bluetooth_init(data, stream, conf);	if (err < 0)		goto error;	data->io.version = SND_PCM_IOPLUG_VERSION;	data->io.name = "Bluetooth Audio Device";	data->io.mmap_rw = 0; /* No direct mmap communication */	data->io.private_data = data;	if (data->cfg.codec == CFG_CODEC_SBC)		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?			&bluetooth_a2dp_playback :			&bluetooth_a2dp_capture;	else		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?			&bluetooth_hsp_playback :			&bluetooth_hsp_capture;	err = snd_pcm_ioplug_create(&data->io, name, stream, mode);	if (err < 0)		goto error;	if (data->cfg.codec == CFG_CODEC_SBC)		err = bluetooth_a2dp_hw_constraint(&data->io);	else		err = bluetooth_hsp_hw_constraint(&data->io);	if (err < 0) {		snd_pcm_ioplug_delete(&data->io);		goto error;	}	*pcmp = data->io.pcm;	return 0;error:	bluetooth_exit(data);	return err;}SND_PCM_PLUGIN_SYMBOL(bluetooth);

⌨️ 快捷键说明

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