* [Bluez-devel] New A2DP related progress
@ 2006-07-28 10:26 Frédéric DALLEAU
2006-07-28 22:02 ` Brad Midgley
0 siblings, 1 reply; 2+ messages in thread
From: Frédéric DALLEAU @ 2006-07-28 10:26 UTC (permalink / raw)
To: BlueZ development
[-- Attachment #1: Type: text/plain, Size: 850 bytes --]
Hi all, Brad, Matthew, Fabien,
My latest work on a2dp daemon. I got good sound on both pc and arm!!
Features a volume control plugin.
Stereo stream is still inverted.
There is also a parasite sound that you hear when silence is sent. Both
are certainly related!
minor cracks when starting a stream.
Matthew, bmp does work well if esd is selected as output. This is due to
difference in alsa data transfer (SND_PCM_ACCESS_RW_INTERLEAVED vs
SND_PCM_ACCESS_MMAP_INTERLEAVED). I will have a look later.
Fabien, I successfully used alsamixer to configure volume on my device.
Do you know of other software that would allow mixing for any control
device? I cannot tell xmms to use the device.
Brad, as usual tarball :D available :
http://fdalleau.free.fr/btsco.tar.gz, and a patch attached.
Hope you like it!
Frédéric
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: btsco-diff-cvs-2006-07-28.patch --]
[-- Type: text/x-patch; name="btsco-diff-cvs-2006-07-28.patch", Size: 186175 bytes --]
? .deps
? Makefile
? Makefile.in
? aclocal.m4
? autom4te.cache
? config.guess
? config.h
? config.h.in
? config.log
? config.status
? config.sub
? configure
? depcomp
? install-sh
? libtool
? missing
? mkinstalldirs
? stamp-h
? stamp-h.in
? alsa-plugins/.deps
? alsa-plugins/Makefile
? alsa-plugins/Makefile.in
? avdtp/.deps
? avdtp/Makefile
? avdtp/Makefile.in
? sbc/.deps
? sbc/Makefile
? sbc/Makefile.in
Index: configure.in
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/configure.in,v
retrieving revision 1.11
diff -u -r1.11 configure.in
--- configure.in 5 Jan 2006 22:51:06 -0000 1.11
+++ configure.in 28 Jul 2006 09:48:08 -0000
@@ -5,7 +5,7 @@
AC_PREREQ(2.50)
AC_INIT()
-AM_INIT_AUTOMAKE(btsco, 0.2)
+AM_INIT_AUTOMAKE(btsco, 0.42)
AM_CONFIG_HEADER(config.h)
if (test "${CFLAGS}" = ""); then
Index: alsa-plugins/Makefile.am
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- alsa-plugins/Makefile.am 12 Jul 2006 05:47:06 -0000 1.3
+++ alsa-plugins/Makefile.am 28 Jul 2006 09:48:08 -0000
@@ -1,5 +1,6 @@
libasound_module_pcm_a2dp_la_LDFLAGS = -module -avoid-version -export-dynamic
libasound_module_pcm_a2dpd_la_LDFLAGS = -module -avoid-version -export-dynamic
+libasound_module_ctl_a2dpd_la_LDFLAGS = -module -avoid-version -export-dynamic
libasound_module_pcm_headset_la_LDFLAGS = -module -avoid-version -export-dynamic
if ALSAPLUGIN
@@ -8,20 +9,23 @@
alsadir = $(libdir)/alsa-lib
-alsa_LTLIBRARIES = libasound_module_pcm_a2dp.la libasound_module_pcm_a2dpd.la libasound_module_pcm_headset.la
+alsa_LTLIBRARIES = libasound_module_pcm_a2dp.la libasound_module_pcm_a2dpd.la libasound_module_ctl_a2dpd.la libasound_module_pcm_headset.la
libasound_module_pcm_a2dp_la_SOURCES = pcm_a2dp.c
libasound_module_pcm_a2dp_la_LIBADD = @ALSA_LIBS@ @BLUEZ_LIBS@ -lsbc -lpthread
-libasound_module_pcm_a2dpd_la_SOURCES = pcm_a2dpd.c
-libasound_module_pcm_a2dpd_la_LIBADD = @ALSA_LIBS@ @BLUEZ_LIBS@ -lsbc -lpthread
+libasound_module_pcm_a2dpd_la_SOURCES = pcm_a2dpd.c a2dp_timer.c a2dp_ipc.c
+libasound_module_pcm_a2dpd_la_LIBADD = @ALSA_LIBS@ -lrt
+
+libasound_module_ctl_a2dpd_la_SOURCES = ctl_a2dpd.c a2dp_ipc.c
+libasound_module_ctl_a2dpd_la_LIBADD = @ALSA_LIBS@
libasound_module_pcm_headset_la_SOURCES = pcm_headset.c
libasound_module_pcm_headset_la_LIBADD = @ALSA_LIBS@ @BLUEZ_LIBS@ -lpthread
-noinst_PROGRAMS = a2dpd
-a2dpd_SOURCES = a2dpd.c a2dplib.c
-a2dpd_LDADD = @BLUEZ_LIBS@ -lsbc -lpthread
+bin_PROGRAMS = a2dpd
+a2dpd_SOURCES = a2dpd.c a2dplib.c a2dp_timer.c a2dp_ipc.c
+a2dpd_LDADD = @BLUEZ_LIBS@ -lsbc -lpthread -lrt
AM_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@ -pthread
Index: alsa-plugins/a2dp_ipc.c
===================================================================
RCS file: alsa-plugins/a2dp_ipc.c
diff -N alsa-plugins/a2dp_ipc.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/a2dp_ipc.c 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,173 @@
+/*
+*
+* 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 "a2dp_ipc.h"
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define DEFAULTIP "127.0.0.1"
+#define DEFAULTPORT 21453
+#define BROADCASTIP "127.0.0.255"
+
+void close_socket(int sockfd)
+{
+ if(sockfd>0) close(sockfd);
+}
+
+int make_udp_socket()
+{
+ int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
+ if(sockfd>0)
+ {
+ int broadcast=1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == 0)
+ {
+ struct sockaddr_in peer_addr;
+ peer_addr.sin_family = AF_INET;
+ peer_addr.sin_port = htons(DEFAULTPORT);
+ peer_addr.sin_addr.s_addr = inet_addr(BROADCASTIP);
+ // Connect on a datagram socket simulate recvfrom with the address specified
+ bind(sockfd, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
+ if(connect(sockfd, (struct sockaddr*)&peer_addr, sizeof(peer_addr)) == 0)
+ {
+ }
+ else
+ {
+ close(sockfd);
+ sockfd=-1;
+ }
+ }
+ else
+ {
+ close(sockfd);
+ sockfd=-1;
+ }
+ }
+ return sockfd;
+}
+
+int make_client_socket()
+{
+ int sockfd = socket(PF_INET, SOCK_STREAM, 0);
+ if(sockfd>0)
+ {
+ struct sockaddr_in peer_addr;
+ peer_addr.sin_family = AF_INET;
+ peer_addr.sin_port = htons(DEFAULTPORT);
+ peer_addr.sin_addr.s_addr = inet_addr(DEFAULTIP);
+ connect(sockfd, (struct sockaddr*)&peer_addr, sizeof(peer_addr));
+ }
+ return sockfd;
+}
+
+int make_server_socket()
+{
+ int sockfd = socket(PF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in my_addr;
+ memset(&my_addr, 0, sizeof(my_addr));
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(DEFAULTPORT);
+ my_addr.sin_addr.s_addr = INADDR_ANY;
+
+ if(sockfd>0)
+ {
+ if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==0)
+ {
+ if(listen(sockfd, 10)==0)
+ {
+ // No error
+ }
+ else
+ {
+ close(sockfd);
+ sockfd = -3;
+ }
+ }
+ else
+ {
+ close(sockfd);
+ sockfd = -2;
+ }
+ }
+
+ return sockfd;
+}
+
+int accept_socket(int sockfd)
+{
+ // Block until connections
+ struct sockaddr_in peer_addr;
+ int sin_size = sizeof(peer_addr);
+ int new_fd = accept(sockfd, (struct sockaddr *)&peer_addr, &sin_size);
+ return new_fd;
+}
+
+void setup_socket(int sockfd)
+{
+ // Timeouts
+ struct timeval t = { 2, 100 };
+ setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
+ setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+}
+
+
+int send_socket(int sockfd, void* buffer, int size)
+{
+ int result = -1;
+ int ioffset = 0;
+ while(sockfd>0 && ioffset<size)
+ {
+ result=send(sockfd, ((char*)buffer)+ioffset, size-ioffset, MSG_NOSIGNAL);
+ if(result>0)
+ {
+ ioffset += result;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return result;
+
+}
+
+int recv_socket(int sockfd, void* buffer, int size)
+{
+ int received = 0;
+ while(buffer && received<size)
+ {
+ int result = recv(sockfd, buffer+received, size-received, MSG_NOSIGNAL);
+ if(result>0)
+ {
+ received += result;
+ }
+ else
+ {
+ received=result;
+ break;
+ }
+ }
+ return received;
+}
Index: alsa-plugins/a2dp_ipc.h
===================================================================
RCS file: alsa-plugins/a2dp_ipc.h
diff -N alsa-plugins/a2dp_ipc.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/a2dp_ipc.h 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,39 @@
+/*
+*
+* 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.
+*/
+
+
+#ifndef __A2DP_IPC_H__
+#define __A2DP_IPC_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int make_udp_socket();
+int make_client_socket();
+int make_server_socket();
+void setup_socket(int sockfd);
+void close_socket(int sockfd);
+int accept_socket(int sockfd);
+int send_socket(int sockfd, void* buffer, int size);
+int recv_socket(int sockfd, void* buffer, int size);
+
+#endif
Index: alsa-plugins/a2dp_timer.c
===================================================================
RCS file: alsa-plugins/a2dp_timer.c
diff -N alsa-plugins/a2dp_timer.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/a2dp_timer.c 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,122 @@
+/*
+*
+* 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 "a2dp_timer.h"
+#include <time.h>
+#include <unistd.h>
+#include <syslog.h>
+
+void sleeptodate(LPTIMERINFO lpTimerInfo, struct timeval* date, int predelay)
+{
+ struct timeval now;
+ struct timeval resolutionval={0, predelay + (int)(lpTimerInfo->timer_resolution.tv_nsec/1000)};
+
+ // See if we have time to wait
+ timeradd(&lpTimerInfo->timeofday, &resolutionval, &now);
+ if (timercmp(date, &now, >))
+ {
+ // Synchronise with usleep 20 ms cycle
+ usleep(1);
+ // See if we must wait again
+ gettimeofday(&now, NULL);
+ timeradd(&now, &resolutionval, &now);
+ if (timercmp(date, &now, >))
+ {
+ struct timeval interval = { 0, 0 };
+ timersub(date, &now, &interval);
+ // sleep
+ usleep(interval.tv_usec);
+ }
+ }
+ else
+ {
+ // We're late, do not wait
+ }
+}
+
+// This version uses values never reset
+void keepfreqtotal(LPTIMERINFO lpTimerInfo, LPKEEPFREQ lpKeepFreq, int predelay)
+{
+ struct timeval playtime, theoricaldate;
+
+ playtime.tv_sec=((int)(1.0*1000.0*1000.0*lpTimerInfo->itotalcount/lpTimerInfo->fps))/1000000;
+ playtime.tv_usec=((int)(1.0*1000.0*1000.0*lpTimerInfo->itotalcount/lpTimerInfo->fps))%1000000;
+ timeradd(&lpTimerInfo->totalcounter, &playtime, &theoricaldate);
+ // Si la date théorique est supérieure à la date actuelle
+ if(timercmp(&theoricaldate, &lpTimerInfo->timeofday, >))
+ {
+ sleeptodate(lpTimerInfo, &theoricaldate, predelay);
+ }
+}
+
+void a2dp_timer_notifyframe(LPTIMERINFO lpTimerInfo)
+{
+ struct timeval lastframe_interval = {0,0};
+ struct timeval maxallowed_interval = {0,200*1000};
+ gettimeofday(&lpTimerInfo->timeofday, NULL);
+ timersub(&lpTimerInfo->timeofday, &lpTimerInfo->lastframe, &lastframe_interval);
+ // Previous frames older than 1 second, reset counters
+ if(timercmp(&lastframe_interval, &maxallowed_interval, >))
+ {
+ // We must reset the total counter because else, we will believe the date is late
+ gettimeofday(&lpTimerInfo->totalcounter, NULL);
+ lpTimerInfo->itotalcount=0;
+ }
+ gettimeofday(&lpTimerInfo->lastframe, NULL);
+}
+
+void a2dp_timer_sleep(LPTIMERINFO lpTimerInfo, int predelay)
+{
+ gettimeofday(&lpTimerInfo->timeofday, NULL);
+
+ // Initialize timers
+ if(lpTimerInfo->staticcounter.tv_sec==0)
+ gettimeofday(&lpTimerInfo->staticcounter, NULL);
+ if(lpTimerInfo->totalcounter.tv_sec==0)
+ {
+ gettimeofday(&lpTimerInfo->totalcounter, NULL);
+ lpTimerInfo->itotalcount=0;
+ }
+ if(lpTimerInfo->timer_resolution.tv_nsec == 0)
+ clock_getres(CLOCK_REALTIME, &lpTimerInfo->timer_resolution);
+
+ // Duration since last call
+ timersub(&lpTimerInfo->timeofday, &lpTimerInfo->staticcounter, &lpTimerInfo->duration);
+
+ // Display data once per second
+ if(lpTimerInfo->duration.tv_sec>0)
+ {
+ // Reset all statistics
+ gettimeofday(&lpTimerInfo->staticcounter, NULL);
+ lpTimerInfo->display=lpTimerInfo->icount;
+ lpTimerInfo->icount=0;
+ }
+ else
+ {
+ lpTimerInfo->display=0;
+ }
+
+ keepfreqtotal(lpTimerInfo, &lpTimerInfo->k, predelay);
+
+ lpTimerInfo->icount++;
+ lpTimerInfo->itotalcount++;
+}
+
Index: alsa-plugins/a2dp_timer.h
===================================================================
RCS file: alsa-plugins/a2dp_timer.h
diff -N alsa-plugins/a2dp_timer.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/a2dp_timer.h 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,75 @@
+/*
+*
+* 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.
+*/
+
+
+#ifndef __A2DP_TIMER_H__
+#define __A2DP_TIMER_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+
+#define A2DPTIMERPREDELAY 2000
+#define CUMULATIVESLEEPDELAY 10000
+
+typedef struct
+{
+ struct timeval t;
+} SLEEPTILL, *LPSLEEPTILL;
+
+
+typedef struct
+{
+ struct timeval t;
+} KEEPFREQ, *LPKEEPFREQ;
+
+//
+typedef struct
+{
+ float fps; // Number of frames per second to achieve
+
+ union
+ {
+ SLEEPTILL s;
+ KEEPFREQ k;
+ };
+
+ struct timespec timer_resolution; // Resolution of the timer in nanoseconds
+ struct timeval timeofday; // Date of this frame
+ struct timeval lastframe; // Date of last frame
+ struct timeval staticcounter; // Date of frame 0 (icount=0) reseted each second
+ struct timeval totalcounter; // Date of frame 0 (itotalcount=0)
+ struct timeval duration; // Time since last frame
+ int icount; // Count of frames for this second
+ int itotalcount; // Count of frames since startup
+ int display; // non zéro if a second has just ended
+} TIMERINFO, *LPTIMERINFO;
+
+// Use notify frame when some data is sent
+void a2dp_timer_notifyframe(LPTIMERINFO lpTimerInfo);
+
+// Use sleep in your upper level loop
+// When no calls to notify_frame are done, internal statistics counter will be reseted
+void a2dp_timer_sleep(LPTIMERINFO lpTimerInfo, int predelay);
+
+#endif
Index: alsa-plugins/a2dpd.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dpd.c,v
retrieving revision 1.1
diff -u -r1.1 a2dpd.c
--- alsa-plugins/a2dpd.c 12 Jul 2006 05:47:06 -0000 1.1
+++ alsa-plugins/a2dpd.c 28 Jul 2006 09:48:08 -0000
@@ -27,26 +27,26 @@
#include <errno.h>
#include <signal.h>
#include <pthread.h>
+#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
#include "a2dplib.h"
+#include "a2dpd_protocol.h"
+#include "a2dp_timer.h"
+#include "a2dp_ipc.h"
#define BLUETOOTHSOUNDFIFOSIZE (16*1024)
#define MAXBLUETOOTHDEVICES 3
-#define MYPORT 21453
#define MAXCLIENTSPERDEVICE 8
-#define MAXCLIENTSRINGSIZE 8
-#define POOLENTRYSIZE 512
+#define MAXCLIENTSRINGSIZE 64
+#define POOLENTRYSIZE A2DPD_BLOCK_SIZE
// if 1 then quit gently
-static int bSigINTReceived = 0;
+static sig_atomic_t bSigINTReceived = 0;
// count the number of client running
-static int iThreadsRunning = 0;
+static sig_atomic_t iThreadsRunning = 0;
#define max(x,y) ((x)>(y)?(x):(y))
@@ -59,35 +59,35 @@
{
free(pool);
}
-
// Data used to mix audio
typedef struct
{
- int lives;
- pthread_mutex_t mutex;
- int ring_in;
- int ring_out;
- int ring_len[MAXCLIENTSRINGSIZE];
- char* ring[MAXCLIENTSRINGSIZE];
+ int lives;
+ pthread_mutex_t mutex;
+ int ring_in;
+ int ring_out;
+ int ring_len[MAXCLIENTSRINGSIZE];
+ char* ring[MAXCLIENTSRINGSIZE];
} BTA2DPPERCLIENTDATA;
// Data to keep per Bluetooth device
typedef struct
{
- char addr[20];
- pthread_t thread;
- pthread_t receiverthread;
- pthread_mutex_t mutex;
- int nb_clients;
- BTA2DPPERCLIENTDATA clients[MAXCLIENTSPERDEVICE];
+ char addr[20];
+ pthread_t thread;
+ pthread_t receiverthread;
+ pthread_mutex_t mutex;
+ AUDIOMIXERDATA mixer;
+ int nb_clients;
+ BTA2DPPERCLIENTDATA clients[MAXCLIENTSPERDEVICE];
} BTA2DPPERDEVICEDATA, *LPBTA2DPPERDEVICEDATA;
// Data needed per Audio Streaming Client
typedef struct
{
- LPBTA2DPPERDEVICEDATA lpDevice;
- int sockfd;
- pthread_t thread;
+ LPBTA2DPPERDEVICEDATA lpDevice;
+ int sockfd;
+ pthread_t thread;
} A2DPDCLIENT, *LPA2DPDCLIENT;
// Allocate a new device
@@ -99,6 +99,10 @@
{
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);
for(i=0;i<MAXCLIENTSPERDEVICE; i++)
{
@@ -139,53 +143,8 @@
printf("handling SIGINT\n");
// Dummy connection to unlock server (currently accepting)
- {
- int sockfd = socket(PF_INET, SOCK_STREAM, 0);
- if(sockfd>0)
- {
- struct sockaddr_in peer_addr;
- peer_addr.sin_family = AF_INET;
- peer_addr.sin_port = htons(MYPORT);
- peer_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- connect(sockfd, (struct sockaddr*)&peer_addr, sizeof(peer_addr));
- close(sockfd);
- }
- }
- }
-}
-
-// Create server socket
-int make_the_socket()
-{
- int sockfd = socket(PF_INET, SOCK_STREAM, 0);
- struct sockaddr_in my_addr;
- memset(&my_addr, 0, sizeof(my_addr));
- my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(MYPORT);
- my_addr.sin_addr.s_addr = INADDR_ANY;
-
- if(sockfd>0)
- {
- if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==0)
- {
- if(listen(sockfd, 10)==0)
- {
- // No error
- }
- else
- {
- close(sockfd);
- sockfd = -3;
- }
- }
- else
- {
- close(sockfd);
- sockfd = -2;
- }
+ close_socket(make_client_socket());
}
-
- return sockfd;
}
// This function handles a client
@@ -193,121 +152,151 @@
{
int bError = 0;
int client_index = -1;
-
+ int32_t client_type = INVALID_CLIENT_TYPE;
+ int result;
+ LPA2DPDCLIENT lpClient = (LPA2DPDCLIENT)param;
+ AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA;
+
// We should not terminate the process if clients are still running
iThreadsRunning ++;
- LPA2DPDCLIENT lpClient = (LPA2DPDCLIENT)param;
pthread_detach(lpClient->thread);
- // Find an index in clients table
- pthread_mutex_lock(&lpClient->lpDevice->mutex);
- for(client_index=0; client_index<MAXCLIENTSPERDEVICE; client_index++)
+ setup_socket(lpClient->sockfd);
+
+ // Receive type of client
+ result = recv_socket(lpClient->sockfd, &client_type, sizeof(client_type));
+
+ // This client wants to send us pcm control data
+ if(client_type==A2DPD_PLUGIN_CTL_WRITE)
{
- if(lpClient->lpDevice->clients[client_index].lives==0)
+ printf("CTL WRITE thread %d.%d started\n", client_index, lpClient->sockfd);
+
+ if(recv_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData))==sizeof(AudioMixerData))
{
- lpClient->lpDevice->clients[client_index].lives = 1;
- lpClient->lpDevice->clients[client_index].ring_in = 0;
- lpClient->lpDevice->clients[client_index].ring_out = 0;
- break;
+ pthread_mutex_lock(&lpClient->lpDevice->mutex);
+ if(AudioMixerData.volume_speaker_left != -1)
+ lpClient->lpDevice->mixer.volume_speaker_left = AudioMixerData.volume_speaker_left;
+ if(AudioMixerData.volume_speaker_left != -1)
+ lpClient->lpDevice->mixer.volume_speaker_right= AudioMixerData.volume_speaker_right;
+ if(AudioMixerData.volume_micro_left != -1)
+ lpClient->lpDevice->mixer.volume_micro_left = AudioMixerData.volume_micro_left;
+ if(AudioMixerData.volume_micro_left != -1)
+ lpClient->lpDevice->mixer.volume_micro_right= AudioMixerData.volume_micro_right;
+ pthread_mutex_unlock(&lpClient->lpDevice->mutex);
+ // Notify other clients
+ int notifyfd = make_udp_socket();
+ int i=send_socket(notifyfd, &AudioMixerData, sizeof(AudioMixerData));
+ printf("Notify return %d\n", i);
+ close_socket(notifyfd);
}
}
- pthread_mutex_unlock(&lpClient->lpDevice->mutex);
-
- if(client_index>=MAXCLIENTSPERDEVICE)
+
+ // This client wants to read our control status
+ if(client_type==A2DPD_PLUGIN_CTL_READ)
{
- printf("Client thread %d cannot start (too many clients already connected)\n", client_index);
- return 0;
- }
+ printf("CTL READ thread %d.%d started\n", client_index, lpClient->sockfd);
- // Allocate buffer
- printf("Client thread %d.%d started\n", client_index, lpClient->sockfd);
+ pthread_mutex_lock(&lpClient->lpDevice->mutex);
+ AudioMixerData = lpClient->lpDevice->mixer;
+ pthread_mutex_unlock(&lpClient->lpDevice->mutex);
- struct timeval t = { 1, 0 };
- setsockopt( lpClient->sockfd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
- setsockopt( lpClient->sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+ send_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData));
+ }
- // Loop while we receive data
- while(!bSigINTReceived && !bError)
+ // This client wants to send us pcm stream
+ if(client_type==A2DPD_PLUGIN_PCM_WRITE)
{
- // Receive data
- int pcm_buffer_size = 0;
- int result = recv(lpClient->sockfd, &pcm_buffer_size, sizeof(pcm_buffer_size), 0);
- if(result == sizeof(pcm_buffer_size) && pcm_buffer_size<=POOLENTRYSIZE)
- {
- int received = 0;
- char* pcm_buffer = pool_pop();
-
- while(pcm_buffer && received<pcm_buffer_size)
+ // Find an index in clients table for the mixer
+ pthread_mutex_lock(&lpClient->lpDevice->mutex);
+ for(client_index=0; client_index<MAXCLIENTSPERDEVICE; client_index++)
+ {
+ if(lpClient->lpDevice->clients[client_index].lives==0)
{
- result = recv(lpClient->sockfd, pcm_buffer+received, pcm_buffer_size-received, 0);
- if(result>0)
- {
- //if(result<pcm_buffer_size) printf("PART : Received %d bytes of data\n", result);
- received += result;
- }
- else
- {
- break;
- }
+ lpClient->lpDevice->clients[client_index].lives = 1;
+ lpClient->lpDevice->clients[client_index].ring_in = 0;
+ lpClient->lpDevice->clients[client_index].ring_out = 0;
+ break;
}
+ }
+ pthread_mutex_unlock(&lpClient->lpDevice->mutex);
+
+ printf("PCM thread %d.%d started\n", client_index, lpClient->sockfd);
- if(received==pcm_buffer_size)
+ if(client_index>=MAXCLIENTSPERDEVICE)
+ {
+ printf("Client thread %d cannot start (too many clients already connected)\n", client_index);
+ return 0;
+ }
+
+ // Loop while we receive data
+ while(!bSigINTReceived && !bError)
+ {
+ // Receive data
+ int32_t pcm_buffer_size = 0;
+ result = recv_socket(lpClient->sockfd, &pcm_buffer_size, sizeof(pcm_buffer_size));
+ if(result == sizeof(pcm_buffer_size) && pcm_buffer_size<=POOLENTRYSIZE)
{
- // Enqueue in bluetooth headset if we can else loose packet
- pthread_mutex_lock(&lpClient->lpDevice->clients[client_index].mutex);
+ char* pcm_buffer = pool_pop();
+ result = recv_socket(lpClient->sockfd, pcm_buffer, pcm_buffer_size);
+
+ if(result==pcm_buffer_size)
+ {
+ // Enqueue in bluetooth headset if we can else loose packet
+ pthread_mutex_lock(&lpClient->lpDevice->clients[client_index].mutex);
+
+ // Append data to ring
+ int this_ring = lpClient->lpDevice->clients[client_index].ring_in;
+ int next_ring = ((this_ring+1)%MAXCLIENTSRINGSIZE);
+
+ if(next_ring != lpClient->lpDevice->clients[client_index].ring_out)
+ {
+ lpClient->lpDevice->clients[client_index].ring[this_ring] = pcm_buffer;
+ lpClient->lpDevice->clients[client_index].ring_len[this_ring] = pcm_buffer_size;
+ lpClient->lpDevice->clients[client_index].ring_in = next_ring;
+ // We will not free that buffer, it's the bthandler thread which will do it
+ pcm_buffer = NULL;
+ }
- // Append data to ring
- int next_ring = ((lpClient->lpDevice->clients[client_index].ring_in+1)%MAXCLIENTSRINGSIZE);
- if(next_ring != lpClient->lpDevice->clients[client_index].ring_out)
- {
- //printf("Writing pool %d[%d ] = %p\n", client_index, lpClient->lpDevice->clients[client_index].ring_in, pcm_buffer);
- lpClient->lpDevice->clients[client_index].ring[lpClient->lpDevice->clients[client_index].ring_in] = pcm_buffer;
- lpClient->lpDevice->clients[client_index].ring_len[lpClient->lpDevice->clients[client_index].ring_in] = pcm_buffer_size;
- lpClient->lpDevice->clients[client_index].ring_in = next_ring;
- pcm_buffer = NULL;
+ pthread_mutex_unlock(&lpClient->lpDevice->clients[client_index].mutex);
+
+ // Reintegrate data in pool
+ if(pcm_buffer) pool_push(pcm_buffer);
}
else
{
- //printf("Client thread %d.%d ring is full (ring_in=%d, next_ring=%d, ring_out=%d)\n", client_index, lpClient->sockfd, lpClient->lpDevice->clients[client_index].ring_in, next_ring, lpClient->lpDevice->clients[client_index].ring_out);
+ printf("[2] Receiving failed on socket %d.%d error (%d/%d bytes)\n", client_index, lpClient->sockfd, result, pcm_buffer_size);
+ bError = 1;
}
-
- pthread_mutex_unlock(&lpClient->lpDevice->clients[client_index].mutex);
-
- // Reintegrate data in pool
- if(pcm_buffer) pool_push(pcm_buffer);
}
else
{
- printf("[2] Receiving failed on socket %d.%d error (%d/%d bytes)\n", client_index, lpClient->sockfd, result, pcm_buffer_size);
+ if(result==sizeof(pcm_buffer_size))
+ {
+ printf("[1] Receiving will not fit pool (poolentrysize=%d != pcm_buffer_size=%d)\n", POOLENTRYSIZE, pcm_buffer_size);
+ }
+ else
+ {
+ printf("[1] Receiving failed on socket %d.%d error (%d/%d bytes) errno=%d:%s\n", client_index, lpClient->sockfd, result, sizeof(pcm_buffer_size), errno, strerror(errno));
+ }
bError = 1;
}
}
- else
- {
- if(result==sizeof(pcm_buffer_size))
- {
- printf("[1] Receiving will not fit pool (poolentrysize=%d != pcm_buffer_size=%d)\n", POOLENTRYSIZE, pcm_buffer_size);
- }
- else
- {
- printf("[1] Receiving failed on socket %d.%d error (%d/%d bytes) \n", client_index, lpClient->sockfd, result, sizeof(pcm_buffer_size));
- }
- bError = 1;
- }
- }
- // Close socket
- printf("Client thread %d.%d ending %s error\n", client_index, lpClient->sockfd, bError?"with":"without");
- close(lpClient->sockfd);
+ pthread_mutex_lock(&lpClient->lpDevice->mutex);
+ if(client_index>=0) lpClient->lpDevice->clients[client_index].lives=0;
+ pthread_mutex_unlock(&lpClient->lpDevice->mutex);
+ }
// Say goodbye
-
pthread_mutex_lock(&lpClient->lpDevice->mutex);
- if(client_index>=0) lpClient->lpDevice->clients[client_index].lives=0;
lpClient->lpDevice->nb_clients--;
pthread_mutex_unlock(&lpClient->lpDevice->mutex);
+ // Close socket
+ printf("Client thread %d.%d ending: %s\n", client_index, lpClient->sockfd, (bError?(errno==EAGAIN?"timeout":"error"):"no error"));
+ close_socket(lpClient->sockfd);
+
// Free client data
free(lpClient);
@@ -332,28 +321,32 @@
{
int bError = 0;
int destroy_count = 0;
+ int satured = 0;
+ int ibytespersecond = 0;
+
// Connect to the A2DP device
LPA2DP lpA2dp = NULL;
char* pcm_buffer = pool_pop();
enum { NOSOUND, SOUND };
int state_previous = NOSOUND;
-
+ TIMERINFO TimerInfos;
+ memset(&TimerInfos, 0, sizeof(TimerInfos));
+ TimerInfos.fps = A2DPD_BLOCK_FREQUENCY;
printf("New connection to bluetooth\n");
// As long as we can send sound
while(!bSigINTReceived && !bError)
{
- static int ibytespersecond = 0;
int pcm_buffer_filed_size = 0;
- int sleepDelay = 1000;
char* pcm_buffers[MAXCLIENTSPERDEVICE];
int pcm_buffers_size[MAXCLIENTSPERDEVICE];
- int state_current = NOSOUND;
+ int state_current = NOSOUND;
memset(pcm_buffers, 0, sizeof(pcm_buffers));
memset(pcm_buffers_size, 0, sizeof(pcm_buffers_size));
// If there are BT data, send them
- //FIXME pthread_mutex_lock(&lpDevice->mutex);
+ //FIXME
+ // pthread_mutex_lock(&lpDevice->mutex);
if(lpDevice->nb_clients>0)
{
@@ -372,7 +365,7 @@
// Tell client we got them
lpDevice->clients[i].ring[lpDevice->clients[i].ring_out] = NULL;
lpDevice->clients[i].ring_len[lpDevice->clients[i].ring_out] = 0;
-
+
// Move to next ring
int next_ring = ((lpDevice->clients[i].ring_out+1)%MAXCLIENTSRINGSIZE);
@@ -388,32 +381,8 @@
}
}
}
- //FIXME pthread_mutex_unlock(&lpDevice->mutex);
-
- // Display some informations while running
- static int iframespersecond = 0;
- static struct timeval staticcounter = { 0, 0 };
- if(staticcounter.tv_usec==0 && staticcounter.tv_sec==0)
- gettimeofday(&staticcounter, NULL);
-
- // Calculate interval
- struct timeval timeofday, duration;
- gettimeofday(&timeofday, NULL);
- timersub(&timeofday, &staticcounter, &duration);
-
- // Display infos each second
- if(duration.tv_sec>0)
- {
- printf("A2DPD: state=%s %s clients=%d freq=%d[%d ko/s]\n", (state_current==SOUND)?"playing":"silent", lpA2dp?"connected":"disconnected", lpDevice->nb_clients, iframespersecond, ibytespersecond);
-
- // Reset all variables used
- ibytespersecond=0;
- iframespersecond=0;
- gettimeofday(&staticcounter, NULL);
- }
-
- // Count this frame
- iframespersecond++;
+ //FIXME
+ // pthread_mutex_unlock(&lpDevice->mutex);
// Send mixed audio stream to clients
switch(state_current)
@@ -421,41 +390,6 @@
case SOUND:
{
/////////////////////////////////
- // Try to synchronize sound to a 344blocks/s
- /////////////////////////////////
- struct timeval playtime, theoricaldate;
-
- // sleeps a little bit to synchronize sound
- // a2dp->sbc.channels*44100*2/(size*a2dp->frame_bytes);
- // 344.53125=channels*freq*16 bits/sizeof(buf)
- float fps=344.53125;
- playtime.tv_sec=0;
- playtime.tv_usec=(int)(1.0*1000.0*1000.0*iframespersecond/fps);
- timeradd(&staticcounter, &playtime, &theoricaldate);
-
- // Si la date théorique est supérieure à la date actuelle
- if((theoricaldate.tv_sec>timeofday.tv_sec)
- || (theoricaldate.tv_sec==timeofday.tv_sec && theoricaldate.tv_usec>timeofday.tv_usec)
- )
- {
- // We're in advance, wait a little bit
- timersub(&theoricaldate, &timeofday, &duration);
- if(duration.tv_sec>0)
- {
- printf("ERROR duration calculed more than one sec : { %d, %d }", (int)duration.tv_sec, (int)duration.tv_usec);
- }
- else
- {
- sleepDelay = duration.tv_usec;
- }
- }
- else
- {
- // We're late, do nothing
- sleepDelay=0;
- }
-
- /////////////////////////////////
// Mix what we got
/////////////////////////////////
@@ -484,14 +418,25 @@
}
//printf("Value %08X|%08X %d|%d\n", channel_1, channel_2, channel_1, channel_2);
// Stay within 16 bits per channel range
- if(channel_1>+32767) channel_1 = +32767;
- if(channel_1<-32768) channel_1 = -32768;
- if(channel_2>+32767) channel_2 = +32767;
- if(channel_2<-32768) channel_2 = -32768;
+ if(channel_1>+32767) { channel_1 = +32767; satured++; }
+ if(channel_1<-32768) { channel_1 = -32768; satured++; }
+ if(channel_2>+32767) { channel_2 = +32767; satured++; }
+ if(channel_2<-32768) { channel_2 = -32768; satured++; }
+
+ channel_1 *= lpDevice->mixer.volume_speaker_left;
+ channel_2 *= lpDevice->mixer.volume_speaker_right;
+ // yes this can be rewritten with << if we consider max volume of 2^x
+ // Isn't it already done by compiler
+ channel_1 /= A2DPD_VOLUME_MAX;
+ channel_2 /= A2DPD_VOLUME_MAX;
pBuffer[j] = (((channel_1&0x0000FFFF)<<16)|(channel_2&0x0000FFFF));
+ //FIXME We have a reverse stereo I don't know why
+ // The following line corrects the problem but I miss the cause so
+ // Do not uncomment it
+ //pBuffer[j] = (((channel_2&0x0000FFFF)<<16)|(channel_1&0x0000FFFF));
//pBuffer[j] = ( (channel_1 & 0xFFFF0000) | (channel_2 & 0x0000FFFF) );
}
-
+
// Free no longer used audio blocks
for(i=0; i<MAXCLIENTSPERDEVICE; i++)
{
@@ -501,17 +446,17 @@
pool_push(pcm_buffers[i]);
}
}
-
+
/////////////////////////////////
// Transfer data to bluetooth
/////////////////////////////////
if(pcm_buffer && pcm_buffer_filed_size>0)
{
- // Transfer takes place by 512 bytes blocks
+ // Transfer takes place by A2DPD_BLOCK_SIZE bytes blocks
int blockstart = 0;
- int blocksize = 512;
-
+ int blocksize = A2DPD_BLOCK_SIZE;
+
// Allocate A2DP if we are not connected
if(!lpA2dp)
{
@@ -526,15 +471,16 @@
{
int transfer;
- blocksize = (pcm_buffer_filed_size<512)?pcm_buffer_filed_size:512;
+ blocksize = (pcm_buffer_filed_size<A2DPD_BLOCK_SIZE)?pcm_buffer_filed_size:A2DPD_BLOCK_SIZE;
transfer = a2dp_transfer_raw(lpA2dp, pcm_buffer+blockstart, blocksize);
-
+
if(transfer>=0)
{
destroy_count = 0;
blockstart += blocksize;
ibytespersecond += transfer;
+ a2dp_timer_notifyframe(&TimerInfos);
}
else
{
@@ -548,24 +494,51 @@
}
case NOSOUND:
{
+ if(state_previous == SOUND)
+ {
+ //printf("Sound stream ran dry!!!\n");
+ }
break;
}
}
// Free the A2DP device if needed
- if(lpA2dp && destroy_count>1000)
+ // When destroy_count reaches 5000 we will destroy the A2DP link
+ // However, destroy_count is reset whenever data are sent
+ destroy_count++;
+ if(lpA2dp && destroy_count>5000)
{
printf("Destroying lpA2dp, destroy_count is %d\n", destroy_count);
a2dp_destroy(lpA2dp);
lpA2dp = NULL;
}
- // When destroy_count reaches 1000 we will destroy the A2DP link
- // However, destroy_count is reset whenever data are sent
- destroy_count++;
-
- if(sleepDelay)
- usleep(sleepDelay);
+ // Wait must take place after sending a packet
+ // This way, you will allow the plugin to send it's data
+ // And you will collect the new data
+ // Time reference floating because of 44100/1000 error in integer calculation
+ a2dp_timer_sleep(&TimerInfos, A2DPTIMERPREDELAY);
+
+ // Display infos each second
+ if(TimerInfos.display>0)
+ {
+ /*
+ char* lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s] sleep=%d satur=%d\n";
+ if(satured==0) lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s]\n";
+ printf(lpszFormat,
+ lpDevice->mixer.volume_speaker_left,
+ lpDevice->mixer.volume_speaker_right,
+ lpDevice->mixer.volume_micro_left,
+ lpDevice->mixer.volume_micro_right,
+ (state_current==SOUND)?"playing":"silent",
+ lpA2dp?"connected":"disconnected", lpDevice->nb_clients, TimerInfos.display,
+ ibytespersecond,
+ satured);
+ // Reset all variables used
+ ibytespersecond=0;
+ satured=0;
+ */
+ }
state_previous = state_current;
}
@@ -618,11 +591,7 @@
printf("socket %d: Connection from %s, mtu=%d\n", new_fd, szRemote, iMTU);
- struct timeval t;
- t.tv_sec=0;
- t.tv_usec=10*1000; // ms
- setsockopt( new_fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
- setsockopt( new_fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+ setup_socket(new_fd);
if(new_fd>0)
{
@@ -656,7 +625,7 @@
while(iReceived>=0 || errno==EAGAIN);
free(lpFrame);
}
- close(new_fd);
+ close_socket(new_fd);
}
else
{
@@ -664,7 +633,7 @@
}
}
- close(sockfd);
+ close_socket(sockfd);
}
// Sleep a little bit if we must retry
@@ -679,67 +648,73 @@
// server processing loop
void main_loop(char* addr)
{
- // Master socket
- int sockfd = make_the_socket();
-
- if(sockfd>0)
+ while(!bSigINTReceived)
{
- LPBTA2DPPERDEVICEDATA lpDevice = bta2dpdevicenew(addr);
- // Set pthread stack size to decrease unused memory usage
- pthread_attr_t tattr;
- size_t size = PTHREAD_STACK_MIN;
- int ret = pthread_attr_init(&tattr);
- ret = pthread_attr_setstacksize(&tattr, size);
- pthread_create(&lpDevice->thread, &tattr, bt_handler, lpDevice);
- pthread_create(&lpDevice->thread, &tattr, bt_receiver, lpDevice);
-
- while(!bSigINTReceived)
- {
- // Block until connections
- struct sockaddr_in peer_addr;
- int sin_size = sizeof(peer_addr);
- printf("Accepting incoming stream connection\n");
- int new_fd = accept(sockfd, (struct sockaddr *)&peer_addr, &sin_size);
- printf("Accepted %d\n", new_fd);
-
- // Handle connection if it is not the final dummy client
- if(!bSigINTReceived)
+ // Master socket
+ int sockfd = make_server_socket();
+
+ if(sockfd>0)
+ {
+ LPBTA2DPPERDEVICEDATA lpDevice = bta2dpdevicenew(addr);
+ // Set pthread stack size to decrease unused memory usage
+ pthread_attr_t tattr;
+ size_t size = PTHREAD_STACK_MIN;
+ int ret = pthread_attr_init(&tattr);
+ ret = pthread_attr_setstacksize(&tattr, size);
+ pthread_create(&lpDevice->thread, &tattr, bt_handler, lpDevice);
+ pthread_create(&lpDevice->thread, &tattr, bt_receiver, lpDevice);
+
+ while(!bSigINTReceived)
{
- LPA2DPDCLIENT lpClient = malloc(sizeof(A2DPDCLIENT));
- lpClient->lpDevice = lpDevice;
- lpClient->sockfd = new_fd;
-
- pthread_mutex_lock(&lpClient->lpDevice->mutex);
- lpClient->lpDevice->nb_clients++;
- pthread_mutex_unlock(&lpClient->lpDevice->mutex);
-
- pthread_create(&lpClient->thread, &tattr, client_handler, lpClient);
+ int new_fd = -1;
+ printf("Accepting incoming stream connection\n");
+ new_fd=accept_socket(sockfd);
+ printf("Accepted %d\n", new_fd);
+
+ // Handle connection if it is not the final dummy client
+ if(!bSigINTReceived)
+ {
+ LPA2DPDCLIENT lpClient = malloc(sizeof(A2DPDCLIENT));
+ lpClient->lpDevice = lpDevice;
+ lpClient->sockfd = new_fd;
+
+ pthread_mutex_lock(&lpClient->lpDevice->mutex);
+ lpClient->lpDevice->nb_clients++;
+ pthread_mutex_unlock(&lpClient->lpDevice->mutex);
+
+ pthread_create(&lpClient->thread, &tattr, client_handler, lpClient);
+ }
+ else
+ {
+ close_socket(new_fd);
+ }
+ usleep(10000);
}
- usleep(10000);
+
+ close_socket(sockfd);
+
+ // Very minor race condition here
+ // No dramatic consequences
+ // But we Must wait all client termination
+ // We will pthread_join one day
+ int icount = 0;
+ while(iThreadsRunning>0 && icount<30)
+ {
+ printf("A2DPD still %d clients running\n", iThreadsRunning);
+ icount++;
+ sleep(1);
+ }
+
+ // Free informations on the device
+ bta2dpdevicefree(lpDevice);
+ pthread_attr_destroy(&tattr);
}
-
- close(sockfd);
- close(sockfd);
-
- // Very minor race condition here
- // No dramatic consequences
- // But we Must wait all client termination
- // We will pthread_join one day
- int icount = 0;
- while(iThreadsRunning>0 && icount<30)
+ else
{
- printf("A2DPD still %d clients running\n", iThreadsRunning);
- icount++;
- sleep(1);
+ printf("Error %d: cannot get the socket errno=%d (%s)\n", sockfd, errno, strerror(errno));
}
- // Free informations on the device
- bta2dpdevicefree(lpDevice);
- pthread_attr_destroy(&tattr);
- }
- else
- {
- printf("Error %d: cannot get the socket errno=%d (%s)\n", sockfd, errno, strerror(errno));
+ sleep(1);
}
}
@@ -748,12 +723,38 @@
// main function
int main(int argc, char *argv[])
{
- char* addr = "00:0D:44:2A:17:C7 [Default Address]";
-
- // Read command line argument
- if(argc>1) addr = argv[1];
- printf("%s addr=%s [%s %s]\n", argv[0], addr, __DATE__, __TIME__);
-
+ int i = 0;
+ struct timespec timer_resolution = { 0, 0 };
+ char* addr = "00:0A:56:00:C0:C2 Sonorix";
+ // C2:00:08:F4:30:07:64 IPhono
+ // 00:0D:44:2A:17:C7 HP
+ // If we can be realtime it will be better
+ struct sched_param schedparam = { sched_get_priority_max(SCHED_FIFO) };
+ int res = sched_setscheduler(0, SCHED_FIFO, &schedparam);
+ int bTestThread = 0;
+ printf("setscheduler returns %d (errno=%d:%s)\n", res, errno, strerror(errno));
+
+ // Parse command line parameters
+ for(i=1; i<argc && argv[i]!=NULL; i++)
+ {
+ char c;
+ // Search a glade file
+ if(0==strcmp(argv[i], "-t"))
+ {
+ bTestThread = 1;
+ }
+ // Search a bluetooth addr
+ else if(sscanf(argv[i], "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", &c, &c, &c, &c, &c, &c, &c, &c, &c, &c, &c, &c)==12)
+ {
+ addr=argv[i];
+ }
+ else
+ {
+ printf("Parameter not handled: %s\r\n", argv[i]);
+ }
+ }
+ clock_getres(CLOCK_REALTIME, &timer_resolution);
+ printf("%s addr=%s timer=%d us [%s %s]\n", argv[0], addr, (int)(timer_resolution.tv_nsec/1000), __DATE__, __TIME__);
// set up the handler
signal(SIGINT, sigint_handler);
signal(SIGTERM, sigint_handler);
Index: alsa-plugins/a2dpd_protocol.h
===================================================================
RCS file: alsa-plugins/a2dpd_protocol.h
diff -N alsa-plugins/a2dpd_protocol.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/a2dpd_protocol.h 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,54 @@
+/*
+*
+* 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.
+*/
+
+#ifndef __A2DPD_PROTOCOL_H__
+#define __A2DPD_PROTOCOL_H__
+
+#include <stdint.h>
+
+
+// parameters used to describe device state
+typedef struct
+{
+ int16_t volume_speaker_right;
+ int16_t volume_speaker_left;
+ int16_t volume_micro_right;
+ int16_t volume_micro_left;
+} AUDIOMIXERDATA;
+
+#define INVALIDAUDIOMIXERDATA { -1, -1, -1, -1 }
+
+// Different types of client plugin for the daemon
+#define INVALID_CLIENT_TYPE 0xFFFFFFFF
+#define A2DPD_PLUGIN_CTL_WRITE 0x00000001
+#define A2DPD_PLUGIN_CTL_READ 0x00000002
+#define A2DPD_PLUGIN_PCM_WRITE 0x00000003
+#define A2DPD_VOLUME_MIN 0
+#define A2DPD_VOLUME_MAX 15
+
+// a2dp->sbc.channels*44100*2/(size*a2dp->frame_bytes);
+// 344.53125=channels*freq*16 bits/sizeof(buf)
+#define A2DPD_BLOCK_FREQUENCY (344.53125/1)
+#define A2DPD_BLOCK_SIZE (512*1)
+#define A2DPD_FRAME_BYTES 4 // 16bits * 2 channels
+#define A2DPD_FRAME_RATE 44100
+
+#endif
Index: alsa-plugins/a2dplib.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dplib.c,v
retrieving revision 1.1
diff -u -r1.1 a2dplib.c
--- alsa-plugins/a2dplib.c 12 Jul 2006 05:47:06 -0000 1.1
+++ alsa-plugins/a2dplib.c 28 Jul 2006 09:48:08 -0000
@@ -1,25 +1,25 @@
/*
- *
- * 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
- *
- */
+*
+* 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>
@@ -45,16 +45,27 @@
#include <pthread.h>
+
#include "a2dplib.h"
+#include "a2dp_timer.h"
+#include "a2dp_ipc.h"
#include "sbc.h"
#include "../a2dp.h"
#define NONSPECAUDIO 1
#define BUFS 1024
+#define A2DPMAXIMUMTRANSFERUNITSIZE 678
+// In fact sbc blocks are 76 bytes long, so a group of them is either 608 or 684 bytes
+// So 650 or 678 makes no differences!
+// However some devices may have longer transfer unit up to I saw omtu=733?
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
-#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#define DBG(fmt, arg...) { if(errno!=0) printf("DEBUG: %s: (errno=%d:%s)" fmt "\n" , __FUNCTION__ , errno, strerror(errno), ## arg);\
+ else printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg); errno=0;}
+
+//#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
//#define DBG(D...)
+
/*
sdp_record_t* g_recordP = NULL;
sdp_session_t* g_sdpSessionP = NULL;
@@ -69,74 +80,74 @@
/*
sdp_record_t* a2dp_advertise_sdp(sdp_session_t* sdpSessionP)
{
- sdp_record_t *recordP=NULL;
- sdp_list_t *svclass=NULL, *rootlist=NULL, *protolist=NULL, *l2caplist=NULL, *avdtplist=NULL, *profileslist=NULL;
- uuid_t rootuuid, svcuuid, l2capuuid, avdtpuuid;
- sdp_profile_desc_t a2dpprofile;
- int error = 0;
-
- if(sdpSessionP)
- {
- // Generic service informations
- recordP = sdp_record_alloc();
- if (recordP)
- {
- sdp_set_info_attr(recordP, A2DP_SERVICE_NAME, NULL, NULL);
-
- // Add to Public Browse Group
- sdp_uuid16_create(&rootuuid, PUBLIC_BROWSE_GROUP);
- rootlist = sdp_list_append(NULL, &rootuuid);
- sdp_set_browse_groups(recordP, rootlist);
-
- // Set service class
- sdp_uuid16_create(&svcuuid, AUDIO_SOURCE_SVCLASS_ID);
- svclass = sdp_list_append(NULL, &svcuuid);
- sdp_set_service_classes(recordP, svclass);
-
- // Set protocols informations
- // L2CAP
- sdp_uuid16_create(&l2capuuid, L2CAP_UUID);
- l2caplist = sdp_list_append(NULL, &l2capuuid);
-
- // AVDTP
- sdp_uuid16_create(&avdtpuuid, AVDTP_UUID);
- avdtplist = sdp_list_append(NULL, &avdtpuuid);
-
- // add protocols
- protolist = sdp_list_append(NULL, l2caplist);
- sdp_list_append(protolist, avdtplist);
- protolist = sdp_list_append(NULL, protolist);
- sdp_set_access_protos(recordP, protolist);
-
- // Set profiles informations
- // A2DP
- sdp_uuid16_create(&a2dpprofile.uuid, AUDIO_SOURCE_PROFILE_ID);
- a2dpprofile.version = A2DP_VERSION;
- profileslist = sdp_list_append(NULL, &a2dpprofile);
-
- // add profiles
- sdp_set_profile_descs(recordP, profileslist);
-
- // Register the record in the sdp session
- error = sdp_record_register(sdpSessionP, recordP, 0);
- if (error)
- {
- DBG("Unable to advertise service (0x%04hX)", error);
- sdp_record_free(recordP);
- recordP = NULL;
- }
- }
- else
- {
- DBG("Allocate for service description failed");
- }
- }
- else
- {
- DBG("No local sdp session available");
- }
+ sdp_record_t *recordP=NULL;
+ sdp_list_t *svclass=NULL, *rootlist=NULL, *protolist=NULL, *l2caplist=NULL, *avdtplist=NULL, *profileslist=NULL;
+ uuid_t rootuuid, svcuuid, l2capuuid, avdtpuuid;
+ sdp_profile_desc_t a2dpprofile;
+ int error = 0;
+
+ if(sdpSessionP)
+ {
+ // Generic service informations
+ recordP = sdp_record_alloc();
+ if (recordP)
+ {
+ sdp_set_info_attr(recordP, A2DP_SERVICE_NAME, NULL, NULL);
+
+ // Add to Public Browse Group
+ sdp_uuid16_create(&rootuuid, PUBLIC_BROWSE_GROUP);
+ rootlist = sdp_list_append(NULL, &rootuuid);
+ sdp_set_browse_groups(recordP, rootlist);
+
+ // Set service class
+ sdp_uuid16_create(&svcuuid, AUDIO_SOURCE_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &svcuuid);
+ sdp_set_service_classes(recordP, svclass);
+
+ // Set protocols informations
+ // L2CAP
+ sdp_uuid16_create(&l2capuuid, L2CAP_UUID);
+ l2caplist = sdp_list_append(NULL, &l2capuuid);
+
+ // AVDTP
+ sdp_uuid16_create(&avdtpuuid, AVDTP_UUID);
+ avdtplist = sdp_list_append(NULL, &avdtpuuid);
+
+ // add protocols
+ protolist = sdp_list_append(NULL, l2caplist);
+ sdp_list_append(protolist, avdtplist);
+ protolist = sdp_list_append(NULL, protolist);
+ sdp_set_access_protos(recordP, protolist);
+
+ // Set profiles informations
+ // A2DP
+ sdp_uuid16_create(&a2dpprofile.uuid, AUDIO_SOURCE_PROFILE_ID);
+ a2dpprofile.version = A2DP_VERSION;
+ profileslist = sdp_list_append(NULL, &a2dpprofile);
+
+ // add profiles
+ sdp_set_profile_descs(recordP, profileslist);
+
+ // Register the record in the sdp session
+ error = sdp_record_register(sdpSessionP, recordP, 0);
+ if (error)
+ {
+ DBG("Unable to advertise service (0x%04hX)", error);
+ sdp_record_free(recordP);
+ recordP = NULL;
+ }
+ }
+ else
+ {
+ DBG("Allocate for service description failed");
+ }
+ }
+ else
+ {
+ DBG("No local sdp session available");
+ }
- return recordP;
+ return recordP;
}
*/
@@ -144,777 +155,578 @@
void a2dp_init(void) __attribute__ ((constructor));
void a2dp_exit(void) __attribute__ ((destructor));
*/
-void change_endian( void *buf, int size)
+void memcpy_changeendian( void* dst, const void *src, int size)
{
- int i;
- char c;
- char *ptr;
-
- ptr = buf;
- for(i = 0; i < size; i += 2) {
- c = ptr[i];
- ptr[i] = ptr[i+1];
- ptr[i+1] = c;
- }
+ int i;
+ uint16_t* ptrsrc=src;
+ uint16_t* ptrdst=dst;
+ for(i = 0; i < size/2; i ++)
+ {
+ *ptrdst++ = htons(*ptrsrc++);
+ }
}
// Prepare packet headers
void init_request(struct avdtp_header * header, int request_id)
{
- static int transaction = 0;
+ static int transaction = 0;
- header->packet_type = PACKET_TYPE_SINGLE;
- header->message_type = MESSAGE_TYPE_COMMAND;
- header->transaction_label = transaction;
- header->signal_id = request_id;
+ header->packet_type = PACKET_TYPE_SINGLE;
+ header->message_type = MESSAGE_TYPE_COMMAND;
+ header->transaction_label = transaction;
+ header->signal_id = request_id;
- // clear rfa bits
- header->rfa0 = 0;
+ // clear rfa bits
+ header->rfa0 = 0;
- transaction = (transaction + 1) & 0xf;
+ transaction = (transaction + 1) & 0xf;
}
// Analyse the SEIDs the sink has sent to us
int process_seid(int s, struct acp_seid_info * get_seid_resp, unsigned short *psm, sbc_t *sbc)
{
- int v, size;
- int seid = get_seid_resp->acp_seid;
- struct getcap_req put_req;
- struct getcap_resp cap_resp;
- struct set_config s_config;
- struct set_config_resp s_resp;
- struct stream_cmd open_stream;
- struct open_stream_rsp open_resp;
- DBG("Begin");
- DBG("SEID = %d", seid);
-
- memset(&put_req, 0, sizeof(put_req));
- init_request(&put_req.header, AVDTP_GET_CAPABILITIES);
- put_req.acp_seid = seid;
-
- if (write(s, &put_req, sizeof(put_req)) != sizeof(put_req)) {
- DBG("Couldn't request capabilities for SEID = %d", seid);
- return (-1);
- }
- else {
- DBG("Requested Capabilities for SEID = %d",seid);
- }
- if (read(s, &cap_resp, sizeof(cap_resp)) < sizeof(cap_resp) ||
- cap_resp.header.message_type == MESSAGE_TYPE_REJECT ||
- cap_resp.media_type != AUDIO_MEDIA_TYPE ||
- cap_resp.media_codec_type != SBC_MEDIA_CODEC_TYPE) {
- DBG("Didn't receive SBC codec parameters (first) for SEID = %d", seid);
- return (-1);
- }
-
- DBG("Got capabilities response");
-
- memset(&s_config, 0, sizeof(s_config));
- init_request(&s_config.header, AVDTP_SET_CONFIGURATION);
- s_config.serv_cap = MEDIA_TRANSPORT_CATEGORY;
- s_config.acp_seid = seid;
- s_config.int_seid = 1; // how should I choose the int_seid??
- s_config.cap_type = MEDIA_CODEC;
- s_config.length = 6;
- s_config.media_type = AUDIO_MEDIA_TYPE;
- s_config.media_codec_type = SBC_MEDIA_CODEC_TYPE;
-
- switch(sbc->channels) {
- case 1:
- v = 8;
- break;
- case 2:
- default:
- v = 2;
- break;
- }
- s_config.codec_elements.sbc_elements.channel_mode = v;
-
- switch(sbc->rate) {
- case 16000:
- v = 8;
- break;
- case 32000:
- v = 4;
- break;
- case 48000:
- v = 1;
- break;
- case 44100:
- default:
- v = 2;
- break;
- }
- s_config.codec_elements.sbc_elements.frequency = v;
- s_config.codec_elements.sbc_elements.allocation_method = 1 << 1;
-
- switch(sbc->subbands) {
- case 4:
- v = 2;
- break;
- case 8:
- default:
- v = 1;
- break;
- }
- s_config.codec_elements.sbc_elements.subbands = v;
-
- switch(sbc->blocks) {
- case 4:
- v = 8;
- break;
- case 8:
- v = 4;
- break;
- case 12:
- v = 2;
- break;
- case 16:
- default:
- v = 1;
- break;
- }
- s_config.codec_elements.sbc_elements.block_length = v;
- s_config.codec_elements.sbc_elements.min_bitpool = cap_resp.codec_elements.sbc_elements.min_bitpool;
- s_config.codec_elements.sbc_elements.max_bitpool = cap_resp.codec_elements.sbc_elements.max_bitpool;
-
- if (!(cap_resp.codec_elements.sbc_elements.channel_mode & s_config.codec_elements.sbc_elements.channel_mode)) {
- DBG("headset does not support this channel mode");
- }
-
- if (!(cap_resp.codec_elements.sbc_elements.frequency & s_config.codec_elements.sbc_elements.frequency)) {
- DBG("headset does not support this frequency");
- }
-
- if (!(cap_resp.codec_elements.sbc_elements.allocation_method & s_config.codec_elements.sbc_elements.allocation_method)) {
- DBG("headset does not support this allocation_method");
- }
-
- if (!(cap_resp.codec_elements.sbc_elements.subbands & s_config.codec_elements.sbc_elements.subbands)) {
- DBG("headset does not support this subbands setting");
- }
-
- if (write(s, &s_config, sizeof(s_config)) != sizeof(s_config)) {
- DBG("couldn't set config seid = %d", seid);
- return (-1);
- }
-
- DBG("Sent set configurations command");
-
- size = read(s, &s_resp, sizeof(s_resp));
- if (size == sizeof(s_resp) - 2) {
- DBG("Set configurations command accepted");
- } else {
- DBG("Set configurations command rejected");
- }
-
- memset(&open_stream, 0, sizeof(open_stream));
- init_request(&open_stream.header, AVDTP_OPEN);
- open_stream.acp_seid = seid;
-
- if (write(s, &open_stream, sizeof(open_stream)) != sizeof(open_stream)) {
- DBG("Couldn't open stream SEID = %d", seid);
- return (-1);
- }
-
- DBG("Sent open stream command");
-
- if (read(s, &open_resp, sizeof(open_resp)) < sizeof(open_resp) - 1 ||
- open_resp.header.message_type == MESSAGE_TYPE_REJECT) {
- DBG("Didn't receive open response confirm for SEID = %d", seid);
- return (-1);
- }
+ int v, size;
+ int seid = get_seid_resp->acp_seid;
+ struct getcap_req put_req;
+ struct getcap_resp cap_resp;
+ struct set_config s_config;
+ struct set_config_resp s_resp;
+ struct stream_cmd open_stream;
+ struct open_stream_rsp open_resp;
+ DBG("Begin SEID = %d", seid);
+
+ memset(&put_req, 0, sizeof(put_req));
+ init_request(&put_req.header, AVDTP_GET_CAPABILITIES);
+ put_req.acp_seid = seid;
+
+ if (write(s, &put_req, sizeof(put_req)) != sizeof(put_req)) {
+ DBG("Couldn't request capabilities for SEID = %d", seid);
+ return (-1);
+ }
+ else {
+ DBG("Requested Capabilities for SEID = %d",seid);
+ }
+ if (read(s, &cap_resp, sizeof(cap_resp)) < sizeof(cap_resp) ||
+ cap_resp.header.message_type == MESSAGE_TYPE_REJECT ||
+ cap_resp.media_type != AUDIO_MEDIA_TYPE ||
+ cap_resp.media_codec_type != SBC_MEDIA_CODEC_TYPE) {
+ DBG("Didn't receive SBC codec parameters (first) for SEID = %d", seid);
+ return (-1);
+ }
+
+ DBG("Got capabilities response");
+
+ memset(&s_config, 0, sizeof(s_config));
+ init_request(&s_config.header, AVDTP_SET_CONFIGURATION);
+ s_config.serv_cap = MEDIA_TRANSPORT_CATEGORY;
+ s_config.acp_seid = seid;
+ s_config.int_seid = 1; // how should I choose the int_seid??
+ s_config.cap_type = MEDIA_CODEC;
+ s_config.length = 6;
+ s_config.media_type = AUDIO_MEDIA_TYPE;
+ s_config.media_codec_type = SBC_MEDIA_CODEC_TYPE;
+
+ switch(sbc->channels) {
+ case 1:
+ v = 8;
+ break;
+ case 2:
+ default:
+ v = 2;
+ break;
+ }
+ s_config.codec_elements.sbc_elements.channel_mode = v;
+
+ switch(sbc->rate) {
+ case 16000:
+ v = 8;
+ break;
+ case 32000:
+ v = 4;
+ break;
+ case 48000:
+ v = 1;
+ break;
+ case 44100:
+ default:
+ v = 2;
+ break;
+ }
+ s_config.codec_elements.sbc_elements.frequency = v;
+ s_config.codec_elements.sbc_elements.allocation_method = 1 << 1;
+
+ switch(sbc->subbands) {
+ case 4:
+ v = 2;
+ break;
+ case 8:
+ default:
+ v = 1;
+ break;
+ }
+ s_config.codec_elements.sbc_elements.subbands = v;
+
+ switch(sbc->blocks) {
+ case 4:
+ v = 8;
+ break;
+ case 8:
+ v = 4;
+ break;
+ case 12:
+ v = 2;
+ break;
+ case 16:
+ default:
+ v = 1;
+ break;
+ }
+ s_config.codec_elements.sbc_elements.block_length = v;
+ s_config.codec_elements.sbc_elements.min_bitpool = cap_resp.codec_elements.sbc_elements.min_bitpool;
+ s_config.codec_elements.sbc_elements.max_bitpool = cap_resp.codec_elements.sbc_elements.max_bitpool;
+
+ if (!(cap_resp.codec_elements.sbc_elements.channel_mode & s_config.codec_elements.sbc_elements.channel_mode)) {
+ DBG("headset does not support this channel mode");
+ }
+
+ if (!(cap_resp.codec_elements.sbc_elements.frequency & s_config.codec_elements.sbc_elements.frequency)) {
+ DBG("headset does not support this frequency");
+ }
- DBG("Got open stream confirm");
+ if (!(cap_resp.codec_elements.sbc_elements.allocation_method & s_config.codec_elements.sbc_elements.allocation_method)) {
+ DBG("headset does not support this allocation_method");
+ }
- *psm = 25;
- return 0;
+ if (!(cap_resp.codec_elements.sbc_elements.subbands & s_config.codec_elements.sbc_elements.subbands)) {
+ DBG("headset does not support this subbands setting");
+ }
+
+ if (write(s, &s_config, sizeof(s_config)) != sizeof(s_config)) {
+ DBG("couldn't set config seid = %d", seid);
+ return (-1);
+ }
+
+ DBG("Sent set configurations command");
+
+ size = read(s, &s_resp, sizeof(s_resp));
+ if (size == sizeof(s_resp) - 2) {
+ DBG("Set configurations command accepted");
+ } else {
+ DBG("Set configurations command rejected");
+ }
+
+ memset(&open_stream, 0, sizeof(open_stream));
+ init_request(&open_stream.header, AVDTP_OPEN);
+ open_stream.acp_seid = seid;
+
+ if (write(s, &open_stream, sizeof(open_stream)) != sizeof(open_stream)) {
+ DBG("Couldn't open stream SEID = %d", seid);
+ return (-1);
+ }
+
+ DBG("Sent open stream command");
+
+ if (read(s, &open_resp, sizeof(open_resp)) < sizeof(open_resp) - 1 ||
+ open_resp.header.message_type == MESSAGE_TYPE_REJECT) {
+ DBG("Didn't receive open response confirm for SEID = %d", seid);
+ return (-1);
+ }
+
+ DBG("Got open stream confirm");
+
+ *psm = 25;
+ return 0;
}
// Detect whether A2DP Sink is present at the destination or not
int detect_a2dp(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm, unsigned long *flags)
{
- sdp_session_t *sess;
- sdp_list_t *attrid, *search, *seq, *next;
- sdp_data_t *pdlist;
- uuid_t group;
- uint32_t range = 0x0000ffff;
- int err;
- int tries;
-
- DBG("Begin");
- tries = 0;
- while(!(sess = sdp_connect(src, dst, SDP_RETRY_IF_BUSY))) {
- DBG("retrying sdp connect: %s", strerror(errno));
- sleep(1);
- if(++tries > 10) {
- break;
- }
- }
- if (!sess) {
- DBG( "Warning: failed to connect to SDP server: %s", strerror(errno));
- if(psm) *psm = 25;
- if(flags) *flags = 0;
- return 0;
- }
-
- /* 0x1108->all? 0x1101->rf sink 0x111e->handsfree 0x1108->headset */
- sdp_uuid16_create(&group, 0x110d);
- search = sdp_list_append(0, &group);
- attrid = sdp_list_append(0, &range);
- err = sdp_service_search_attr_req(sess, search,
- SDP_ATTR_REQ_RANGE, attrid, &seq);
- sdp_list_free(search, 0);
- sdp_list_free(attrid, 0);
-
- if (err) {
- DBG( "Service Search failed: %s", strerror(errno));
- sdp_close(sess);
- return -1;
- }
-
- for (; seq; seq = next) {
- sdp_record_t *rec = (sdp_record_t *) seq->data;
-
- DBG( "Found A2DP Sink");
- if (psm)
- *psm = 25;
-
- next = seq->next;
- free(seq);
- sdp_record_free(rec);
- }
-
- sdp_uuid16_create(&group, PNP_INFO_SVCLASS_ID);
- search = sdp_list_append(0, &group);
- attrid = sdp_list_append(0, &range);
- err = sdp_service_search_attr_req(sess, search,
- SDP_ATTR_REQ_RANGE, attrid, &seq);
- sdp_list_free(search, 0);
- sdp_list_free(attrid, 0);
-
- if (err)
- goto done;
-
- if (flags)
- *flags = 0;
-
- for (; seq; seq = next) {
- sdp_record_t *rec = (sdp_record_t *) seq->data;
- uint16_t vendor, product, version;
-
- pdlist = sdp_data_get(rec, 0x0201);
- vendor = pdlist ? pdlist->val.uint16 : 0x0000;
-
- pdlist = sdp_data_get(rec, 0x0202);
- product = pdlist ? pdlist->val.uint16 : 0x0000;
-
- pdlist = sdp_data_get(rec, 0x0203);
- version = pdlist ? pdlist->val.uint16 : 0x0000;
-
- DBG( "Product ID %04x:%04x:%04x", vendor, product, version);
-
- if (vendor == 0x1310 && product == 0x0100 && version == 0x0104) {
- DBG( "Enabling GCT media payload workaround");
- if (flags)
- *flags |= NONSPECAUDIO;
- }
-
- next = seq->next;
- free(seq);
- sdp_record_free(rec);
- }
+ sdp_session_t *sess;
+ sdp_list_t *attrid, *search, *seq, *next;
+ sdp_data_t *pdlist;
+ uuid_t group;
+ uint32_t range = 0x0000ffff;
+ int err;
+ int tries;
+
+ DBG("Begin");
+ tries = 0;
+ while(!(sess = sdp_connect(src, dst, SDP_RETRY_IF_BUSY))) {
+ DBG("retrying sdp connect: %s", strerror(errno));
+ sleep(1);
+ if(++tries > 2) {
+ break;
+ }
+ }
+ if (!sess) {
+ DBG( "Warning: failed to connect to SDP server: %s", strerror(errno));
+ if(psm) *psm = 25;
+ if(flags) *flags = 0;
+ return 0;
+ }
+
+ /* 0x1108->all? 0x1101->rf sink 0x111e->handsfree 0x1108->headset */
+ sdp_uuid16_create(&group, 0x110d);
+ search = sdp_list_append(0, &group);
+ attrid = sdp_list_append(0, &range);
+ err = sdp_service_search_attr_req(sess, search,
+ SDP_ATTR_REQ_RANGE, attrid, &seq);
+ sdp_list_free(search, 0);
+ sdp_list_free(attrid, 0);
+
+ if (err) {
+ DBG( "Service Search failed: %s", strerror(errno));
+ sdp_close(sess);
+ return -1;
+ }
+
+ for (; seq; seq = next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+
+ DBG( "Found A2DP Sink");
+ if (psm)
+ *psm = 25;
+
+ next = seq->next;
+ free(seq);
+ sdp_record_free(rec);
+ }
+
+ sdp_uuid16_create(&group, PNP_INFO_SVCLASS_ID);
+ search = sdp_list_append(0, &group);
+ attrid = sdp_list_append(0, &range);
+ err = sdp_service_search_attr_req(sess, search,
+ SDP_ATTR_REQ_RANGE, attrid, &seq);
+ sdp_list_free(search, 0);
+ sdp_list_free(attrid, 0);
+
+ if (err)
+ goto done;
+
+ if (flags)
+ *flags = 0;
+
+ for (; seq; seq = next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ uint16_t vendor, product, version;
+
+ pdlist = sdp_data_get(rec, 0x0201);
+ vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0202);
+ product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0203);
+ version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ DBG( "Product ID %04x:%04x:%04x", vendor, product, version);
+
+ if (vendor == 0x1310 && product == 0x0100 && version == 0x0104) {
+ DBG( "Enabling GCT media payload workaround");
+ if (flags)
+ *flags |= NONSPECAUDIO;
+ }
+
+ next = seq->next;
+ free(seq);
+ sdp_record_free(rec);
+ }
done:
- sdp_close(sess);
- return 0;
+ sdp_close(sess);
+ return 0;
}
// Connecting on PSM 25
int do_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint16_t *mtu)
{
- struct sockaddr_l2 addr;
- struct l2cap_options opts;
- int sk;
- unsigned int opt;
- int tries;
-
- DBG("Begin");
- sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
- if (sk < 0) {
- DBG( "Can't create socket. %s(%d)",
- strerror(errno), errno);
- return -1;
- }
-
- DBG( "Connecting to bluetooth");
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, src);
- if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- DBG( "Can't bind socket. %s(%d)",
- strerror(errno), errno);
- return -1;
- }
-
- /* Get default options */
- opt = sizeof(opts);
- if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) {
- DBG( "Can't get default L2CAP options. %s(%d)",
- strerror(errno), errno);
- return -1;
- }
-
- /* Set new options */
- if(mtu && *mtu) {
- opts.omtu = *mtu;
- //opts.imtu = *mtu;
- }
- if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) {
- DBG( "Can't set L2CAP options. %s(%d)",
- strerror(errno), errno);
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, dst);
- addr.l2_psm = htobs(psm);
-
- tries = 0;
- while (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- DBG("Can't connect to %s on psm %d. %s(%d)",
- batostr(&addr.l2_bdaddr), psm, strerror(errno), errno);
- sleep(1);
- ++tries;
- if(++tries > 10) {
- close(sk);
- return -1;
- }
- }
- opt = sizeof(opts);
- if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) {
- DBG( "Can't get L2CAP options. %s(%d)",
- strerror(errno), errno);
- close(sk);
- return -1;
- }
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk;
+ unsigned int opt;
+ int tries;
+
+ DBG("Begin");
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ DBG( "Can't create socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
- DBG( "Connected [imtu %d, omtu %d, flush_to %d]",
- opts.imtu, opts.omtu, opts.flush_to);
+ DBG( "Connecting to bluetooth");
- if (mtu)
- *mtu = opts.omtu;
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ DBG( "Can't bind socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
- return sk;
-}
+ /* Get default options */
+ opt = sizeof(opts);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) {
+ DBG( "Can't get default L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
-int connect_stream(bdaddr_t *src, bdaddr_t *dst, int *cmdfd_return, sbc_t *sbc, int* seid_return)
-{
- int cmdfd=-1;
- struct getcap_req put_req;
- struct sepd_resp get_resp;
- struct stream_cmd start_stream;
- struct start_stream_rsp start_resp;
- int seid, last_seid_index;
- int size;
- int i;
- unsigned short psm_cmd,psm_stream;
- unsigned long flags = 0;
- static int streamfd;
- uint16_t mtu = 0;
- int tries;
-
- DBG("Begin");
- DBG( "Using address: %s", batostr(dst));
-
- if (detect_a2dp(src, dst, &psm_cmd, &flags) < 0) {
- DBG( "could not find A2DP services on device %s", batostr(dst));
- return -1;
- }
- else {
- DBG( "Found A2DP Sink at the destination");
- }
-
- psm_cmd=25;
- cmdfd = do_connect(src, dst, psm_cmd, NULL);
- if (cmdfd < 0) {
- DBG( "cannot open psm_cmd = %d", psm_cmd);
- return -1;
- }
-
- // avdt_discover_req
- memset(&put_req, 0, sizeof(put_req));
- init_request(&put_req.header, AVDTP_DISCOVER);
-
- if (write(cmdfd, &put_req, sizeof(put_req)) != sizeof(put_req)) {
- DBG("couldn't send avdtp_discover");
- close(cmdfd);
- return -1;
- }
- else {
- DBG("Sent the Stream End Point Discovery Command");
- }
- tries = 0;
- while((size = read(cmdfd, &get_resp, sizeof(get_resp))) < 0) {
- DBG("retrying discover response read...");
- sleep(1);
- if(++tries > 10) {
- break;
- }
- }
- if (size < sizeof(get_resp) - MAX_ADDITIONAL_CODEC_OCTETS) {
- DBG("couldn't get avdtp_discover");
- close(cmdfd);
- return -1;
- }
- else {
- DBG("Got a Stream End Point Discovery Response");
- }
- seid = -1;
- last_seid_index = MAX_ADDITIONAL_CODEC - ((sizeof(get_resp)-size)/sizeof(struct acp_seid_info));
-
- DBG("received %d capabilities", last_seid_index + 1);
-
- for(i=0; i <= last_seid_index; i++) {
- if (process_seid(cmdfd, &get_resp.infos[i], &psm_stream, sbc) == 0) {
- seid = get_resp.infos[i].acp_seid;
- break;
- }
- }
-
- if(seid == -1) {
- //We have not found the seid that we want
- DBG("couldn't locate the correct seid");
- return -1;
- }
-
- // open the stream
-
- streamfd = do_connect(src, dst, psm_stream, &mtu);
- if (streamfd < 0) {
- DBG("cannot open psm_stream = %d", psm_stream);
- return -1;
- }
-
- // start the stream
-
- memset(&start_stream, 0, sizeof(start_stream));
- init_request(&start_stream.header, AVDTP_START);
- start_stream.acp_seid = seid;
-
- if (write(cmdfd, &start_stream, sizeof(start_stream)) != sizeof(start_stream)) {
- DBG("couldn't send start_stream");
- close(streamfd);
- close(cmdfd);
- return -1;
- }
-
- DBG("Sent stream start");
-
- if (read(cmdfd, &start_resp, sizeof(start_resp)) < sizeof(start_resp) - 2 ||start_resp.header.message_type == MESSAGE_TYPE_REJECT) {
- DBG("didn't receive start_resp confirm for seid = %d", seid);
- close(streamfd);
- close(cmdfd);
- return (-1);
- }
-
- DBG("Got start stream confirm");
-
- *seid_return = seid;
- *cmdfd_return = cmdfd;
- return streamfd;
-}
+ /* Set new options */
+ if(mtu && *mtu) {
+ opts.omtu = *mtu;
+ //opts.imtu = *mtu;
+ }
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) {
+ DBG( "Can't set L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
-typedef struct snd_pcm_a2dp {
- //snd_pcm_ioplug_t io;
- int refcnt;
- int timeout;
- unsigned long state;
- bdaddr_t src;
- bdaddr_t dst;
- int sk;
- int control_sk;
- sbc_t sbc;
- int/*snd_pcm_sframes_t */num;
- unsigned char buf[1024]; // contain sbc encoded data, incrementally filled
- unsigned int len; // number of valid bytes in buf
- unsigned int frame_bytes; // fixed when initializing
- int use_rfcomm;
-
- char bufe[BUFS]; // temporary encoding buffer
-
- int lenbufe;//=0;
- unsigned long nbytes;//=0;
- struct timeval tsend;
-
- time_t timestamp;//=0;
- uint16_t seq_num;//=1;
- int frame_count;//=0;
- int transfer_num;
-
- int seid;
-
- // Used to control stream from headset
- int stop_writing;// = 0;
- int pause_writing;// = 0;
- pthread_t hListenThread;
-} snd_pcm_a2dp_t;
-/*
-void inline a2dp_get(snd_pcm_a2dp_t *a2dp)
-{
- a2dp->refcnt++;
- a2dp->timeout = 0;
-}
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(psm);
+
+ tries = 0;
+ while (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ DBG("Can't connect to %s on psm %d. %s(%d)",
+ batostr(&addr.l2_bdaddr), psm, strerror(errno), errno);
+ sleep(1);
+ if(++tries > 2) {
+ close(sk);
+ return -1;
+ }
+ }
+ opt = sizeof(opts);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) {
+ DBG( "Can't get L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
-void inline a2dp_put(snd_pcm_a2dp_t *a2dp)
-{
- a2dp->refcnt--;
+ DBG( "Connected psm=%d sk=%d [imtu %d, omtu %d, flush_to %d]", psm, sk,
+ opts.imtu, opts.omtu, opts.flush_to);
+
+ if (mtu)
+ *mtu = opts.omtu;
- if (a2dp->refcnt <= 0)
- a2dp->timeout = 2;
+ return sk;
}
-int a2dp_start(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
+int connect_stream(bdaddr_t *src, bdaddr_t *dst, int *cmdfd_return, sbc_t *sbc, int* seid_return, int* omtu)
+{
+ int cmdfd=-1;
+ struct sepd_req discover_req;
+ struct sepd_resp discover_resp;
+ struct stream_cmd start_stream;
+ struct start_stream_rsp start_resp;
+ int seid, nb_seid;
+ int size;
+ int i;
+ unsigned short psm_cmd,psm_stream=25;
+ unsigned long flags = 0;
+ int streamfd = -1;
+ uint16_t mtu = 0;
+ int tries, res;
+
+ DBG("Begin");
+ DBG( "Using address: %s", batostr(dst));
+
+ if (detect_a2dp(src, dst, &psm_cmd, &flags) < 0) {
+ DBG( "could not find A2DP services on device %s", batostr(dst));
+ return -1;
+ }
+ else {
+ DBG( "Found A2DP Sink at the destination (psm_cmd=%d)", psm_cmd);
+ }
- DBG("a2dp %p", a2dp);
+ psm_cmd=25;
+ cmdfd = do_connect(src, dst, psm_cmd, &mtu);
+ if (cmdfd < 0) {
+ DBG( "cannot open psm_cmd = %d", psm_cmd);
+ return -1;
+ }
- a2dp->len = 13;
+ // avdt_discover_req
+ memset(&discover_req, 0, sizeof(discover_req));
+ init_request(&discover_req.header, AVDTP_DISCOVER);
+
+ res=write(cmdfd, &discover_req, sizeof(discover_req));
+ if (res != sizeof(discover_req)) {
+ DBG("couldn't send avdtp_discover (res=%d)", res);
+ close(cmdfd);
+ return -1;
+ }
+ else {
+ DBG("Sent the Stream End Point Discovery Command");
+ }
+ tries = 0;
+ memset(&discover_resp, 0, sizeof(discover_resp));
- return 0;
-}
+ // SONORIX sends us a discover signal we should answer but we will discard
+ while(tries<10)
+ {
+ size = read(cmdfd, &discover_resp, sizeof(discover_resp));
+ tries++;
+ if(size>0)
+ {
+ if((discover_resp.header.message_type == MESSAGE_TYPE_ACCEPT)
+ &&(discover_resp.header.signal_id == AVDTP_DISCOVER)
+ )
+ {
+ break;
+ }
+ else
+ {
+ DBG("Invalid response read, rejected...");
+ if(discover_resp.header.message_type == MESSAGE_TYPE_COMMAND)
+ {
+ discover_resp.header.message_type = MESSAGE_TYPE_REJECT;
+ write(cmdfd, &discover_resp, sizeof(discover_resp.header));
+ }
+
+ DBG("retrying discover response read...");
+ // Discard what is not an answer
+ usleep(100);
+ }
+ }
+ else
+ {
+ DBG("retrying discover response read...");
+ // Discard what is not an answer
+ usleep(100);
+ }
+ }
-snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
+ if (size > sizeof(discover_resp.header))
+ {
+ DBG("Got a Stream End Point Discovery (%d bytes) Response (msgtype=%d,pkttype=%d,lbl=%d,sig=%d,rfa=%d)",
+ size,
+ discover_resp.header.message_type,
+ discover_resp.header.packet_type,
+ discover_resp.header.transaction_label,
+ discover_resp.header.signal_id,
+ discover_resp.header.rfa0);
+ for(i=0; i<size; i++)
+ printf("%02X", (int)(*(((char*)&discover_resp)+i)));
+ printf("\n");
+ }
+ else
+ {
+ DBG("couldn't get avdtp_discover (size=%d, min=%d, max=%d)", size, sizeof(discover_resp.header), sizeof(discover_resp));
+ close(cmdfd);
+ return -1;
+ }
- return a2dp->num;
-}
+ seid = -1;
+ nb_seid = (size-sizeof(discover_resp.header))/sizeof(struct acp_seid_info);
-void sleeptill(struct timeval *t, struct timeval *dt)
-{
- struct timeval tc,dtc;
- struct timezone tz;
- int i;
-
- i=gettimeofday(&tc,&tz);
- if timercmp(t, &tc, <){ // too late to wait
- timeradd(&tc, dt, t);
- return;
- }
- usleep(1); //sinchronize with usleep cycle
- i=gettimeofday(&tc,&tz);
- timersub(t, &tc, &dtc);
- if (dtc.tv_sec==0){ timeradd(t, dt, t);}
- else {timeradd(&tc, dt, t);return; } //more than a second to sleep, possibly error
- if (dtc.tv_usec<=2000) return; //too late to sleep
- usleep(dtc.tv_usec-2000); // wake up somewhere in the middle of 4ms
- return;
-}
-*/
-/*
-// returns time to wait ie difference between tsend and current time
-// if time has come, advances tsend
-int time_to_wait(struct timeval *tsend, struct timeval *dt)
-{
- struct timeval tc,dtc,t2,dt2;
- struct timezone tz;
- int i;
- dt2.tv_sec=0;
- dt2.tv_usec=2000;// middle of 4ms
- i=gettimeofday(&tc,&tz);
-
- timeradd(&tc, &dt2, &t2);
-
- if timercmp(tsend, &t2, <){ // time has come
- timeradd(tsend, dt, tsend);
- if timercmp(tsend, &tc, <) timeradd(&tc, dt, tsend); //if tsend<tc; tsend=tc+dt
- return 0;
- }
- timersub(tsend, &tc, &dtc);
- return dtc.tv_usec;
-}
+ DBG("received %d capabilities", nb_seid);
-// transfers around correct time postions
-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 size)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- char *buf;
- int len;
- struct media_packet_header packet_header;
- struct media_payload_header payload_header;
- int codesize,datatoread;
- unsigned long sleeptime;
- struct timeval dt;
-
-
- codesize=a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->sbc.channels*2; // size of data encoded by sbc_encode in one call
- datatoread=min(codesize,size*a2dp->frame_bytes); // amount of data to read
- buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8;
- if(a2dp->lenbufe<codesize && a2dp->lenbufe+datatoread<sizeof(a2dp->bufe))
- {
- // if not enough data in bufe to encode and there is space in bufe
- memcpy(a2dp->bufe+a2dp->lenbufe,buf,datatoread);// we read data to bufe
- a2dp->lenbufe+=datatoread;
- }
- else{
- datatoread=0; //nothing has been read
- }
-
- // Encode
- if(a2dp->lenbufe>=codesize && a2dp->len + a2dp->sbc.len < 678)
- {
- // if enough data in bufe to encode and not enough frame to fill up mtu: encoding
- change_endian(a2dp->bufe,codesize); // changing the endianness
- len = sbc_encode(&(a2dp->sbc), a2dp->bufe, codesize); //encode
- memmove(a2dp->bufe, a2dp->bufe + len, a2dp->lenbufe - len); //shift the bufe
- a2dp->lenbufe-=len;
- a2dp->nbytes+=len;
- sleeptime += a2dp->sbc.duration;
- if (len <= 0)
- return len;
- a2dp->frame_count++;
- memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len); // copy encoded frames into a2dp->buf
- a2dp->len+=a2dp->sbc.len;
- if (a2dp->state == BT_CONNECTED)
- a2dp->num += len / a2dp->frame_bytes; //update pointer
- a2dp->num %=io->buffer_size;
- }
-
- // Transfer
- if(a2dp->len + a2dp->sbc.len > 678)
- {
- // if packet is formed
- dt.tv_usec=1000000*a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->frame_count/io->rate; // time interval between transmitions
- dt.tv_sec=0;
- if(time_to_wait(&a2dp->tsend, &dt)==0)
- {
- // time to send data
- memset(&payload_header, 0, sizeof(payload_header)); // fill up the headers
- memset(&packet_header, 0, sizeof(packet_header)); //---
- payload_header.frame_count=a2dp->frame_count;
- packet_header.v = 2;
- packet_header.pt = 1;
- packet_header.sequence_number = htons(a2dp->seq_num);
- packet_header.timestamp = htonl(a2dp->timestamp);
- packet_header.ssrc = htonl(1);
- a2dp->timestamp += (a2dp->sbc.blocks + 1)*4 * (a2dp->sbc.subbands + 1)*4;
- memcpy(a2dp->buf, &packet_header, sizeof(packet_header)); //copy the headers to buf
- memcpy(a2dp->buf + sizeof(packet_header), &payload_header, sizeof(payload_header));//---
- write(a2dp->sk,a2dp->buf,a2dp->len); // sending the packet
- a2dp->len = sizeof(packet_header)+sizeof(payload_header); //inital position in buf, just after headers
- a2dp->frame_count=0;
- sleeptime=0;
- a2dp->seq_num++;
- }
- else
- {
- usleep(1);
- }
- }
- return datatoread / a2dp->frame_bytes;
-}
+ for(i=0; i < nb_seid; i++)
+ {
+ if (process_seid(cmdfd, &discover_resp.infos[i], &psm_stream, sbc) == 0)
+ {
+ seid = discover_resp.infos[i].acp_seid;
+ break;
+ }
+ }
+
+ if(seid == -1) {
+ //We have not found the seid that we want
+ DBG("couldn't locate the correct seid");
+ return -1;
+ }
+
+ // open the stream
+ streamfd = do_connect(src, dst, psm_stream, &mtu);
+ if (streamfd < 0) {
+ DBG("cannot open psm_stream = %d", psm_stream);
+ return -1;
+ }
+
+ // start the stream
+
+ memset(&start_stream, 0, sizeof(start_stream));
+ init_request(&start_stream.header, AVDTP_START);
+ start_stream.acp_seid = seid;
+
+ if (write(cmdfd, &start_stream, sizeof(start_stream)) != sizeof(start_stream)) {
+ DBG("couldn't send start_stream");
+ close(streamfd);
+ close(cmdfd);
+ return -1;
+ }
+
+ DBG("Sent stream start(seid=%d)", seid);
+
+ if (read(cmdfd, &start_resp, sizeof(start_resp)) < sizeof(start_resp) - 2 ||start_resp.header.message_type == MESSAGE_TYPE_REJECT) {
+ DBG("didn't receive start_resp confirm for seid = %d", seid);
+ close(streamfd);
+ close(cmdfd);
+ return (-1);
+ }
+
+ DBG("Got start stream confirm");
+
+ *seid_return = seid;
+ *cmdfd_return = cmdfd;
+ return streamfd;
+}
+
+typedef struct snd_pcm_a2dp
+{
+ bdaddr_t src;
+ bdaddr_t dst;
+ int sk;
+ int control_sk;
+ sbc_t sbc;
+ unsigned char buf[1024]; // contain sbc encoded data, incrementally filled
+ unsigned int len; // number of valid bytes in buf
+ unsigned int frame_bytes; // fixed when initializing
+
+ char bufe[BUFS]; // temporary encoding buffer
+ int lenbufe;//=0;
+
+ time_t timestamp;//=0;
+ uint16_t seq_num;//=1;
+ int frame_count;//=0; // Number of sbc frames in one AVDTP packet
+
+ int mtu;//=A2DPMAXIMUMTRANSFERUNITSIZE
+ int seid;
+
+ // Used to control stream from headset
+ int stop_writing;// = 0;
+ int pause_writing;// = 0;
+ pthread_t hListenThread;
+
+} snd_pcm_a2dp_t;
-// also works but sleeps between transfers
-snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- char *buf;
- int len;
- struct media_packet_header packet_header;
- struct media_payload_header payload_header;
- int codesize,datatoread;
- unsigned long sleeptime;
- int written;
-
- struct timeval dt;
-
- codesize=a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->sbc.channels*2;
- datatoread=min(codesize,size*a2dp->frame_bytes);
-
- struct timeval timeofday;
- gettimeofday(&timeofday, NULL);
-
- buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8;
- if(a2dp->lenbufe<codesize){
- memcpy(a2dp->bufe+a2dp->lenbufe,buf,datatoread);
- a2dp->lenbufe+=datatoread;
- }
- else{datatoread=0;}
-
- if(a2dp->lenbufe>=codesize){ //enough data to encode
- change_endian(a2dp->bufe,codesize); // changing the endianness
- len = sbc_encode(&(a2dp->sbc), a2dp->bufe, codesize); //encode
- memmove(a2dp->bufe, a2dp->bufe + len, a2dp->lenbufe - len); //shift the bufe
- a2dp->lenbufe-=len;
- a2dp->nbytes+=len;
- sleeptime += a2dp->sbc.duration;
- if (len <= 0)
- return len;
- if(a2dp->len + a2dp->sbc.len > 678) { // time to prepare and send the packet
- dt.tv_sec=0;
- dt.tv_usec=1000000*a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->frame_count/io->rate;
- memset(&payload_header, 0, sizeof(payload_header));
- memset(&packet_header, 0, sizeof(packet_header));
- payload_header.frame_count=a2dp->frame_count;
- packet_header.v = 2;
- packet_header.pt = 1;
- packet_header.sequence_number = htons(a2dp->seq_num);
- packet_header.timestamp = htonl(a2dp->timestamp);
- packet_header.ssrc = htonl(1);
- a2dp->timestamp += (a2dp->sbc.blocks + 1)*4 * (a2dp->sbc.subbands + 1)*4;
- memcpy(a2dp->buf, &packet_header, sizeof(packet_header));
- memcpy(a2dp->buf + sizeof(packet_header), &payload_header, sizeof(payload_header));
- sleeptill(&a2dp->tsend, &dt);
- if((written = write(a2dp->sk,a2dp->buf,a2dp->len)) != a2dp->len) {
- DBG("Wrote %d not %d bytes; errno %s(%d)", written, a2dp->len,
- strerror(errno), errno);
- }
- a2dp->len = sizeof(packet_header)+sizeof(payload_header);
- a2dp->frame_count=0;
- sleeptime=0;
- a2dp->seq_num++;
- }
- a2dp->frame_count++;
- memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len);
- a2dp->len+=a2dp->sbc.len;
- if (a2dp->state == BT_CONNECTED)
- a2dp->num += len / a2dp->frame_bytes;
- }
- return datatoread / a2dp->frame_bytes;
-}
-*/
// We have pcm data to send through bluetooth
int a2dp_transfer_raw(LPA2DP a2dp, const char* pcm_buffer, int pcm_buffer_size)
{
// No error
- int result = 0;
- struct media_packet_header packet_header;
- struct media_payload_header payload_header;
- int codesize,datatoread;
- unsigned long sleeptime;
- int written;
- struct timeval dt;
+ int result = 0;
+ struct media_packet_header packet_header;
+ struct media_payload_header payload_header;
+ int codesize,datatoread;
+ int written;
// Check parameter
- if(a2dp==0 || pcm_buffer==0 || pcm_buffer_size==0) return EINVAL;
-
- //if(a2dp->transfer_num%1000==0) DBG("Transfer %d", a2dp->transfer_num);
+ if(a2dp==0 || pcm_buffer==0 || pcm_buffer_size==0) return EINVAL;
// How much data can be encoded by sbc at a time?
codesize=a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->sbc.channels*2;
// 16 bits * 2 channels * 16 blocks * 8 subbands = 4096bits = 512 o
datatoread=min(codesize,pcm_buffer_size);
- //if(a2dp->transfer_num%1000==0) DBG("Codec encode blocks of %d bytes, we read %d bytes of %d incoming", codesize, datatoread, pcm_buffer_size);
-
// Enqueue data in bufe
- if(a2dp->lenbufe<codesize)
+ if(a2dp->lenbufe+datatoread < BUFS)
{
// Append data to bufe, for sbc encoding
- memcpy(a2dp->bufe+a2dp->lenbufe, pcm_buffer, datatoread);
+ memcpy_changeendian(a2dp->bufe+a2dp->lenbufe, pcm_buffer, datatoread);
a2dp->lenbufe+=datatoread;
}
else
@@ -922,32 +734,24 @@
datatoread=0;
}
+
// If bufe is full, encode
if(a2dp->lenbufe>=codesize)
{
// Enough data to encode (sbc wants 1k blocks)
int encoded;
- change_endian(a2dp->bufe,codesize); // changing the endianness
encoded = sbc_encode(&(a2dp->sbc), a2dp->bufe, codesize); //encode
if (encoded <= 0)
return encoded;
- //if(a2dp->transfer_num%1000==0) DBG("Compressed SBC %d encoded bytes / %d bytes really encoded / %d bytes wanted", a2dp->sbc.len, encoded, codesize);
memmove(a2dp->bufe, a2dp->bufe + encoded, a2dp->lenbufe - encoded); // Shift the bufe
- a2dp->lenbufe-=encoded;
- a2dp->nbytes+=encoded;
- //if(a2dp->transfer_num%1000==0) DBG("Shifted SBC (bufe, bufe+encoded=%d, lenbufe=%d-encoded=%d)", encoded, a2dp->lenbufe, a2dp->lenbufe - encoded);
-
- sleeptime += a2dp->sbc.duration;
+ a2dp->lenbufe -= encoded;
// Send data through bluetooth
- if(a2dp->len + a2dp->sbc.len > 678)
+ if(a2dp->len + a2dp->sbc.len >= a2dp->mtu)
{
- //if(a2dp->transfer_num%1000==0) DBG("Ready to send a2dp->len = %d + a2dp->sbc.len = %d = %d > 678", a2dp->len, a2dp->sbc.len, a2dp->len + a2dp->sbc.len);
// time to prepare and send the packet
- dt.tv_sec=0;
- dt.tv_usec=1000000*a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->frame_count/48000;
memset(&payload_header, 0, sizeof(payload_header));
memset(&packet_header, 0, sizeof(packet_header));
payload_header.frame_count=a2dp->frame_count;
@@ -959,7 +763,7 @@
a2dp->timestamp += (a2dp->sbc.blocks + 1)*4 * (a2dp->sbc.subbands + 1)*4;
memcpy(a2dp->buf, &packet_header, sizeof(packet_header));
memcpy(a2dp->buf + sizeof(packet_header), &payload_header, sizeof(payload_header));
- //sleeptill(&a2dp->tsend, &dt);
+ // Send our data
if((written = write(a2dp->sk,a2dp->buf,a2dp->len)) != a2dp->len)
{
// Error while sending data
@@ -970,662 +774,360 @@
result = written;
// Reset buffer of data to send
- a2dp->len = sizeof(packet_header)+sizeof(payload_header);
+ a2dp->len = sizeof(struct media_packet_header)+sizeof(struct media_payload_header);
a2dp->frame_count=0;
a2dp->seq_num++;
- sleeptime=0;
}
- // Append sbc encoded data to buf, until buf reaches 678 to send
+ // Append sbc encoded data to buf, until buf reaches A2DPMAXIMUMTRANSFERUNITSIZE to send
a2dp->frame_count++;
memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len);
a2dp->len+=a2dp->sbc.len;
}
- a2dp->transfer_num++;
-
- return result;
-}
-/*
-int a2dp_close(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- struct stream_cmd close_stream;
- struct close_stream_rsp close_resp;
-
- DBG("a2dp Destroying %p", a2dp);
- memset(&close_stream, 0, sizeof(close_stream));
- memset(&close_resp, 0, sizeof(close_resp));
-
- // the stream-close used to make the iTech headset lock up and require it to be powercycled
- // should be tested again now that we drain the queue properly
-
- init_request(&close_stream.header, AVDTP_CLOSE);
- close_stream.acp_seid = a2dp->seid;
- if (write(a2dp->control_sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream))
- {
- printf("Couldn't send close_stream (errno=%d:%s)\n", errno, strerror(errno));
- close(a2dp->control_sk);
- a2dp->control_sk = -1;
- close(a2dp->sk);
- a2dp->sk = -1;
- }
-
- a2dp->len = 0;
- a2dp->len = 0;
-
- a2dp_put(a2dp);
-
- return 0;
-}
-
-int a2dp_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- unsigned int period_bytes;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8;
-
- period_bytes = io->period_size * a2dp->frame_bytes;
-
- DBG("format %s rate %d channels %d", snd_pcm_format_name(io->format), io->rate, io->channels);
-
- DBG("frame_bytes %d period_bytes %d period_size %ld buffer_size %ld",
- a2dp->frame_bytes, period_bytes, io->period_size, io->buffer_size);
-
- return 0;
+ return result;
}
-int a2dp_prepare(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 13;
-
- a2dp->num = 0;
-
- a2dp->sbc.rate = io->rate;
- a2dp->sbc.channels = io->channels;
- a2dp->sbc.subbands = 8; // safe default
- a2dp->sbc.blocks = 16; // safe default
- a2dp->sbc.bitpool = 32; // recommended value 53, safe default is 32
-
- return 0;
-}
-
-int a2dp_drain(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- return 0;
-}
-
-int a2dp_descriptors_count(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- if (a2dp->state == BT_CLOSED)
- return 0;
-
- return 1;
-}
-
-int a2dp_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int space)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- if (a2dp->state == BT_CLOSED)
- return 0;
-
- if (space < 1) {
- SNDERR("Can't fill in descriptors");
- return 0;
- }
-
- pfds[0].fd = a2dp->sk;
- pfds[0].events = POLLOUT;
-
- return 1;
-}
-
-int a2dp_poll(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- *revents = pfds[0].revents;
-
- if (a2dp->state == BT_CLOSED)
- return 0;
-
- if (pfds[0].revents & POLLHUP) {
- a2dp->state = BT_CLOSED;
- snd_pcm_ioplug_reinit_status(&a2dp->io);
- }
-
- return 0;
-}
-*/
static void init_response(struct avdtp_header * header, int response_type)
{
- // leave signal_id and transaction label since we are reusing the request
- header->packet_type = PACKET_TYPE_SINGLE;
- header->message_type = response_type;
+ // leave signal_id and transaction label since we are reusing the request
+ header->packet_type = PACKET_TYPE_SINGLE;
+ header->message_type = response_type;
- // clear rfa bits
- header->rfa0 = 0;
+ // clear rfa bits
+ header->rfa0 = 0;
}
// monitor the control connection for pause/play signals from headset
// note this signaling is in the avdtp core; avrcp signaling is different
-static void* listen_thread(void* param)
+static void* listen_thread(void* param)
{
- snd_pcm_a2dp_t* a2dp = (snd_pcm_a2dp_t*)param;
+ snd_pcm_a2dp_t* a2dp = (snd_pcm_a2dp_t*)param;
- printf("Listen thread running\n");
+ DBG("Listen thread running\n");
- // Set a timeout to close thread
- struct timeval t = { 5, 0 };
- setsockopt( a2dp->control_sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
- setsockopt( a2dp->control_sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
-
- // Loop until end of writing
- while(!a2dp->stop_writing)
- {
- struct stream_cmd cmd;
- int size = read(a2dp->control_sk, &cmd, sizeof(cmd));
- if(size == sizeof(cmd))
- {
- printf("Received signal %d from set\n", cmd.header.signal_id);
- if(cmd.header.signal_id == AVDTP_SUSPEND)
- {
- a2dp->pause_writing = 1;
- }
- else if(cmd.header.signal_id == AVDTP_START)
- {
- a2dp->pause_writing = 0;
- }
- else
- {
- printf("Unexpected headset directive %d\n", cmd.header.signal_id);
- }
- // ack the command regardless
- // take a shortcut and reuse the command struct (knock one byte off length)
- init_response(&cmd.header, MESSAGE_TYPE_ACCEPT);
- if (write(a2dp->control_sk, &cmd, sizeof(cmd)-1) != sizeof(cmd)-1)
- {
- printf("Couldn't ack %d\n", cmd.header.signal_id);
- }
- }
- else
- {
- if(errno!=EAGAIN)
- printf("Error while receiving %d (errno=%d:%s)\n", size, errno, strerror(errno));
- if(errno!=EINTR)
- break;
- }
- }
+ // Set a timeout to close thread
+ struct timeval t = { 5, 0 };
+ setsockopt( a2dp->control_sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
+ setsockopt( a2dp->control_sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
- return NULL;
+ // Loop until end of writing
+ while(!a2dp->stop_writing)
+ {
+ struct stream_cmd cmd;
+ int size = read(a2dp->control_sk, &cmd, sizeof(cmd));
+ if(size == sizeof(cmd))
+ {
+ DBG("Received signal %d from set\n", cmd.header.signal_id);
+ if(cmd.header.signal_id == AVDTP_SUSPEND)
+ {
+ a2dp->pause_writing = 1;
+ }
+ else if(cmd.header.signal_id == AVDTP_START)
+ {
+ a2dp->pause_writing = 0;
+ }
+ else
+ {
+ DBG("Unexpected headset directive %d\n", cmd.header.signal_id);
+ }
+ // ack the command regardless
+ // take a shortcut and reuse the command struct (knock one byte off length)
+ init_response(&cmd.header, MESSAGE_TYPE_ACCEPT);
+ if (write(a2dp->control_sk, &cmd, sizeof(cmd)-1) != sizeof(cmd)-1)
+ {
+ DBG("Couldn't ack %d\n", cmd.header.signal_id);
+ }
+ }
+ else
+ {
+ if(errno!=EAGAIN)
+ DBG("Error while receiving %d (errno=%d:%s)\n", size, errno, strerror(errno));
+ if(errno!=EINTR)
+ break;
+ }
+ }
+
+ return NULL;
}
int a2dp_connect(snd_pcm_a2dp_t *a2dp)
{
- struct sockaddr_rc addr;
- socklen_t len;
- int sk;
- int control_sk = -1;
-
- DBG("a2dp %p", a2dp);
-
- if(a2dp->use_rfcomm) {
- sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sk < 0)
- return -errno;
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &a2dp->src);
-
- if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return -errno;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &a2dp->dst);
- addr.rc_channel = 1;
-
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return -errno;
- }
-
- memset(&addr, 0, sizeof(addr));
- len = sizeof(addr);
-
- if (getsockname(sk, (struct sockaddr *) &addr, &len) < 0) {
- close(sk);
- return -errno;
- }
-
- bacpy(&a2dp->src, &addr.rc_bdaddr);
-
- //fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK);
- } else {
- sk = connect_stream(&a2dp->src, &a2dp->dst, &control_sk, &a2dp->sbc, &a2dp->seid);
- }
-
- a2dp->sk = sk;
- a2dp->control_sk = control_sk;
-
- // Start listen thread
- a2dp->pause_writing = 0;
- a2dp->stop_writing = 0;
+ //struct sockaddr_rc addr;
+ //socklen_t len;
+ int sk = -1;
+ int control_sk = -1;
+ errno=0;
+ DBG("a2dp %p", a2dp);
+ /*
+ if(a2dp->use_rfcomm) {
+ sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0)
+ return -errno;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &a2dp->src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -errno;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &a2dp->dst);
+ addr.rc_channel = 1;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -errno;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+
+ if (getsockname(sk, (struct sockaddr *) &addr, &len) < 0) {
+ close(sk);
+ return -errno;
+ }
+
+ bacpy(&a2dp->src, &addr.rc_bdaddr);
+
+ //fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK);
+ } else {*/
+ sk = connect_stream(&a2dp->src, &a2dp->dst, &control_sk, &a2dp->sbc, &a2dp->seid, &a2dp->mtu);
+ //}
+
+ a2dp->sk = sk;
+ a2dp->control_sk = control_sk;
+
+ // Start listen thread
+ a2dp->pause_writing = 0;
+ a2dp->stop_writing = 0;
// Set pthread stack size to decrease unused memory usage
pthread_attr_t tattr;
size_t size = PTHREAD_STACK_MIN;
int ret = pthread_attr_init(&tattr);
ret = pthread_attr_setstacksize(&tattr, size);
- pthread_create(&a2dp->hListenThread, &tattr, listen_thread, (void*)a2dp);
+ pthread_create(&a2dp->hListenThread, &tattr, listen_thread, (void*)a2dp);
pthread_attr_destroy(&tattr);
- return 0;
+ return 0;
}
-/*
-#define MAX_CONNECTIONS 10
-
-static snd_pcm_a2dp_t *connections[MAX_CONNECTIONS];
-
-static snd_timer_t *timer = NULL;
-
-static volatile sig_atomic_t __locked = 0;
-
-void a2dp_lock(void)
-{
- while (__locked)
- usleep(100);
-
- __locked = 1;
-}
-
-void a2dp_unlock(void)
-{
- __locked = 0;
-}
-*/
snd_pcm_a2dp_t *a2dp_alloc(void)
{
- snd_pcm_a2dp_t *a2dp;
- DBG("Begin");
- a2dp = malloc(sizeof(*a2dp));
- if (!a2dp)
- return NULL;
-
- memset(a2dp, 0, sizeof(*a2dp));
- a2dp->refcnt = 1;
- a2dp->seq_num = 1;
- a2dp->state = BT_OPEN;
- sbc_init(&a2dp->sbc, 0L);
+ snd_pcm_a2dp_t *a2dp;
+ DBG("Begin");
+ a2dp = malloc(sizeof(*a2dp));
+ if (!a2dp)
+ return NULL;
+
+ memset(a2dp, 0, sizeof(*a2dp));
+ a2dp->seq_num = 1;
+ a2dp->mtu = A2DPMAXIMUMTRANSFERUNITSIZE;
+ a2dp->len = sizeof(struct media_packet_header)+sizeof(struct media_payload_header);
+ sbc_init(&a2dp->sbc, 0L);
- return a2dp;
+ return a2dp;
}
void a2dp_free(snd_pcm_a2dp_t *a2dp)
{
- DBG("Begin");
- if (a2dp->sk > fileno(stderr))
- close(a2dp->sk);
- if (a2dp->control_sk > fileno(stderr))
- close(a2dp->control_sk);
+ DBG("Begin");
+ if (a2dp->sk > 0)
+ close(a2dp->sk);
+ if (a2dp->control_sk > 0)
+ close(a2dp->control_sk);
- sbc_finish(&a2dp->sbc);
+ sbc_finish(&a2dp->sbc);
- free(a2dp);
+ free(a2dp);
}
-/*
-void a2dp_timer(snd_async_handler_t *async)
-{
- snd_timer_t *handle = snd_async_handler_get_timer(async);
- snd_timer_read_t tr;
- int i, ticks = 0;
-
- while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr))
- ticks += tr.ticks;
-
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++) {
- snd_pcm_a2dp_t *a2dp = connections[i];
-
- if (a2dp && a2dp->refcnt <= 0) {
- a2dp->timeout = ((a2dp->timeout * 1000) - ticks) / 1000;
- if (a2dp->timeout <= 0) {
- connections[i] = NULL;
- a2dp_free(a2dp);
- }
- }
- }
- a2dp_unlock();
-}
-*/
static void sighand(int signo)
{
- printf("Thread in signal handler %d\n", signo);
- return;
+ return;
}
void a2dp_init(void)
-{/*
- snd_async_handler_t *async;
-
- snd_timer_info_t *info;
- snd_timer_params_t *params;
- long resolution;
- char timername[64];
- int err, i;
-*/
- // set up thread signal handler
- memset(&actions, 0, sizeof(actions));
- sigemptyset(&actions.sa_mask);
- actions.sa_flags = 0;
- actions.sa_handler = sighand;
- sigaction(SIGALRM,&actions,NULL);
-/*
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++)
- connections[i] = NULL;
-
- a2dp_unlock();
-
- snd_timer_info_alloca(&info);
- snd_timer_params_alloca(¶ms);
-
- sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
- SND_TIMER_CLASS_GLOBAL, SND_TIMER_CLASS_NONE, 0,
- SND_TIMER_GLOBAL_SYSTEM, 0);
-
- err = snd_timer_open(&timer, timername, SND_TIMER_OPEN_NONBLOCK);
- if (err < 0) {
- SNDERR("Can't open global timer");
- return;
- }
-
- err = snd_timer_info(timer, info);
- if (err < 0) {
- SNDERR("Can't get global timer info");
- return;
- }
-
- snd_timer_params_set_auto_start(params, 1);
-
- resolution = snd_timer_info_get_resolution(info);
- snd_timer_params_set_ticks(params, 1000000000 / resolution);
- if (snd_timer_params_get_ticks(params) < 1)
- snd_timer_params_set_ticks(params, 1);
-
- err = snd_timer_params(timer, params);
- if (err < 0) {
- SNDERR("Can't set global timer parameters");
- snd_timer_close(timer);
- return;
- }
-
- err = snd_async_add_timer_handler(&async, timer, a2dp_timer, NULL);
- if (err < 0) {
- SNDERR("Can't create global async callback");
- snd_timer_close(timer);
- return;
- }
-*/
- // Start sdp advertising
- /*
- g_sdpSessionP = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
- g_recordP = a2dp_advertise_sdp(g_sdpSessionP);
- */
- /*
- err = snd_timer_start(timer);*/
+{
+ // set up thread signal handler
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = sighand;
+ sigaction(SIGALRM,&actions,NULL);
+
+ // Start sdp advertising
+ /*
+ g_sdpSessionP = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ g_recordP = a2dp_advertise_sdp(g_sdpSessionP);
+ */
}
void a2dp_exit(void)
{
-// int err, i;
-
- // Stop advertising A2DP
- /*
- if (g_recordP)
- sdp_record_free(g_recordP);
- g_recordP = NULL;
- if (g_sdpSessionP)
- sdp_close(g_sdpSessionP);
- g_sdpSessionP = NULL;
- */
- /*
- err = snd_timer_stop(timer);
-
- err = snd_timer_close(timer);
-
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++) {
- snd_pcm_a2dp_t *a2dp = connections[i];
-
- if (a2dp) {
- connections[i] = NULL;
- a2dp_free(a2dp);
- }
- }
-
- a2dp_unlock();*/
}
LPA2DP a2dp_new(char* addr)
{
- snd_pcm_a2dp_t *a2dp = NULL;
- bdaddr_t src, dst;
- int err, /*pos = -1, */use_rfcomm = 0;
-
- DBG("Begin");
- bacpy(&src, BDADDR_ANY);
- bacpy(&dst, BDADDR_ANY);
- DBG("bdaddr/dest is %s", addr);
- str2ba(addr, &dst);
-
-/*
- a2dp_lock();
- for (n = 0; n < MAX_CONNECTIONS; n++) {
- if (connections[n]) {
- if (!bacmp(&connections[n]->dst, &dst) &&
- (!bacmp(&connections[n]->src, &src) ||
- !bacmp(&src, BDADDR_ANY))) {
- a2dp = connections[n];
- a2dp_get(a2dp);
- break;
- }
- } else if (pos < 0)
- pos = n;
- }
-
- if (!a2dp) {
- if (pos < 0) {
- DBG("Too many connections");
- return NULL;
- }
-*/
- if (!a2dp) {
- a2dp = a2dp_alloc();
- if (!a2dp) {
- DBG("Can't allocate");
- return NULL;
- }
-
- //connections[pos] = a2dp;
-
- a2dp->state = BT_CONNECT;
-
- bacpy(&a2dp->src, &src);
- bacpy(&a2dp->dst, &dst);
- a2dp->use_rfcomm = use_rfcomm;
- }
-
- //a2dp_unlock();
-
- if (a2dp->state != BT_CONNECTED) {
- err = a2dp_connect(a2dp);
- if (err < 0) {
- DBG("Can't connect");
- goto error;
- }
+ snd_pcm_a2dp_t *a2dp = NULL;
+ bdaddr_t src, dst;
+ int err; //, pos = -1, use_rfcomm = 0;
+
+ DBG("Begin");
+ bacpy(&src, BDADDR_ANY);
+ bacpy(&dst, BDADDR_ANY);
+ DBG("bdaddr/dest is %s", addr);
+ str2ba(addr, &dst);
+
+ a2dp = a2dp_alloc();
+ if (!a2dp) {
+ DBG("Can't allocate");
+ return NULL;
+ }
- a2dp->state = BT_CONNECTED;
- }
+ bacpy(&a2dp->src, &src);
+ bacpy(&a2dp->dst, &dst);
+ //a2dp->use_rfcomm = use_rfcomm;
+
+ err = a2dp_connect(a2dp);
+ if (err < 0) {
+ DBG("Can't connect");
+ goto error;
+ }
- return a2dp;
+ return a2dp;
error:
- a2dp_free(a2dp);
- return NULL;
+ a2dp_free(a2dp);
+ return NULL;
}
void a2dp_destroy(LPA2DP a2dp)
{
- struct stream_cmd close_stream;
- struct close_stream_rsp close_resp;
-
- DBG("Begin");
- DBG("Destroying %p", a2dp);
+ struct stream_cmd close_stream;
+ struct close_stream_rsp close_resp;
- DBG("Listen thread terminating");
- a2dp->stop_writing = 1;
- pthread_kill(a2dp->hListenThread, SIGALRM);
- pthread_join(a2dp->hListenThread, NULL);
- DBG("Listen thread terminated");
+ DBG("Begin");
+ a2dp->stop_writing = 1;
+ pthread_kill(a2dp->hListenThread, SIGALRM);
+ pthread_join(a2dp->hListenThread, NULL);
memset(&close_stream, 0, sizeof(close_stream));
- memset(&close_resp, 0, sizeof(close_resp));
+ memset(&close_resp, 0, sizeof(close_resp));
- // the stream-close used to make the iTech headset lock up and require it to be powercycled
- // should be tested again now that we drain the queue properly
+ // the stream-close used to make the iTech headset lock up and require it to be powercycled
+ // should be tested again now that we drain the queue properly
- init_request(&close_stream.header, AVDTP_CLOSE);
- close_stream.acp_seid = a2dp->seid;
- if (write(a2dp->control_sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream))
- {
- printf("Couldn't send close_stream (errno=%d:%s)\n", errno, strerror(errno));
- }
-
- DBG("a2dp_free(%p)", a2dp);
- a2dp_free(a2dp);
- DBG("a2dp_free(%p) OK", a2dp);
- DBG("a2dp_destroy(%p) OK", a2dp);
+ init_request(&close_stream.header, AVDTP_CLOSE);
+ close_stream.acp_seid = a2dp->seid;
+ if (write(a2dp->control_sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream))
+ {
+ DBG("Couldn't send close_stream (errno=%d:%s)\n", errno, strerror(errno));
+ }
+
+ a2dp_free(a2dp);
+ DBG("a2dp_destroy(%p) OK", a2dp);
}
int a2dp_make_listen_socket(unsigned short psm)
{
- DBG("Begin");
- char* lpszError = NULL;
- int sockfd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-
- if (sockfd >= 0)
- {
- struct sockaddr_l2 addr;
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, BDADDR_ANY);
- addr.l2_psm=htobs(psm);
- if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) >= 0)
- {
- /*
- struct l2cap_options opts;
- unsigned int iOptSize = sizeof(opts);
- // Get default options
- if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) >= 0)
- {
- if (setsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) >= 0)
- {
- */
- if(listen(sockfd,5)>=0)
- {
- }
- else
- {
- lpszError = "Can't listen.";
- }
- /*
- }
- else
- {
- lpszError = "Can't get default L2CAP options.";
- }
- }
- else
- {
- lpszError = "Can't set L2CAP options.";
- }
- */
- }
- else
- {
- lpszError = "Can't bind socket (already used?).";
- }
- }
- else
- {
- lpszError = "Can't create socket.";
- }
-
- if(lpszError)
- {
- printf("%s %s(%d)\n", lpszError, strerror(errno), errno);
- close(sockfd);
- sockfd=-1;
- }
-
- return sockfd;
+ DBG("Begin");
+ char* lpszError = NULL;
+ int sockfd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+
+ if (sockfd >= 0)
+ {
+ struct sockaddr_l2 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+ addr.l2_psm=htobs(psm);
+ if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) >= 0)
+ {
+ /*
+ struct l2cap_options opts;
+ unsigned int iOptSize = sizeof(opts);
+ // Get default options
+ if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) >= 0)
+ {
+ if (setsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) >= 0)
+ {
+ */
+ if(listen(sockfd,5)>=0)
+ {
+ }
+ else
+ {
+ lpszError = "Can't listen.";
+ }
+ /*
+ }
+ else
+ {
+ lpszError = "Can't get default L2CAP options.";
+ }
+ }
+ else
+ {
+ lpszError = "Can't set L2CAP options.";
+ }
+ */
+ }
+ else
+ {
+ lpszError = "Can't bind socket (already used?).";
+ }
+ }
+ else
+ {
+ lpszError = "Can't create socket.";
+ }
+
+ if(lpszError)
+ {
+ DBG("%s %s(%d)\n", lpszError, strerror(errno), errno);
+ close(sockfd);
+ sockfd=-1;
+ }
+
+ return sockfd;
}
int a2dp_wait_connection( int sockfd, char* szRemote, int iRemoteSize, uint16_t *mtu)
{
- // Wait client connection
- struct sockaddr_l2 addr;
- socklen_t addrlen = sizeof(addr);
- int new_fd = accept(sockfd, (struct sockaddr *) &addr, &addrlen);
-
- DBG("Begin");
- if (szRemote) *szRemote='\0';
-
- if(new_fd >= 0)
- {
- // Get default options
- struct l2cap_options opts;
- unsigned int iOptSize = sizeof(opts);
- if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &iOptSize) >= 0)
- {
- if (mtu && opts.imtu)
- *mtu = opts.imtu;
- if (mtu && opts.omtu)
- *mtu = opts.omtu;
- if (!(*mtu))
- *mtu=678;
- }
- printf( "Connected [imtu %d, omtu %d, flush_to %d]\n", opts.imtu, opts.omtu, opts.flush_to);
-
- if (szRemote)
- {
- strncpy(szRemote, batostr(&addr.l2_bdaddr), iRemoteSize);
- szRemote[iRemoteSize-1] = '\0';
- }
- }
- return new_fd;
+ // Wait client connection
+ struct sockaddr_l2 addr;
+ socklen_t addrlen = sizeof(addr);
+ int new_fd = accept(sockfd, (struct sockaddr *) &addr, &addrlen);
+
+ DBG("Begin");
+ if (szRemote) *szRemote='\0';
+
+ if(new_fd >= 0)
+ {
+ // Get default options
+ struct l2cap_options opts;
+ unsigned int iOptSize = sizeof(opts);
+ if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &iOptSize) >= 0)
+ {
+ if (mtu && opts.imtu)
+ *mtu = opts.imtu;
+ if (mtu && opts.omtu)
+ *mtu = opts.omtu;
+ if (!(*mtu))
+ *mtu=A2DPMAXIMUMTRANSFERUNITSIZE;
+ }
+ DBG("Connected [imtu %d, omtu %d, flush_to %d]\n", opts.imtu, opts.omtu, opts.flush_to);
+
+ if (szRemote)
+ {
+ strncpy(szRemote, batostr(&addr.l2_bdaddr), iRemoteSize);
+ szRemote[iRemoteSize-1] = '\0';
+ }
+ }
+ return new_fd;
}
Index: alsa-plugins/a2dplib.h
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dplib.h,v
retrieving revision 1.1
diff -u -r1.1 a2dplib.h
--- alsa-plugins/a2dplib.h 12 Jul 2006 05:47:06 -0000 1.1
+++ alsa-plugins/a2dplib.h 28 Jul 2006 09:48:08 -0000
@@ -32,37 +32,6 @@
typedef struct snd_pcm_a2dp* LPA2DP;
-/*
-void a2dp_init(void) __attribute__ ((constructor));
-void a2dp_exit(void) __attribute__ ((destructor));
-void change_endian( void *buf, int size);
-// Prepare packet headers
-void init_request(struct avdtp_header * header, int request_id);
-// Analyse the SEIDs the sink has sent to us
-int process_seid(int s, struct acp_seid_info * get_seid_resp, unsigned short *psm, sbc_t *sbc);
-// Detect whether A2DP Sink is present at the destination or not
-int detect_a2dp(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm, unsigned long *flags);
-// Connecting on PSM 25
-int do_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint16_t *mtu);
-int connect_stream(bdaddr_t *src, bdaddr_t *dst, int *cmdfd_return, sbc_t *sbc);
-*/
-/*
-snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t *io);
-void sleeptill(struct timeval *t, struct timeval *dt);
-// returns time to wait ie difference between tsend and current time
-// if time has come, advances tsend
-int time_to_wait(struct timeval *tsend, struct timeval *dt);
-int a2dp_close(snd_pcm_ioplug_t *io);
-int a2dp_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params);
-int a2dp_prepare(snd_pcm_ioplug_t *io);
-int a2dp_drain(snd_pcm_ioplug_t *io);
-int a2dp_descriptors_count(snd_pcm_ioplug_t *io);
-int a2dp_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int space);
-int a2dp_poll(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
-int a2dp_connect(snd_pcm_a2dp_t *a2dp);
-int a2dp_constraint(snd_pcm_a2dp_t *a2dp);
-void a2dp_timer(snd_async_handler_t *async);
-*/
// Global library initialisation
extern void a2dp_init( void);
extern void a2dp_exit( void);
@@ -71,7 +40,7 @@
extern LPA2DP a2dp_new( char* bdaddr);
extern void a2dp_destroy( LPA2DP a2dp);
-// transfers around correct time postions
+// compress and transfers data
extern int a2dp_transfer_raw( LPA2DP a2dp, const char* pcm_buffer, int pcm_buffer_size);
// a2dp server functions
Index: alsa-plugins/a2dplib.o
===================================================================
RCS file: alsa-plugins/a2dplib.o
diff -N alsa-plugins/a2dplib.o
Binary files /tmp/cvsL1OoEg and /dev/null differ
Index: alsa-plugins/ctl_a2dpd.c
===================================================================
RCS file: alsa-plugins/ctl_a2dpd.c
diff -N alsa-plugins/ctl_a2dpd.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-plugins/ctl_a2dpd.c 28 Jul 2006 09:48:08 -0000
@@ -0,0 +1,330 @@
+/*
+* Bluetooth Headset ALSA Plugin
+*
+* Copyright (c) 2006 by Fabien Chevalier
+*
+* 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 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <syslog.h>
+#include <signal.h>
+#include <alsa/asoundlib.h>
+#include <alsa/control_external.h>
+
+#include "a2dp_ipc.h"
+#include "a2dpd_protocol.h"
+
+/* Defines */
+
+#define HS_SPEAKER 0
+#define HS_MICROPHONE 1
+
+#define MINVOL 0
+#define MAXVOL 15
+
+/* Debug */
+
+#define NDEBUG
+#ifdef NDEBUG
+ #define DBG(fmt, arg...)
+#else
+ #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#endif
+
+typedef enum {SPEAKER, MICROPHONE} volume_t;
+
+typedef struct snd_ctl_a2dpd {
+ snd_ctl_ext_t ext;
+} snd_ctl_a2dpd_t;
+
+static const char* vol_devices[] = {
+ "A2DPD0 Playback Volume",
+ "A2DPD1 Capture Volume"
+};
+
+// Signal handler, there is a SIGPIPE sent when using tcp when the daemon is not running
+// We catch it to not quit
+void sighand(int signo)
+{
+ //printf("A2DPD CTL in signal handler %d\n", signo);
+ return;
+}
+
+static void a2dpd_ctl_close(snd_ctl_ext_t *ext)
+{
+ snd_ctl_a2dpd_t *a2dpd = ext->private_data;
+ close_socket(ext->poll_fd);
+ free(a2dpd);
+}
+
+static int a2dpd_ctl_elem_count(snd_ctl_ext_t *ext)
+{
+ DBG("");
+ return 2;
+}
+
+static int a2dpd_ctl_elem_list(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id)
+{
+ DBG("%d", offset);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ if (offset < 2) {
+ snd_ctl_elem_id_set_name(id, vol_devices[offset]);
+ DBG("=> %s", vol_devices[offset]);
+ return 0;
+ }
+ else {
+ return -EINVAL;
+ }
+}
+
+static snd_ctl_ext_key_t a2dpd_ctl_find_elem(snd_ctl_ext_t *ext,
+ const snd_ctl_elem_id_t *id)
+{
+ const char *name = snd_ctl_elem_id_get_name(id);
+ DBG("%s", name);
+
+ if(strcmp(name, vol_devices[0]) == 0) {
+ return HS_SPEAKER;
+ }
+ else if(strcmp(name, vol_devices[1]) == 0) {
+ return HS_MICROPHONE;
+ }
+ else {
+ return SND_CTL_EXT_KEY_NOT_FOUND;
+ }
+}
+
+static int a2dpd_ctl_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ int *type, unsigned int *acc, unsigned int *count)
+{
+ DBG("");
+ *type = SND_CTL_ELEM_TYPE_INTEGER;
+ *acc = SND_CTL_EXT_ACCESS_READWRITE;
+ *count = 1;
+ return 0;
+}
+
+static int a2dpd_ctl_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ long *imin, long *imax, long *istep)
+{
+ DBG("");
+ *istep = 1;
+ *imin = MINVOL;
+ *imax = MAXVOL;
+ return 0;
+}
+
+static int a2dpd_ctl_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value)
+{
+ AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA;
+ DBG("");
+
+ if(!value) return 0;
+
+ *value = 8;
+
+ int client_type=A2DPD_PLUGIN_CTL_READ;
+ int sockfd=make_client_socket();
+
+ if(send_socket(sockfd, &client_type, sizeof(client_type)) == sizeof(client_type))
+ {
+ if(recv_socket(sockfd, &AudioMixerData, sizeof(AudioMixerData)) == sizeof(AudioMixerData))
+ {
+ if(key == HS_SPEAKER)
+ {
+ if(AudioMixerData.volume_speaker_right!=-1 && AudioMixerData.volume_speaker_left!=-1)
+ *value = (AudioMixerData.volume_speaker_right+AudioMixerData.volume_speaker_left)/2;
+ }
+ else if(key == HS_MICROPHONE)
+ {
+ if(AudioMixerData.volume_micro_right!=-1 && AudioMixerData.volume_micro_left!=-1)
+ *value = (AudioMixerData.volume_micro_right+AudioMixerData.volume_micro_left)/2;
+ }
+ }
+ else
+ {
+ DBG("Unable to receive new volume value from server");
+ }
+ }
+ else
+ {
+ DBG("Unable to request new volume value to server");
+ }
+ close_socket(sockfd);
+
+ return 0;
+}
+
+static int a2dpd_ctl_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value)
+{
+ int iResult = 0;
+ long curvalue;
+
+ DBG("");
+
+ a2dpd_ctl_read_integer(ext, key, &curvalue);
+
+ if(value && *value != curvalue)
+ {
+ AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA;
+ int client_type=A2DPD_PLUGIN_CTL_WRITE;
+ int sockfd=make_client_socket();
+
+ if(send_socket(sockfd, &client_type, sizeof(client_type)) == sizeof(client_type))
+ {
+ if(key == HS_SPEAKER)
+ {
+ AudioMixerData.volume_speaker_right = *value;
+ AudioMixerData.volume_speaker_left = *value;
+ }
+ else if(key == HS_MICROPHONE)
+ {
+ AudioMixerData.volume_micro_right = *value;
+ AudioMixerData.volume_micro_left = *value;
+ }
+
+ if(send_socket(sockfd, &AudioMixerData, sizeof(AudioMixerData)) == sizeof(AudioMixerData))
+ {
+ iResult=1;
+ }
+ else
+ {
+ DBG("Unable to send new volume value to server");
+ }
+ }
+ else
+ {
+ DBG("Unable to set new volume value to server");
+ }
+ close_socket(sockfd);
+ }
+
+ iResult=1;
+ return iResult;
+}
+
+static int a2dpd_ctl_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
+ unsigned int *event_mask)
+{
+ AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA;
+// snd_ctl_a2dpd_t *a2dpd = ext->private_data;
+
+ DBG("");
+ syslog(LOG_INFO, "%s", __FUNCTION__);
+ if(recv_socket(ext->poll_fd, &AudioMixerData, sizeof(AudioMixerData)) == sizeof(AudioMixerData))
+ {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+
+ if(AudioMixerData.volume_speaker_right!=-1 || AudioMixerData.volume_speaker_left!=-1)
+ snd_ctl_elem_id_set_name(id, vol_devices[HS_SPEAKER]);
+ else if(AudioMixerData.volume_micro_right!=-1 || AudioMixerData.volume_micro_left!=-1)
+ snd_ctl_elem_id_set_name(id, vol_devices[HS_MICROPHONE]);
+
+ *event_mask = SND_CTL_EVENT_MASK_VALUE;
+ return 1;
+ }
+ else
+ {
+ syslog(LOG_INFO, "error %s", __FUNCTION__);
+ DBG("Unable to receive volume notification from server");
+ return -errno;
+ }
+ return -EINVAL;
+ /*
+ if(recv(a2dpd->serverfd, &pkt, sizeof(pkt), MSG_DONTWAIT) == sizeof(pkt)) {
+ if(pkt.type == PKT_TYPE_CTL_NTFY) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, pkt.voltype == SPEAKER ? vol_devices[HS_SPEAKER] : vol_devices[HS_MICROPHONE]);
+ *event_mask = SND_CTL_EVENT_MASK_VALUE;
+ return 1;
+ }
+ else {
+ SNDERR("Unexpected packet type %d received!", pkt.type);
+ return -EAGAIN;
+ }
+ }
+ else {
+ return -errno;
+ }*/
+}
+
+static snd_ctl_ext_callback_t a2dpd_ext_callback = {
+ .close = a2dpd_ctl_close,
+ .elem_count = a2dpd_ctl_elem_count,
+ .elem_list = a2dpd_ctl_elem_list,
+ .find_elem = a2dpd_ctl_find_elem,
+ .get_attribute = a2dpd_ctl_get_attribute,
+ .get_integer_info = a2dpd_ctl_get_integer_info,
+ .read_integer = a2dpd_ctl_read_integer,
+ .write_integer = a2dpd_ctl_write_integer,
+ .read_event = a2dpd_ctl_read_event,
+};
+
+SND_CTL_PLUGIN_DEFINE_FUNC(a2dpd)
+{
+ snd_config_iterator_t it, next;
+ int err = 0;
+ snd_ctl_a2dpd_t *a2dpd = 0;
+
+ // set up thread signal handler
+ signal(SIGPIPE, sighand);
+
+ DBG("");
+ snd_config_for_each(it, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(it);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (!strcmp(id, "comment") || !strcmp(id, "type"))
+ //if (snd_pcm_conf_generic_id(id)) // Alsa-lib 1.0.11
+ continue;
+ SNDERR("Unknown field %s", id);
+ return -EINVAL;
+ }
+
+ a2dpd = malloc(sizeof(*a2dpd));
+ if(a2dpd == NULL)
+ {
+ err=ENOMEM;
+ goto error;
+ }
+
+ a2dpd->ext.version = SND_CTL_EXT_VERSION;
+ a2dpd->ext.card_idx = 0; //FIXME
+ strncpy(a2dpd->ext.id, "A2DPD CTL ID", sizeof(a2dpd->ext.id) - 1);
+ strncpy(a2dpd->ext.driver, "A2DPD CTL Bluetooth Headset Driver", sizeof(a2dpd->ext.driver) - 1);
+ strncpy(a2dpd->ext.name, "A2DPD CTL Headset Name", sizeof(a2dpd->ext.name) - 1);
+ strncpy(a2dpd->ext.longname, "A2DPD CTL Headset Long Name", sizeof(a2dpd->ext.longname) - 1);
+ strncpy(a2dpd->ext.mixername, "A2DPD CTL Headset Mixer Name", sizeof(a2dpd->ext.mixername) - 1);
+ a2dpd->ext.callback = &a2dpd_ext_callback;
+ a2dpd->ext.poll_fd = make_udp_socket();
+ a2dpd->ext.private_data = a2dpd;
+
+ err = snd_ctl_ext_create(&a2dpd->ext, name, mode);
+ if (err < 0)
+ goto error;
+
+ *handlep = a2dpd->ext.handle;
+ return 0;
+
+error:
+ if(a2dpd->ext.poll_fd!=-1) close_socket(a2dpd->ext.poll_fd);
+ if(a2dpd != NULL) free(a2dpd);
+ return err;
+}
+
+SND_CTL_PLUGIN_SYMBOL(a2dpd);
Index: alsa-plugins/pcm_a2dpd.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/pcm_a2dpd.c,v
retrieving revision 1.1
diff -u -r1.1 pcm_a2dpd.c
--- alsa-plugins/pcm_a2dpd.c 12 Jul 2006 05:47:07 -0000 1.1
+++ alsa-plugins/pcm_a2dpd.c 28 Jul 2006 09:48:08 -0000
@@ -32,24 +32,16 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include <syslog.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
#include <alsa/timer.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-#include <syslog.h>
+#include "a2dpd_protocol.h"
+#include "a2dp_timer.h"
+#include "a2dp_ipc.h"
-#include "sbc.h"
#include "../a2dp.h"
#define NONSPECAUDIO 1
@@ -59,285 +51,168 @@
#define DBG(fmt, arg...) printf("DEBUG: %s: (errno=%d:%s)" fmt "\n" , __FUNCTION__ , errno, strerror(errno), ## arg)
//#define DBG(D...)
-static void a2dp_init(void) __attribute__ ((constructor));
-static void a2dp_exit(void) __attribute__ ((destructor));
+
+// Signal handler, there is a strange SIGPIPE when the daemon is not running
+// We catch it to not quit
+void sighand(int signo)
+{
+ printf("A2DPD CTL in signal handler %d\n", signo);
+ return;
+}
typedef struct snd_pcm_a2dp {
snd_pcm_ioplug_t io;
- int refcnt;
- int timeout;
- unsigned long state;
- bdaddr_t src;
- bdaddr_t dst;
int sk;
- int control_sk;
- sbc_t sbc;
+ int rate;
+ int channels;
snd_pcm_sframes_t num;
- unsigned char buf[1024];
- unsigned int len;
unsigned int frame_bytes;
- int use_rfcomm;
-
- char bufe[BUFS];
- int lenbufe;//=0;
-
- unsigned long nbytes;//=0;
- struct timeval tsend;
-
- time_t timestamp;//=0;
- uint16_t seq_num;//=1;
- int frame_count;//=0;
-
+ TIMERINFO TimerInfos;
} snd_pcm_a2dp_t;
-static void inline a2dp_get(snd_pcm_a2dp_t *a2dp)
+static int a2dp_disconnect(snd_pcm_a2dp_t *a2dp)
{
- a2dp->refcnt++;
- a2dp->timeout = 0;
+ close_socket(a2dp->sk);
+ a2dp->sk = -1;
+ return 0;
}
-static void inline a2dp_put(snd_pcm_a2dp_t *a2dp)
+static int a2dp_connect(snd_pcm_a2dp_t *a2dp)
{
- a2dp->refcnt--;
-
- if (a2dp->refcnt <= 0)
- a2dp->timeout = 2;
+ if(a2dp->sk <= 0)
+ {
+ int sockfd = make_client_socket();
+ a2dp->sk = -1;
+ if(sockfd>0)
+ {
+ int32_t client_type = A2DPD_PLUGIN_PCM_WRITE;
+ if(send_socket(sockfd, &client_type, sizeof(client_type))==sizeof(client_type))
+ {
+ a2dp->sk = sockfd;
+ syslog(LOG_INFO, "Connected a2dp %p, sk %d", a2dp, a2dp->sk);
+ }
+ 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 int a2dp_start(snd_pcm_ioplug_t *io)
+static inline snd_pcm_a2dp_t *a2dp_alloc(void)
{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
+ snd_pcm_a2dp_t *a2dp;
+ DBG("Init");
+ a2dp = malloc(sizeof(*a2dp));
+ if (!a2dp)
+ return NULL;
+ memset(a2dp, 0, sizeof(*a2dp));
+ a2dp->sk = -1;
+ a2dp->TimerInfos.fps = A2DPD_BLOCK_FREQUENCY;
+ DBG("OK");
+ return a2dp;
+}
- a2dp->len = 13;
+static inline void a2dp_free(snd_pcm_a2dp_t *a2dp)
+{
+ DBG("Finishing");
+ a2dp_disconnect(a2dp);
+ free(a2dp);
+ DBG("OK");
+}
+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;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
+ //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;
-
return a2dp->num;
}
-static int a2dp_disconnect(snd_pcm_a2dp_t *a2dp)
+// This is the main transfer func which does the transfer and sleep job
+static snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t *io,
+ char* buf,
+ int32_t datatoread)
{
- if(a2dp->sk > fileno(stderr))
- {
- DBG("a2dp %p", a2dp);
+ snd_pcm_a2dp_t* a2dp = io->private_data;
+ int transfer = 0;
- close(a2dp->sk);
- a2dp->sk = -1;
- a2dp->state = BT_CLOSED;
- }
- else
- {
- //DBG("INVALID DISCONNECT a2dp %p, sk %d", a2dp, a2dp->sk);
- }
- return 0;
-}
+ // Connect if needed and send
+ a2dp_connect(a2dp);
+ if(transfer>=0) transfer=send_socket(a2dp->sk, &datatoread, sizeof(datatoread));
+ if(transfer>=0) transfer=send_socket(a2dp->sk, buf, datatoread);
-static int a2dp_connect(snd_pcm_a2dp_t *a2dp)
-{
- if(a2dp->sk <= fileno(stderr))
- {
- //DBG("a2dp %p", a2dp);
+ // Disconnect if error detected
+ if(transfer<0) a2dp_disconnect(a2dp);
- int sockfd = socket(PF_INET, SOCK_STREAM, 0);
- if(sockfd>0)
- {
- struct sockaddr_in my_addr;
- memset(&my_addr, 0, sizeof(my_addr));
- my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(0);
- my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ // 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);
+ a2dp_timer_sleep(&a2dp->TimerInfos, 4*A2DPTIMERPREDELAY);
- if(bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr)) == 0)
- {
- struct timeval t;
- t.tv_sec=0;
- t.tv_usec=100*1000; // 100 ms
- setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
- setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
-
- struct sockaddr_in peer_addr;
- peer_addr.sin_family = AF_INET;
- peer_addr.sin_port = htons(21453);
- peer_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- // Make connection
- if(connect(sockfd, (struct sockaddr*)&peer_addr, sizeof(peer_addr)) == 0)
- {
- a2dp->sk = sockfd;
- a2dp->state = BT_CONNECTED;
- syslog(LOG_INFO, "Connected a2dp %p, sk %d", a2dp, a2dp->sk);
- }
- else
- {
- // Clean socket
- a2dp->sk = -1;
- //DBG("Connect failed a2dp %p, sk %d", a2dp, a2dp->sk);
- syslog(LOG_WARNING, "Connect failed a2dp %p, sk %d", a2dp, a2dp->sk);
- close(sockfd);
- }
- }
- else
- {
- DBG("Bind failed a2dp %p, sk %d", a2dp, a2dp->sk);
- syslog(LOG_WARNING, "Bind failed a2dp %p, sk %d", a2dp, a2dp->sk);
- a2dp->sk = -1;
- close(sockfd);
- }
- }
- else
- {
- DBG("Socket failed a2dp %p, sk %d", a2dp, a2dp->sk);
- syslog(LOG_WARNING, "Socket failed a2dp %p, sk %d", a2dp, a2dp->sk);
- }
- }
- return 0;
-}
-
-int a2dp_send(snd_pcm_a2dp_t* a2dp, void* buf, int len)
-{
- int result = -1;
- int ioffset = 0;
-
- if(a2dp->sk>fileno(stderr))
+ // Stats
+ if(a2dp->TimerInfos.display>0)
{
- // The system may split 512 bytes of data so we loop
- while(ioffset<len)
- {
- result=send(a2dp->sk, ((char*)buf)+ioffset, len-ioffset, 0);
- //DBG("[2] send(sk, buf+%d, %d-%d=%d) == %d", ioffset, datatoread, ioffset, datatoread-ioffset, result);
- if(result>0)
- {
- ioffset += result;
- }
- else
- {
- break;
- }
- }
-
- if(result<0)
+ if(errno != 0 || transfer <= 0)
{
- if(errno != EAGAIN)
- {
- DBG("[2] send() failed with value = %d, (errno=%d:%s)", result, errno, strerror(errno));
- // Disconnect on error, we will reconnect later
- a2dp_disconnect(a2dp);
- }
+ syslog( LOG_INFO, "send_socket(%d bytes)=%d (errno=%d:%s)", datatoread, transfer, errno, strerror(errno));
}
}
- return result;
+ // 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 job
-static snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t *io,
+static snd_pcm_sframes_t a2dp_transfer_all(snd_pcm_ioplug_t *io,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- int codesize=a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->sbc.channels*2;
- int datatoread=min(codesize,size*a2dp->frame_bytes);
- int transfer = 0;
- int display = 0;
- char* buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8;
-
- // Time reference
- static struct timeval staticcounter = {0,0};
- static int icount = 0;
- if(staticcounter.tv_sec==0)
+ snd_pcm_a2dp_t* a2dp = io->private_data;
+ int i = 0;
+ snd_pcm_sframes_t totaltransfered = 0;
+ while(i++<1 && totaltransfered < size)
{
- gettimeofday(&staticcounter, NULL);
- }
-
- struct timeval timeofday, duration, playtime, theoricaldate;
- gettimeofday(&timeofday, NULL);
- timersub(&timeofday, &staticcounter, &duration);
-
- // Display data once per second
- if(duration.tv_sec>0)
- {
- gettimeofday(&staticcounter, NULL);
- display=icount;
- icount=1;
- }
-
- // sleeps a little bit to synchronize sound
- // a2dp->sbc.channels*44100*2/(size*a2dp->frame_bytes);
- // 344.53125=channels*freq*16 bits/sizeof(buf)
- float fps=344.53125;
- playtime.tv_sec=0;//ipart(icount/fps);
- playtime.tv_usec=(int)(1.0*1000.0*1000.0*icount/fps);
- timeradd(&staticcounter, &playtime, &theoricaldate);
-
- // Si la date théorique est supérieure à la date actuelle
- if((theoricaldate.tv_sec>timeofday.tv_sec)
- || (theoricaldate.tv_sec==timeofday.tv_sec && theoricaldate.tv_usec>timeofday.tv_usec)
- )
- {
- // We're in advance, wait a little bit
- timersub(&theoricaldate, &timeofday, &duration);
- if(duration.tv_sec>0)
+ char* buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8;
+ int datatoread=min(A2DPD_BLOCK_SIZE,size*a2dp->frame_bytes);
+ snd_pcm_sframes_t transfered = a2dp_transfer2(io, buf, datatoread);
+ if(transfered>0)
{
- DBG("ERROR duration calculed more than one sec : { %d, %d }", (int)duration.tv_sec, (int)duration.tv_usec);
+ offset += transfered;
+ totaltransfered += transfered;
}
else
{
- usleep(duration.tv_usec);
+ break;
}
}
- else
- {
- // We're late, do nothing
- }
-
- // Connect if needed and send
- a2dp_connect(a2dp);
- if(transfer>=0) transfer=a2dp_send(a2dp, &datatoread, sizeof(datatoread));
- if(transfer>=0) transfer=a2dp_send(a2dp, buf, datatoread);
- if(display>0 && transfer >=0)
- {
- DBG("a2dp_send(%d * %d = %d bytes of data) returned %d (%d fps)", (int)size, (int)a2dp->frame_bytes, datatoread, transfer, display);
- }
-
- icount++;
-
- // update pointer, tells alsa we're done
- a2dp->num += datatoread / a2dp->frame_bytes;
-
- return datatoread / a2dp->frame_bytes;
+ return totaltransfered;
}
static int a2dp_close(snd_pcm_ioplug_t *io)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
a2dp_disconnect(a2dp);
-
- a2dp->len = 0;
-
- a2dp_put(a2dp);
-
+ a2dp_free(a2dp);
return 0;
}
@@ -364,51 +239,27 @@
static int a2dp_prepare(snd_pcm_ioplug_t *io)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
-
DBG("a2dp %p", a2dp);
-
- a2dp->len = 13;
-
a2dp->num = 0;
-
- a2dp->sbc.rate = io->rate;
- a2dp->sbc.channels = io->channels;
- a2dp->sbc.subbands = 8; // safe default
- a2dp->sbc.blocks = 16; // safe default
- a2dp->sbc.bitpool = 32; // recommended value 53, safe default is 32
-
+ a2dp->rate = io->rate;
+ a2dp->channels = io->channels;
return 0;
}
static int a2dp_drain(snd_pcm_ioplug_t *io)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
-
DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
return 0;
}
static int a2dp_descriptors_count(snd_pcm_ioplug_t *io)
{
- // snd_pcm_a2dp_t *a2dp = io->private_data;
-
- //DBG("Descriptor count = %d (state=%lu)", (a2dp->state != BT_CLOSED)?1:0, a2dp->state);
-
- return 1; //(a2dp->state != BT_CLOSED)?1:0;
+ return 1;
}
static int a2dp_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int space)
{
-// snd_pcm_a2dp_t *a2dp = io->private_data;
-
-/*
- // We won't reconnect if we do not have data
- if (a2dp->state == BT_CLOSED)
- return 0;
-*/
if (space < 1)
{
DBG("Can't fill in descriptors");
@@ -417,14 +268,9 @@
}
// Alsa does make sure writing now will not block
- // If we have a valid socket, poll the socket
- // Else poll stdout, so that Alsa do not block.
- // We use sockets with low timeouts
- pfds[0].fd = /*(a2dp->state == BT_CONNECTED)?(a2dp->sk):*/fileno(stdout);
+ // So give him an always writable socket!
+ pfds[0].fd = fileno(stdout);
pfds[0].events = POLLOUT;
-
- // DBG("Descriptor filling=%d state=%lu fd = %d", (a2dp->state != BT_CLOSED)?1:0, a2dp->state, pfds[0].fd);
-
return 1;
}
@@ -432,12 +278,9 @@
unsigned int nfds, unsigned short *revents)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
-
- //DBG("Descriptor polling = %d (state=%lu)", (a2dp->state != BT_CLOSED)?1:0, a2dp->state);
-
*revents = pfds[0].revents;
- if (a2dp->state == BT_CLOSED)
+ if (a2dp->sk<=0)
return 0;
if (pfds[0].revents & POLLHUP) {
@@ -449,21 +292,22 @@
}
static snd_pcm_ioplug_callback_t a2dp_callback = {
- .start = a2dp_start, //FDOK
- .stop = a2dp_stop,//FDOK
- .pointer = a2dp_pointer,//FDOK
- .transfer = a2dp_transfer2,
- .close = a2dp_close,//FDOK
- .hw_params = a2dp_params,//FDOK
- .prepare = a2dp_prepare,//FDOK
- .drain = a2dp_drain,//FDOK
-
- .poll_descriptors_count = a2dp_descriptors_count,//FDOK
- .poll_descriptors = a2dp_descriptors,//FDOK
- .poll_revents = a2dp_poll,//FDOK
+ .start = a2dp_start,
+ .stop = a2dp_stop,
+ .pointer = a2dp_pointer,
+ .transfer = a2dp_transfer_all,
+ .close = a2dp_close,
+ .hw_params = a2dp_params,
+ .prepare = a2dp_prepare,
+ .drain = a2dp_drain,
+
+ .poll_descriptors_count = a2dp_descriptors_count,
+ .poll_descriptors = a2dp_descriptors,
+ .poll_revents = a2dp_poll,
};
-// Force alsa to give use the 44100 or 48000 hz sound
+// Force alsa to give use the 44100 hz sound
+// Or say alsa we will accept only 44100hz?
static int a2dp_constraint(snd_pcm_a2dp_t *a2dp)
{
snd_pcm_ioplug_t *io = &a2dp->io;
@@ -474,7 +318,7 @@
unsigned int format[2], channel[2], rate[2];
int err;
- DBG("[build %s %s] a2dp %p", __DATE__, __TIME__, a2dp);
+ syslog(LOG_INFO, "[build %s %s] a2dp %p", __DATE__, __TIME__, a2dp);
err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, 2, access_list);
if (err < 0)
@@ -493,10 +337,10 @@
if (err < 0)
return err;
- rate[0] = 44100;
- rate[1] = 48000;
+ rate[0] = A2DPD_FRAME_RATE;
+ //rate[1] = 48000;
- err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, 2, rate);
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, 1, rate);
if (err < 0)
return err;
@@ -511,184 +355,20 @@
return 0;
}
-#define MAX_CONNECTIONS 10
-
-static snd_pcm_a2dp_t *connections[MAX_CONNECTIONS];
-
-//static snd_timer_t *timer = NULL;
-
-static volatile sig_atomic_t __locked = 0;
-
-static inline void a2dp_lock(void)
-{
- while (__locked)
- usleep(100);
-
- __locked = 1;
-}
-
-static inline void a2dp_unlock(void)
-{
- __locked = 0;
-}
-
-static inline snd_pcm_a2dp_t *a2dp_alloc(void)
-{
- snd_pcm_a2dp_t *a2dp;
- DBG("Init");
- a2dp = malloc(sizeof(*a2dp));
- if (!a2dp)
- return NULL;
-
- memset(a2dp, 0, sizeof(*a2dp));
-
- a2dp->sk = -1;
- a2dp->refcnt = 1;
- a2dp->seq_num = 1;
- a2dp->state = BT_OPEN;
-
- sbc_init(&a2dp->sbc, 0L);
-
- DBG("OK");
- return a2dp;
-}
-
-static inline void a2dp_free(snd_pcm_a2dp_t *a2dp)
-{
- DBG("Finishing");
- a2dp_disconnect(a2dp);
-
- sbc_finish(&a2dp->sbc);
-
- free(a2dp);
- DBG("OK");
-}
-/*
-// Le timer gère la déconnexion
-static void a2dp_timer(snd_async_handler_t *async)
-{
- snd_timer_t *handle = snd_async_handler_get_timer(async);
- snd_timer_read_t tr;
- int i, ticks = 0;
-
- while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr))
- ticks += tr.ticks;
-
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++) {
- snd_pcm_a2dp_t *a2dp = connections[i];
-
- if (a2dp && a2dp->refcnt <= 0) {
- a2dp->timeout = ((a2dp->timeout * 1000) - ticks) / 1000;
- if (a2dp->timeout <= 0) {
- connections[i] = NULL;
- a2dp_free(a2dp);
- }
- }
- }
-
- a2dp_unlock();
-}
-*/
-static void a2dp_init(void)
-{/*
- snd_async_handler_t *async;
- snd_timer_info_t *info;
- snd_timer_params_t *params;
- long resolution;
- char timername[64];
- int err, i;
-
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++)
- connections[i] = NULL;
-
- a2dp_unlock();
-
- snd_timer_info_alloca(&info);
- snd_timer_params_alloca(¶ms);
-
- sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
- SND_TIMER_CLASS_GLOBAL, SND_TIMER_CLASS_NONE, 0,
- SND_TIMER_GLOBAL_SYSTEM, 0);
-
- err = snd_timer_open(&timer, timername, SND_TIMER_OPEN_NONBLOCK);
- if (err < 0) {
- SNDERR("Can't open global timer");
- return;
- }
-
- err = snd_timer_info(timer, info);
- if (err < 0) {
- SNDERR("Can't get global timer info");
- return;
- }
-
- snd_timer_params_set_auto_start(params, 1);
-
- resolution = snd_timer_info_get_resolution(info);
- snd_timer_params_set_ticks(params, 1000000000 / resolution);
- if (snd_timer_params_get_ticks(params) < 1)
- snd_timer_params_set_ticks(params, 1);
-
- err = snd_timer_params(timer, params);
- if (err < 0) {
- SNDERR("Can't set global timer parameters");
- snd_timer_close(timer);
- return;
- }
-
- err = snd_async_add_timer_handler(&async, timer, a2dp_timer, NULL);
- if (err < 0) {
- SNDERR("Can't create global async callback");
- snd_timer_close(timer);
- return;
- }
-
- err = snd_timer_start(timer);
-*/
-}
-
-static void a2dp_exit(void)
-{/*
- int err, i;
-
- err = snd_timer_stop(timer);
-
- err = snd_timer_close(timer);
-
- a2dp_lock();
-
- for (i = 0; i < MAX_CONNECTIONS; i++) {
- snd_pcm_a2dp_t *a2dp = connections[i];
-
- if (a2dp) {
- connections[i] = NULL;
- a2dp_free(a2dp);
- }
- }
-
- a2dp_unlock();
-*/
-}
-
SND_PCM_PLUGIN_DEFINE_FUNC(a2dpd)
{
snd_pcm_a2dp_t *a2dp = NULL;
snd_config_iterator_t i, next;
- bdaddr_t src, dst;
- int err, n, pos = -1, use_rfcomm = 0;
+ int err = 0;
DBG("name %s mode %d", name, mode);
- bacpy(&src, BDADDR_ANY);
- bacpy(&dst, BDADDR_ANY);
+ // set up thread signal handler
+ signal(SIGPIPE,sighand);
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
- const char *id, *addr;
+ const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
@@ -696,90 +376,24 @@
if (!strcmp(id, "comment") || !strcmp(id, "type"))
continue;
- if (!strcmp(id, "bdaddr") || !strcmp(id, "dst")) {
- if (snd_config_get_string(n, &addr) < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- DBG("bdaddr/dest is %s", addr);
- str2ba(addr, &dst);
- continue;
- }
-
- if (!strcmp(id, "local") || !strcmp(id, "src")) {
- if (snd_config_get_string(n, &addr) < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- str2ba(addr, &src);
- continue;
- }
-
- if (!strcmp(id, "use_rfcomm")) {
- if ((err = snd_config_get_bool(n)) < 0) {
- SNDERR("The field use_rfcomm must be a boolean type");
- return err;
- }
- use_rfcomm = err;
+ // Ignore old options
+ if (strstr("ipaddr bdaddr port src dst use_rfcomm", id))
continue;
- }
SNDERR("Unknown field %s", id);
return -EINVAL;
}
- a2dp_lock();
-
- for (n = 0; n < MAX_CONNECTIONS; n++) {
- if (connections[n]) {
- if (!bacmp(&connections[n]->dst, &dst) &&
- (!bacmp(&connections[n]->src, &src) ||
- !bacmp(&src, BDADDR_ANY))) {
- a2dp = connections[n];
- a2dp_get(a2dp);
- break;
- }
- } else if (pos < 0)
- pos = n;
- }
-
- if (!a2dp) {
- if (pos < 0) {
- SNDERR("Too many connections");
- return -ENOMEM;
- }
-
- a2dp = a2dp_alloc();
- if (!a2dp) {
- SNDERR("Can't allocate");
- return -ENOMEM;
- }
-
- connections[pos] = a2dp;
-
- a2dp->state = BT_CONNECT;
-
- bacpy(&a2dp->src, &src);
- bacpy(&a2dp->dst, &dst);
- a2dp->use_rfcomm = use_rfcomm;
+ a2dp = a2dp_alloc();
+ if (!a2dp)
+ {
+ SNDERR("Can't allocate plugin data");
+ return -ENOMEM;
}
- a2dp_unlock();
-/*
- if (a2dp->state != BT_CONNECTED) {
- err = a2dp_connect(a2dp);
- if (err < 0) {
- DBG("Can't connect");
- SNDERR("Can't connect");
- goto error;
- }
-
- a2dp->state = BT_CONNECTED;
- }
-*/
// Connect
a2dp_connect(a2dp);
-
+
// Notify plugin
a2dp->io.version = SND_PCM_IOPLUG_VERSION;
a2dp->io.name = "Bluetooth Advanced Audio Distribution";
@@ -802,7 +416,7 @@
error:
a2dp_disconnect(a2dp);
- a2dp_put(a2dp);
+ a2dp_free(a2dp);
return err;
}
[-- Attachment #3: Type: text/plain, Size: 348 bytes --]
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
[-- Attachment #4: Type: text/plain, Size: 164 bytes --]
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2006-07-28 22:02 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-28 10:26 [Bluez-devel] New A2DP related progress Frédéric DALLEAU
2006-07-28 22:02 ` Brad Midgley
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).