Skip to contentSkip to conversation

yes, a2dp timing is whacked

Bluez-devel Brad Midgley 3 participants 9 messages May 4, 2005 – May 9, 2005 Bluetooth drivers

lore
Share & tools
lore
#0 lore raw

Hey

I have the hp headset now. woohoo. a2play will use it but only after tuning the usleep parameter for my specific setup. I think the hp does buffer a little bit (a2play seems to finish streaming just a bit before the audio track finishes)

I will start by looking at the rtc interface and trying to find hooks in alsa for timing.

Maybe for convenience, libsbc should compute the timeslice for the frame.

Brad

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel

#1 lore raw

Moin,

Am Wed, 04 May 2005 13:58:49 -0600 schrieb Brad Midgley:

Quoted 2 lines

Maybe for convenience, libsbc should compute the timeslice for the frame.

It does now. I added a function sbc_time(sbc_t *sbc) which computes the microseconds for one frame. I then replaced the fixed value in a2play's usleep() call with the time the whole packet should take to play (time for one frame times number of frames in the packet) multiplied by (arbitrarily chosen) 0.8 to make up for the time lost with computing and sending.

Quoted 2 lines

I will start by looking at the rtc interface and trying to find hooks in alsa for timing.

That would be really cool.

Signature · 4 lines
Henryk Plötz
Grüße aus Berlin
~~~~~~~ Un-CDs, nein danke! http://www.heise.de/ct/cd-register/ ~~~~~~~
~ Help Microsoft fight software piracy: Give Linux to a friend today! ~
maintainer
#2 lore raw

Hi Henryk,

Quoted 9 lines
Quoted 2 lines

Maybe for convenience, libsbc should compute the timeslice for the frame.

It does now. I added a function sbc_time(sbc_t *sbc) which computes the microseconds for one frame. I then replaced the fixed value in a2play's usleep() call with the time the whole packet should take to play (time for one frame times number of frames in the packet) multiplied by (arbitrarily chosen) 0.8 to make up for the time lost with computing and sending.

the time that is eaten up by encoding or decoding could be measured with gettimeofday() and then you can subtract this from the whole time.

Since it is a linear operation we can make the SBC library do it all for us. Let sbc_encode() and sbc_decode() calculate the processing time and then store a sleep value for us inside sbc_t.

And the value should be really unsigned long and not int.

Regards

Marcel

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel

maintainer
#3 lore raw

Hi Henryk,

Quoted 10 lines
Quoted 9 lines
Quoted 2 lines

Maybe for convenience, libsbc should compute the timeslice for the frame.

It does now. I added a function sbc_time(sbc_t *sbc) which computes the microseconds for one frame. I then replaced the fixed value in a2play's usleep() call with the time the whole packet should take to play (time for one frame times number of frames in the packet) multiplied by (arbitrarily chosen) 0.8 to make up for the time lost with computing and sending.

Quoted 6 lines

the time that is eaten up by encoding or decoding could be measured with gettimeofday() and then you can subtract this from the whole time.

Since it is a linear operation we can make the SBC library do it all for us. Let sbc_encode() and sbc_decode() calculate the processing time and then store a sleep value for us inside sbc_t.

forget about this stupid idea. I tried it and it is not working out. So I only moved the time calculation into sbc->duration and added the gettimeofday() magic into a2play. This is somekind of working, but the quality is not that good with the HP headphone.

I also figured out that the HP headphone has somekind of buffer and you can send more than one AVDTP frame (equals 8 SBC frames). But it seems that this is somekind of ringbuffer. Too many frames will overwrite previous frames and make the sound a mess.

So the best thing is too encode two or three full AVDTP frames in advance and have them ready to send. The sending can't be controlled be usleep(). We need a time that kicks in every whatever msec and makes us to transmit the next frame.

Regards

Marcel

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel

#4 lore raw

Moin,

Am Sat, 07 May 2005 21:27:33 +0200 schrieb Marcel Holtmann:

Quoted 1 line

forget about this stupid idea. I tried it and it is not working out.

I didn't like it either.

Quoted 4 lines

So the best thing is too encode two or three full AVDTP frames in advance and have them ready to send. The sending can't be controlled be usleep(). We need a time that kicks in every whatever msec and makes us to transmit the next frame.

That's what I would favour. According to the libc manual setitimer() would be the function of choice, if nobody else has a better idea (RTC, anyone?).

Signature · 4 lines
Henryk Plötz
Grüße aus Berlin
~~~~~~~ Un-CDs, nein danke! http://www.heise.de/ct/cd-register/ ~~~~~~~
~ Help Microsoft fight software piracy: Give Linux to a friend today! ~
maintainer
#5 lore raw

Hi Henryk,

Quoted 8 lines
Quoted 4 lines

So the best thing is too encode two or three full AVDTP frames in advance and have them ready to send. The sending can't be controlled be usleep(). We need a time that kicks in every whatever msec and makes us to transmit the next frame.

That's what I would favour. According to the libc manual setitimer() would be the function of choice, if nobody else has a better idea (RTC, anyone?).

so in general the time that a SBC frame is calculated for, is always the same if we don't change the parameters, right?

Using the ITIMER_REAL domain may works and since you can setup your timer in microseconds, then this would be the way to go.

Regards

Marcel

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel

#6 lore raw

Guys,

I put a rough proof-of-concept implementation together using setitimer. (Yes, it's gross and the signal handler doesn't actually do any work, just indicates that the interval is over)

The music seems to be timed right but it's a very "noisy" signal on the hp headset. Any ideas?

I didn't commit it because it's so ugly.

Brad

Quoted 13 lines · Marcel Holtmann

Hi Henryk,

Quoted 8 lines
Quoted 4 lines

So the best thing is too encode two or three full AVDTP frames in advance and have them ready to send. The sending can't be controlled be usleep(). We need a time that kicks in every whatever msec and makes us to transmit the next frame.

That's what I would favour. According to the libc manual setitimer() would be the function of choice, if nobody else has a better idea (RTC, anyone?).

Quoted 5 lines

so in general the time that a SBC frame is calculated for, is always the same if we don't change the parameters, right?

Using the ITIMER_REAL domain may works and since you can setup your timer in microseconds, then this would be the way to go.

Index: a2play.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/a2play.c,v retrieving revision 1.64

a2play.c +27 -2
diff -u -b -B -w -p -r1.64 a2play.c
@@ -56,6 +56,7 @@
 #define BUFS 1024
 
 static volatile int terminate = 0;
+static volatile int xmitpause = 0;
 static int cmdfd;
 
 struct sbc_frame_header sbc_info;
@@ -102,6 +103,16 @@ static void sig_term(int sig)
 	terminate = 1;
 }
 
+static void sig_alrm(int sig)
+{
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags = SA_NOCLDSTOP;
+	sa.sa_handler = sig_alrm;
+	sigaction(SIGALRM, &sa, NULL);
+	xmitpause = 0;
+}
+
 // Usage
 static void usage()
 {
@@ -404,6 +415,8 @@ s_config was set from sbc_info before:
 
 int main(int argc, char *argv[])
 {
+	int timerset = 0;
+	struct itimerval itimer;
 	struct sigaction sa;
 	struct timeval start, end;
 	int streamfd;
@@ -619,6 +632,8 @@ int main(int argc, char *argv[])
 
 	timestamp = 0;
 
+	sig_alrm(0);
+
 	while (!terminate) {
 
 		// a2dp headers: avdtp p.45
@@ -698,8 +713,18 @@ int main(int argc, char *argv[])
 
 			memcpy(buf + sizeof(packet_header), &payload_header, sizeof(payload_header));
 			write(streamfd, buf, psize);
-			//printf("\nSending packet:%d, size %d no. of sbc frames:%d", seq_num, psize, payload_header.frame_count);
 
+			if(!timerset) {
+				timerset = 1;
+				itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
+				itimer.it_interval.tv_usec = itimer.it_value.tv_usec = (sleeptime*95)/100;
+				if(setitimer(ITIMER_REAL, &itimer, NULL)) printf("couldn't setitimer\n");
+			}
+
+			while(xmitpause) usleep(10);
+			xmitpause = 1;
+#if 0
+			//printf("\nSending packet:%d, size %d no. of sbc frames:%d", seq_num, psize, payload_header.frame_count);
 			pending++;
 
 			if (pending > 4) {
@@ -717,7 +742,7 @@ int main(int argc, char *argv[])
 				sleeptime = 0;
 				pending = 0;
 			}
-
+#endif
 			/* END: NONSPECAUDIO == FALSE */
 		}
 
#7 lore raw

I think my hack introduced timing issues of its own... tomorrow I'll fix it up with code that uses a ring buffer and bleeds from the ring in the sighandler.

Quoted 107 lines · Brad Midgley

Guys,

I put a rough proof-of-concept implementation together using setitimer. (Yes, it's gross and the signal handler doesn't actually do any work, just indicates that the interval is over)

The music seems to be timed right but it's a very "noisy" signal on the hp headset. Any ideas?

I didn't commit it because it's so ugly.

Brad

Quoted 15 lines · Marcel Holtmann

Hi Henryk,

Quoted 9 lines
Quoted 4 lines

So the best thing is too encode two or three full AVDTP frames in advance and have them ready to send. The sending can't be controlled be usleep(). We need a time that kicks in every whatever msec and makes us to transmit the next frame.

That's what I would favour. According to the libc manual setitimer() would be the function of choice, if nobody else has a better idea (RTC, anyone?).

Quoted 5 lines

so in general the time that a SBC frame is calculated for, is always the same if we don't change the parameters, right?

Using the ITIMER_REAL domain may works and since you can setup your timer in microseconds, then this would be the way to go.

------------------------------------------------------------------------

Index: a2play.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/a2play.c,v retrieving revision 1.64
diff -u -b -B -w -p -r1.64 a2play.c

a2play.c +26 -1
@@ -56,6 +56,7 @@
 #define BUFS 1024
 
 static volatile int terminate = 0;
+static volatile int xmitpause = 0;
 static int cmdfd;
 
 struct sbc_frame_header sbc_info;
@@ -102,6 +103,16 @@ static void sig_term(int sig)
 	terminate = 1;
 }
 
+static void sig_alrm(int sig)
+{
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags = SA_NOCLDSTOP;
+	sa.sa_handler = sig_alrm;
+	sigaction(SIGALRM, &sa, NULL);
+	xmitpause = 0;
+}
+
 // Usage
 static void usage()
 {
@@ -404,6 +415,8 @@ s_config was set from sbc_info before:
 
 int main(int argc, char *argv[])
 {
+	int timerset = 0;
+	struct itimerval itimer;
 	struct sigaction sa;
 	struct timeval start, end;
 	int streamfd;
@@ -619,6 +632,8 @@ int main(int argc, char *argv[])
 
 	timestamp = 0;
 
+	sig_alrm(0);
+
 	while (!terminate) {
 
 		// a2dp headers: avdtp p.45
@@ -698,8 +713,18 @@ int main(int argc, char *argv[])
 
 			memcpy(buf + sizeof(packet_header), &payload_header, sizeof(payload_header));
 			write(streamfd, buf, psize);
-			//printf("\nSending packet:%d, size %d no. of sbc frames:%d", seq_num, psize, payload_header.frame_count);
 
+			if(!timerset) {
+				timerset = 1;
+				itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
+				itimer.it_interval.tv_usec = itimer.it_value.tv_usec = (sleeptime*95)/100;
+				if(setitimer(ITIMER_REAL, &itimer, NULL)) printf("couldn't setitimer\n");
+			}
+
+			while(xmitpause) usleep(10);
+			xmitpause = 1;
+#if 0
+			//printf("\nSending packet:%d, size %d no. of sbc frames:%d", seq_num, psize, payload_header.frame_count);
 			pending++;
 
 			if (pending > 4) {

a2play.c int main(int argc, char *argv[]) · line 748

Quoted 8 lines
a2play.c +1 -1
@@ -717,7 +742,7 @@ int main(int argc, char *argv[])
 				sleeptime = 0;
 				pending = 0;
 			}
-
+#endif
 			/* END: NONSPECAUDIO == FALSE */
 		}
 

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel

#8 lore raw

guys,

I wrote up basic itimer/ring buffer support in a2play. It still needed some tuning (eg it uses the frame timespan * .87 to set the itimer)

I really just think the HP headset is poorly engineered. Either you are going to send frames too slowly and get a noisy glitch when it hits a dry patch or you are going to send them too quickly and have the occasional pop when it drops a frame. There's no way around it.

I did experiment with ring buffer size and SA_NODEFER option on the signal handler. I was trying to see if one write to the headset could be interrupted by the alarm so that the next frame could get on its way, like if you had a poor connection and wanted to keep the timing more accurate. I finally decided this wasn't the way... a long ring buffer and nodefer enabled meant really fast-playing catch-up periods after the connection was good again.

Brad

Quoted 1 line · Brad Midgley

I think my hack introduced timing issues of its own... tomorrow I'll fix it up with code that uses a ring buffer and bleeds from the ring in the sighandler.

-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel