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

📄 a2dpd.c

📁 linux蓝牙剖面实现
💻 C
📖 第 1 页 / 共 4 页
字号:
/***  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 + -