📄 pcm_a2dpd.c
字号:
/*** BlueZ - Bluetooth protocol stack for Linux** Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>*** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* License as published by the Free Software Foundation; either* version 2.1 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU* Lesser General Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA**/#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <malloc.h>#include <signal.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <syslog.h>#include <alsa/asoundlib.h>#include <alsa/pcm_external.h>#include <alsa/timer.h>#include "../a2dp.h"#include "a2dpd_protocol.h"#include "a2dpd_timer.h"#include "a2dpd_ipc.h"int g_bdebug = 0;typedef struct snd_pcm_a2dp { snd_pcm_ioplug_t io; int opened_for; int streamid; int rateconstraint; int rate; int sk; int channels; snd_pcm_sframes_t num; unsigned int frame_bytes; TIMERINFO TimerInfos;} snd_pcm_a2dp_t;static int a2dp_disconnect(snd_pcm_a2dp_t * a2dp){ close_socket(&a2dp->sk); return 0;}static int a2dp_connect(snd_pcm_a2dp_t * a2dp){ if (a2dp->sk <= 0) { int sockfd = make_client_socket(); a2dp->sk = -1; if (sockfd > 0) { int32_t client_type = a2dp->opened_for; if (send_socket(sockfd, &client_type, sizeof(client_type)) == sizeof(client_type)) { // Fill stream informations AUDIOSTREAMINFOS StreamInfos = INVALIDAUDIOSTREAMINFOS; StreamInfos.rate = a2dp->rate; StreamInfos.channels = a2dp->channels; StreamInfos.bitspersample = a2dp->frame_bytes/a2dp->channels; StreamInfos.streamid = a2dp->streamid; switch(a2dp->io.format) { case SND_PCM_FORMAT_S8: StreamInfos.format = A2DPD_PCM_FORMAT_S8; break; case SND_PCM_FORMAT_U8: StreamInfos.format = A2DPD_PCM_FORMAT_U8; break; case SND_PCM_FORMAT_S16_LE: StreamInfos.format = A2DPD_PCM_FORMAT_S16_LE; break; default: StreamInfos.format = A2DPD_PCM_FORMAT_UNKNOWN; break; } if (send_socket(sockfd, &StreamInfos, sizeof(StreamInfos)) == sizeof(StreamInfos)) { a2dp->sk = sockfd; DBG("Connected a2dp %p, sk %d, fps %f", a2dp, a2dp->sk, a2dp->TimerInfos.fps); } else { //syslog(LOG_WARNING, "Couldn't send stream informations"); a2dp_disconnect(a2dp); } } else { close_socket(&sockfd); //syslog(LOG_WARNING, "Connected a2dp %p, sk %d, Authorisation failed", a2dp, a2dp->sk); } } else { //syslog(LOG_ERR, "Socket failed a2dp %p, sk %d", a2dp, a2dp->sk); } } return 0;}static inline snd_pcm_a2dp_t *a2dp_alloc(void){ snd_pcm_a2dp_t *a2dp; a2dp = malloc(sizeof(*a2dp)); if (a2dp) { memset(a2dp, 0, sizeof(*a2dp)); a2dp->sk = -1; } return a2dp;}static inline void a2dp_free(snd_pcm_a2dp_t * a2dp){ a2dp_disconnect(a2dp); free(a2dp);}static int a2dp_start(snd_pcm_ioplug_t * io){ //snd_pcm_a2dp_t *a2dp = io->private_data; //FIXME return 0;}static int a2dp_stop(snd_pcm_ioplug_t * io){ //snd_pcm_a2dp_t *a2dp = io->private_data; return 0;}static snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t * io){ snd_pcm_a2dp_t *a2dp = io->private_data; //DBG("returning a2dp->num = %lu", a2dp->num); return a2dp->num;}// This is the main transfer func which does the transfer and sleep jobstatic snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t * io, char *buf, int32_t datatoread){ snd_pcm_a2dp_t *a2dp = io->private_data; int transfer = 0; int delay = 0; AUDIOPACKETHEADER hdr; gettimeofday(&hdr.packet_date, NULL); hdr.pcm_buffer_size = datatoread; // Connect if needed and send a2dp_connect(a2dp); if (transfer >= 0) transfer = send_socket(a2dp->sk, &hdr, sizeof(hdr)); if (transfer >= 0) transfer = send_socket(a2dp->sk, buf, hdr.pcm_buffer_size); // Disconnect if error detected if (transfer < 0) a2dp_disconnect(a2dp); // The data are sent to the daemon that act as a proxy thus we double transfer delay to compensate latency a2dp_timer_notifyframe(&a2dp->TimerInfos); delay = a2dp_timer_sleep(&a2dp->TimerInfos, 4*A2DPTIMERPREDELAY); //usleep(delay); // Stats if (a2dp->TimerInfos.display > 0) { if (errno != 0 || transfer <= 0) { //syslog(LOG_INFO, "send_socket(%d bytes)=%d (errno=%d:%s)", datatoread, transfer, errno, strerror(errno)); } } // update pointer, tell alsa we're done a2dp->num += datatoread / a2dp->frame_bytes; return datatoread / a2dp->frame_bytes;}// also works but sleeps between transfers// This is the main transfer func which does the transfer and sleep jobstatic snd_pcm_sframes_t a2dp_transfer(snd_pcm_ioplug_t * io, const snd_pcm_channel_area_t * areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t nframes){ snd_pcm_a2dp_t *a2dp = io->private_data; int i = 0; snd_pcm_sframes_t totaltransfered = 0; while (i++ < 1 && totaltransfered < nframes) { char *buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8; int datatoread = min(A2DPD_BLOCK_SIZE, nframes * a2dp->frame_bytes); snd_pcm_sframes_t transfered = a2dp_transfer2(io, buf, datatoread); if (transfered > 0) { offset += transfered; totaltransfered += transfered; } else { break; } } return totaltransfered;}// This is the main transfer func which does the transfer and sleep jobstatic snd_pcm_sframes_t a2dp_read2(snd_pcm_ioplug_t * io, char *buf, int32_t datatoread){ snd_pcm_a2dp_t *a2dp = io->private_data; int transfer = 0; uint32_t pkt_size = 0; int dummy = 0; int delay = 0; // Connect if needed and send a2dp_connect(a2dp); DBG("writing 2"); if(transfer>=0) transfer = send_socket(a2dp->sk, &dummy, sizeof(dummy)); DBG("reading 2 = %d", transfer); if(transfer>=0) transfer = recv_socket(a2dp->sk, &pkt_size, sizeof(pkt_size)); DBG("got size %d:%d", transfer, pkt_size); if(transfer>=0) { if(pkt_size<datatoread) { transfer = recv_socket(a2dp->sk, buf, min(datatoread, pkt_size)); DBG("got data %d/%d", transfer, pkt_size); } else { DBG("invalid pkt size"); } } // update pointer, tell alsa we're done if(transfer>=0) a2dp->num += transfer / a2dp->frame_bytes; // Disconnect if error detected if (transfer < 0) { a2dp_disconnect(a2dp); transfer = 0; } a2dp_timer_notifyframe(&a2dp->TimerInfos); delay = a2dp_timer_sleep(&a2dp->TimerInfos, 0); //usleep(delay); return transfer / a2dp->frame_bytes;}static snd_pcm_sframes_t a2dp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t nframes){ snd_pcm_a2dp_t *a2dp = io->private_data; DBG("reading"); char *buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8; int datatoread = min(A2DPD_BLOCK_SIZE, nframes * a2dp->frame_bytes); snd_pcm_uframes_t totaltransfered = a2dp_read2(io, buf, datatoread); return totaltransfered;}/*// DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); if(bt_headset->sco_data_count == 0) { int nrecv = recv(scofd, &bt_headset->pkt, sizeof(sco_packet_t), MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0 )); if(nrecv == sizeof(sco_packet_t)) { ret = 0; // Increment hardware transmition pointer bt_headset->hw_ptr = (bt_headset->hw_ptr + SCO_PACKET_LEN / 2) % io->buffer_size; } else if(nrecv > 0) { ret = -EIO; SNDERR(strerror(-ret)); } else if(nrecv == -1 && errno == EAGAIN) { ret = -EAGAIN; } else { // nrecv < 0 // EPIPE means device underrun in ALSA world. But we mean we lost contact // with server, so we have to find another error cod ret = (errno == EPIPE ? -EIO : -errno); SYSERR("Lost contact with headsetd"); } } if(ret == 0) { // Still ok, proceed snd_pcm_uframes_t frames_to_write; unsigned char *buff; buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; if((bt_headset->sco_data_count + 2 * size) <= SCO_PACKET_LEN) { frames_to_write = size; } else { frames_to_write = (SCO_PACKET_LEN - bt_headset->sco_data_count) / 2; } memcpy(buff, bt_headset->pkt.sco_data + bt_headset->sco_data_count, areas->step / 8 * frames_to_write); bt_headset->sco_data_count += (areas->step / 8 * frames_to_write); bt_headset->sco_data_count %= SCO_PACKET_LEN; // Return written frames count ret = frames_to_write; } return totaltransfered;}*/static int a2dp_close(snd_pcm_ioplug_t * io){ snd_pcm_a2dp_t *a2dp = io->private_data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -