📄 a2dpd.c
字号:
/*** A2DPD - Bluetooth A2DP daemon for Linux** Copyright (C) 2006 Frédéric DALLEAU <frederic.dalleau@palmsource.com>** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program 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 General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <unistd.h>#include <string.h>#include <strings.h>#include <errno.h>#include <signal.h>#include <pthread.h>#include <fcntl.h>#include <getopt.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/poll.h>#include "a2dpd_output_a2dp.h"#include "a2dpd_output_alsa.h"#include "a2dpd_output_sco.h"#include "a2dpd_protocol.h"#include "a2dpd_uinput.h"#include "a2dpd_dbus.h"#include "a2dpd_mixer.h"#include "a2dpd_tools.h"#include "a2dpd_timer.h"#include "a2dpd_ipc.h"#include "../avrcp.h"#define MAXBLUETOOTHDEVICES (3)#define MAXCLIENTSPERDEVICE (8)// Using less than 64 (even 32) in this value results in some gaps in the sound.// Probably the ringbuffer is full.//FIXME Monitor the ring buffer to understand why it is full// The plugin sends the sound a few ms before it must be played// This may be the cause (See [4*]A2DPTIMERPREDELAY)#define MAXCLIENTSRINGSIZE (64)#define POOLENTRYSIZE_A2DP (A2DPD_BLOCK_SIZE)#define POOLENTRYSIZE_SCO (48)#define A2DPD_CONFIG_FILE ".a2dpdrc"enum { DEVICE_STATE_NOSOUND, DEVICE_STATE_SOUND };enum { CLIENT_STATE_DISCONNECTED, CLIENT_STATE_NEW, CLIENT_STATE_STREAMINGSETUP, CLIENT_STATE_STREAMING, CLIENT_STATE_CAPTURESETUP, CLIENT_STATE_CAPTURE };static char g_sOutputFilename[512] = "";static char g_srcfilename[512] = "";static char g_sCmdPlay[512] = "";static char g_sCmdPause[512] = "";static char g_sCmdStop[512] = "";static char g_sCmdPrev[512] = "";static char g_sCmdNext[512] = "";static char g_sCmdNew[512] = "";static int g_brereadconfig = -1;static int g_breversestereo = -1;static int g_autoconnect = -1;int g_bdebug = -1;static int g_stdin = -1;////////////////////////////////////////////////////////////////////////////////////// AVRCP CODE BEGIN //////////////////////////////////////////////////////////////////////////////////////////////////////////// Prepare packet headersstatic void init_response(struct avctp_header *header){ header->ipid = 0; header->cr = AVCTP_RESPONSE_FRAME; header->packet_type = PACKET_TYPE_SINGLE;}// This function handle the bluetooth connectionint a2dp_handle_avrcp_message(int sockfd){ char lpFrame[A2DPMAXIMUMTRANSFERUNITSIZE]; int iReceived = recv(sockfd, lpFrame, sizeof(lpFrame), 0); if (iReceived > 0) { struct avc_frame frame = *((struct avc_frame *) lpFrame); // Handle message if (frame.ctype == CMD_PASSTHROUGH) { switch (frame.operand0) { #define CASEPRINT_DN(x) case (x): DBG("%s DOWN", #x); a2dpd_signal_command("down", #x); break; #define CASEPRINT_UP(x) case (x+128): DBG("%s UP", #x); a2dpd_signal_command("up", #x); break; #define CASEPRINT(x) CASEPRINT_DN(x) CASEPRINT_UP(x) #define CASEDOWN(x) case (x): DBG("%s DOWN", #x); a2dpd_signal_command("down", #x); #define CASEUP(x) case (x+128): DBG("%s UP", #x); a2dpd_signal_command("up", #x); CASEPRINT(avrcp_select); CASEPRINT(avrcp_up); CASEPRINT(avrcp_down); CASEPRINT(avrcp_left); CASEPRINT(avrcp_right); CASEPRINT(avrcp_right_up); CASEPRINT(avrcp_right_down); CASEPRINT(avrcp_left_up); CASEPRINT(avrcp_left_down); CASEPRINT(avrcp_root_menu); CASEPRINT(avrcp_setup_menu); CASEPRINT(avrcp_contents_menu); CASEPRINT(avrcp_favourite_menu); CASEPRINT(avrcp_exit); CASEPRINT(avrcp_0); CASEPRINT(avrcp_1); CASEPRINT(avrcp_2); CASEPRINT(avrcp_3); CASEPRINT(avrcp_4); CASEPRINT(avrcp_5); CASEPRINT(avrcp_6); CASEPRINT(avrcp_7); CASEPRINT(avrcp_8); CASEPRINT(avrcp_9); CASEPRINT(avrcp_dot); CASEPRINT(avrcp_enter); CASEPRINT(avrcp_clear); CASEPRINT(avrcp_channel_up); CASEPRINT(avrcp_channel_down); CASEPRINT(avrcp_sound_select); CASEPRINT(avrcp_input_select); CASEPRINT(avrcp_display_information); CASEPRINT(avrcp_help); CASEPRINT(avrcp_page_up); CASEPRINT(avrcp_page_down); CASEPRINT(avrcp_power); CASEPRINT(avrcp_volume_up); CASEPRINT(avrcp_volume_down); CASEPRINT(avrcp_mute); CASEPRINT_UP(avrcp_play); CASEDOWN(avrcp_play) DBG("[play] %s", g_sCmdPlay); if (g_sCmdPlay[0]) async_run_process(g_sCmdPlay, 0); else send_key(KEY_PLAY); break; CASEPRINT_UP(avrcp_stop); CASEDOWN(avrcp_stop) DBG("[stop] %s", g_sCmdStop); if (g_sCmdStop[0]) async_run_process(g_sCmdStop, 0); else send_key(KEY_STOP); break; CASEPRINT_UP(avrcp_pause); CASEDOWN(avrcp_pause) DBG("[pause] %s", g_sCmdPause); if (g_sCmdPause[0]) async_run_process(g_sCmdPause, 0); else send_key(KEY_PAUSE); break; CASEPRINT(avrcp_record); CASEPRINT_UP(avrcp_rewind); CASEDOWN(avrcp_rewind) DBG("[previous] %s", g_sCmdPrev); if (g_sCmdPrev[0]) async_run_process(g_sCmdPrev, 0); else send_key(KEY_PREVIOUSSONG); break; CASEPRINT(avrcp_fast_forward); CASEPRINT(avrcp_eject); CASEPRINT_UP(avrcp_forward); CASEDOWN(avrcp_forward) DBG("[next] %s", g_sCmdNext); if (g_sCmdNext[0]) async_run_process(g_sCmdNext, 0); else send_key(KEY_NEXTSONG); break; CASEPRINT_UP(avrcp_backward); CASEDOWN(avrcp_backward) DBG("[previous] %s", g_sCmdPrev); if (g_sCmdPrev[0]) async_run_process(g_sCmdPrev, 0); else send_key(KEY_PREVIOUSSONG); break; CASEPRINT(avrcp_angle); CASEPRINT(avrcp_subpicture); CASEPRINT(avrcp_f1); CASEPRINT(avrcp_f2); CASEPRINT(avrcp_f3); CASEPRINT(avrcp_f4); CASEPRINT(avrcp_f5); CASEPRINT(avrcp_vendor_unique); default: DBG("received passthrough %d bytes: operand %d, operand %d", iReceived, frame.operand0, frame.operand1); //dump_packet(&frame, iReceived); } } else { DBG("received %d bytes:", iReceived); //dump_packet(&frame, iReceived); } // Send response if (iReceived > 0) { if (frame.ctype == CMD_ACCEPTED) { DBG("(ack)"); } else if (frame.ctype == CMD_PASSTHROUGH) { init_response(&frame.header); frame.ctype = CMD_ACCEPTED; write(sockfd, &frame, iReceived); } else { DBG("only passthrough ctype command is implemented. doh!"); // ierk!!! exit(0); } } } else { if (errno != EAGAIN) DBG("AVRCP Receive failed"); } return iReceived;}////////////////////////////////////////////////////////////////////////////////////// AVRCP CODE END ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if 1 then quit gentlystatic sig_atomic_t bSigINTReceived = 0;// Data used to mix audiotypedef struct { void* lpVoid; uint32_t index_to_construct; uint32_t index_0; uint32_t size;} CONVERTBUFFER;typedef struct { AUDIOPACKETHEADER hdr; int len; char* buf;} RINGINFO;// Data used to mix audiotypedef struct { int socket; int lives; int state; int timeoutcount; pthread_mutex_t mutex; AUDIOSTREAMINFOS StreamInfos; CONVERTBUFFER conv; int ring_in; int ring_out; RINGINFO ring[MAXCLIENTSRINGSIZE];} BTA2DPPERCLIENTDATA;#define WRITE_SIDE 0#define READ_SIDE 1enum { REDIRECT_A2DP=0, REDIRECT_ALSA=1, REDIRECT_NONE, REDIRECT_AUTO, REDIRECT_SCO,};// Data to keep per Bluetooth devicetypedef struct { char addr[20]; char plug[20]; pthread_t thread; pthread_t receiverthread; pthread_mutex_t mutex; pthread_mutex_t capture_mutex; AUDIOMIXERDATA mixer; int ctl_socket[2]; int nb_clients; int bredirect; int a2dp_rate; int a2dp_channels; int a2dp_bitspersample; int a2dp_framesize; int a2dp_timeout; int sbcbitpool; int ring_in; int ring_out; RINGINFO ring[MAXCLIENTSRINGSIZE]; BTA2DPPERCLIENTDATA clients[MAXCLIENTSPERDEVICE];} BTA2DPPERDEVICEDATA, *LPBTA2DPPERDEVICEDATA;// Data needed per Audio Streaming Clienttypedef struct { LPBTA2DPPERDEVICEDATA lpDevice; int sockfd; pthread_t thread;} A2DPDCLIENT, *LPA2DPDCLIENT;// Allocate a new deviceLPBTA2DPPERDEVICEDATA bta2dpdevicenew(char *addr){ int i = 0; LPBTA2DPPERDEVICEDATA lpDevice = mymalloc(sizeof(BTA2DPPERDEVICEDATA)); DBG(""); if (lpDevice) { memset(lpDevice, 0, sizeof(BTA2DPPERDEVICEDATA)); strncpy(lpDevice->addr, addr, sizeof(lpDevice->addr)); lpDevice->addr[sizeof(lpDevice->addr) - 1] = 0; lpDevice->mixer.volume_speaker_left = A2DPD_VOLUME_MAX; lpDevice->mixer.volume_speaker_right = A2DPD_VOLUME_MAX; lpDevice->mixer.volume_micro_left = A2DPD_VOLUME_MAX; lpDevice->mixer.volume_micro_right = A2DPD_VOLUME_MAX; pthread_mutex_init(&lpDevice->mutex, NULL); pthread_mutex_init(&lpDevice->capture_mutex, NULL); if(socketpair(AF_UNIX, SOCK_STREAM, 0, lpDevice->ctl_socket)<0) { DBG("Failed to create ctl_socket pair"); memset(lpDevice->ctl_socket, 0, sizeof(lpDevice->ctl_socket)); } else { a2dpd_signal_set_socket(lpDevice->ctl_socket[WRITE_SIDE]); } for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { pthread_mutex_init(&lpDevice->clients[i].mutex, NULL); } // In frame unit lpDevice->a2dp_timeout = read_config_int(g_srcfilename, "a2dpd", "timeout", 2000); lpDevice->sbcbitpool = read_config_int(g_srcfilename, "a2dpd", "sbcbitpool", 32); lpDevice->bredirect = read_config_int(g_srcfilename, "a2dpd", "enableredirectalsa", REDIRECT_A2DP); // A2DP specific settings lpDevice->a2dp_framesize = POOLENTRYSIZE_A2DP; lpDevice->a2dp_rate = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); lpDevice->a2dp_channels = read_config_int(g_srcfilename, "a2dpd", "channels", 2); lpDevice->a2dp_bitspersample = 16/8; } return lpDevice;}// Free a devicevoid bta2dpdevicefree(LPBTA2DPPERDEVICEDATA lpDevice){ int i = 0; int j = 0; if (lpDevice) { for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { for (j = 0; j < MAXCLIENTSRINGSIZE; j++) { safefree(lpDevice->clients[i].ring[j].buf); } pthread_mutex_destroy(&lpDevice->clients[i].mutex); } a2dpd_signal_set_socket(-1); close_socket(&lpDevice->ctl_socket[READ_SIDE]); close_socket(&lpDevice->ctl_socket[WRITE_SIDE]); pthread_mutex_destroy(&lpDevice->mutex); pthread_mutex_destroy(&lpDevice->capture_mutex); safefree(lpDevice); }}// handle sigterm to terminate properlyvoid sigint_handler(int sig){ // User wants to force quit if (bSigINTReceived == 1) { DBG("handling SIGINT again: exit forced"); exit(0); } else { // Now we must quit properly bSigINTReceived = 1; DBG("handling SIGINT"); // Dummy connection to unlock server (currently accepting) int sk = make_client_socket(); close_socket(&sk); }}// This function append data received from a client to the device ring buffervoid append_to_ring_buffer(BTA2DPPERCLIENTDATA* lpClientData, CONVERTBUFFER* lpConvert, AUDIOPACKETHEADER* lpHdr){ if(lpConvert->lpVoid != NULL) { // Enqueue in bluetooth headset if we can else loose packet pthread_mutex_lock(&lpClientData->mutex); // Append data to ring int next_ring = ((lpClientData->ring_in + 1) % MAXCLIENTSRINGSIZE); if (next_ring != lpClientData->ring_out) { lpClientData->ring[lpClientData->ring_in].buf = lpConvert->lpVoid;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -