Bug 42804 - raop module does not work with shairport
Summary: raop module does not work with shairport
Status: RESOLVED FIXED
Alias: None
Product: PulseAudio
Classification: Unclassified
Component: modules (show other bugs)
Version: unspecified
Hardware: All All
: medium major
Assignee: pulseaudio-bugs
QA Contact: pulseaudio-bugs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-11-10 15:31 UTC by Daniel Svensson
Modified: 2017-01-18 23:58 UTC (History)
16 users (show)

See Also:
i915 platform:
i915 features:


Attachments
Handshake between shairport and iPhone (3.53 KB, text/plain)
2013-02-17 19:31 UTC, Rune K. Svendsen
Details
Patch to raop_client.c (2.39 KB, patch)
2013-08-03 11:59 UTC, Matthias
Details | Splinter Review
Patch to RAOP module to use LIBSALAC (9.48 KB, text/plain)
2013-08-23 10:38 UTC, Matthias
Details
Patch to Hajime's RAOP module to use LIBSALAC (6.55 KB, patch)
2013-09-01 13:25 UTC, Matthias
Details | Splinter Review
Packet retransmission buffer addition to Hajime's v4.0+raop branch (13.84 KB, patch)
2013-09-03 21:39 UTC, Matthias
Details | Splinter Review
Sample PCAP snippet of two retransmissions (100.22 KB, application/vnd.tcpdump.pcap)
2013-09-03 21:40 UTC, Matthias
Details
A patch to Matthias's packet retransmission patch (85150) (2.63 KB, patch)
2013-09-04 05:45 UTC, Hajime Fujita
Details | Splinter Review

Description Daniel Svensson 2011-11-10 15:31:13 UTC
raop module does not work with shairport, which is kind of annoying as it's much nicer to use than having to have a dedicated airport hardware which does nothing but ship music.

During some investigation I found the following difference in the SETUP phase:

pulseaudio sends:
RTP/AVP/TCP;unicast;interleaved=0-1;mode=record

my iphone sends:
RTP/AVP/UDP;unicast;mode=record;timing_port=59159;events;control_port=56536

The timing_port and control_port are needed for shairport to work.

Shairport can be found at:
https://github.com/albertz/shairport

and works under both OS X and Linux.
Comment 1 Daniel Svensson 2011-11-10 15:33:38 UTC
Another interesting observation is that mplayer gets the following format when trying to play over RAOP:

AO: [pulse] 22050Hz 1ch floatle (4 bytes per sample)
Comment 2 Daniel Svensson 2011-11-13 08:40:59 UTC
pulseaudio does not work with shairport as shairport only supports RAOP over UDP and not TCP which PulseAudio uses. But PulseAudio should probably move over to UDP as well, as the rest of the RAOP-world seems to use that (iOS-devices, AirFoil etc).
Comment 3 Martin-Éric Racine 2012-06-18 23:02:23 UTC
Has there been any progress on this issue? Perhaps some of the Shairport code could be leveraged to implement the same functionality as a PulseAudio module?
Comment 4 Felix Möller 2012-07-15 16:08:47 UTC
(In reply to comment #3)
> Has there been any progress on this issue? Perhaps some of the Shairport code
> could be leveraged to implement the same functionality as a PulseAudio module?
This is tracked at https://github.com/albertz/shairport/issues/47
Comment 5 Albert Zeyer 2012-07-16 06:09:43 UTC
The same issue was also reported here:
https://github.com/albertz/shairport/issues/100

I also tried to report it upstream:
http://sourceforge.net/tracker/?func=detail&aid=3438902&group_id=119473&atid=684236

But I'm not sure if that is really the official upstream bug tracker. I didn't got any response.
Comment 6 Felix Möller 2012-07-16 08:04:33 UTC
Looking at the git log of roap-play looks quiet good, the last commit http://raop-play.git.sourceforge.net/git/gitweb.cgi?p=raop-play/raop-play;a=commit;h=c394ae1139d90b23c2af87fbbfc58d01e480db7d is named "Initial UDP commit".

Can anyone test it? I do not own a shairport nor airport express device just, I am just evaluating Linux support to buy it later ...
Comment 7 Rune K. Svendsen 2013-02-02 23:53:18 UTC
I've succeeded in building the UDP version of raop-play. First I cloned from this repo:

git://raop-play.git.sourceforge.net/gitroot/raop-play/raop-play

Then I had to make the following change to the Makefile in raop_UDP to get it to build:

 raop_play: $(OBJS)
-       $(CC) -o $@  -lssl -lsamplerate -lid3tag -lpthread $^
+       $(CC) -o $@ $^ -lcrypto -lsamplerate -lid3tag -lpthread

It's not working with shairport though :/

This is the output of shairport when trying to play the 16 bit sample "ALAC CD quality test" from here: http://www.linnrecords.com/linn-downloads-testfiles.aspx

Listening...
Established under name 'CD531B0FDBAD@ShairPort 2252 on raspberrypi'
New connection from ::ffff:192.168.1.34
REQ: OPTIONS
REQ: ANNOUNCE
Audio StreamoAEeTgG38KP1CDspMrmbvwREQ: SETUP
launched decoder: 2257 on port: 6000
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
REQ: RECORD
REQ: SET_PARAMETER
REQ: SET_PARAMETER
Use of uninitialized value $1 in lc at /usr/local/bin/shairport.pl line 704.
Use of uninitialized value $1 in lc at /usr/local/bin/shairport.pl line 704.
hairtunes: hairtunes.c:363: alac_decode: Assertion `outsize == (4*frame_size)' failed.
Aborted
Child exited

And here's the output of raop_play:

DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 1
DBG: Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER
DBG: Apple-Response: YS0IgunAPZc/j5D3uTF18kLHY5vtiEvwE/8i6JA+VEWeOOdXeLaps7DOPcW+oX2eh75hfN83WJ02pmtTtWsDhCsUlEQs3FXtzoerd6JWApIDyCloIJ9TCStGO1cHHZ9vhtUNnkJ0L1EEP8bJSyHXcPIhMfFhUneqbPaUH8+KZFrKEb9AwV4pp5qPthjb9+6U/xZ1iomBCpukCWxlcuRRXbsJDEW31dqIhEVmeXY64xmRBbchNnCgGAsJIvASGWL+RxN2Y/Kbi3v/4kAvwWpNO4n7cEa/Hrv+FLdk9yklU8QJ7o6Xm5HGnMWOj7IfDngyNdvGHj/KY+8Kz4QaG+OilQ
DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 2
DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 3
DBG: Session: DEADBEEF
DBG: Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=6001;timing_port=6002;server_port=6000
DBG: rtspcl_setup: session: DEADBEEF
DBG: port = 6001
DBG: port = 6002
����"����`DBG: port = 0
DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 4
DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 5
DBG: exec_request: write 291: 291 
DBG: Audio-Jack-Status: connected; type=analog
DBG: CSeq: 6
connected
done

As you can see the third port DBG message is garbled. Not sure what that is about. Could it be memory corruption?
Comment 8 Rune K. Svendsen 2013-02-03 00:47:47 UTC
Here's the entire conversion on the wire between the two apps (for reference). Captured with Wireshark and selected "Follow TCP Stream".

OPTIONS * RTSP/1.0
CSeq: 1
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e

RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 1
Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER

ANNOUNCE rtsp://192.168.1.36/34202083 RTSP/1.0
Content-Type: application/sdp
Content-Length: 568
CSeq: 2
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e
Apple-Challenge: 4PlZFs4jfEaDJx3HoGRB7A

v=0
o=iTunes 34202083 0 IN IP4 192.168.1.36
s=iTunes
c=IN IP4 192.168.1.34
t=0 0
m=audio 0 RTP/AVP 96
a=rtpmap:96 AppleLossless
a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100
a=rsaaeskey:0WOvElSnPb5oykQpxo40veLtm7vZ/zgoYbjQxLKXY/usCGrvc/7crCjadPh9Ri8cdyDQhzzQ9BkUXhua2555Ce6/8/zVdc+SGg1wJCfi8g5ZKmdyWp5Q0A7KFdnonIrJxJQ+3lbQ4PyD3GWuEHPck25kI1xmA+1NJTybPzdbs0BPFRMx1lwZOKa0UnBKutzw6lQb3S4AQqdTsVxO8+vH0WFpIHJsnm6V2tBk+j1SqkySICvcCaNuYCrRUse0q3DOq1usM4dze6aJ+ztkN1JeoZqMhfb8ws7HLs/3QTCzuG2c607jxF9u9zSxvgDLcFKRj8oVWmqoT0/2d/iEDBP1QQ
a=aesiv:oAEeTgG38KP1CDspMrmbvw
RTSP/1.0 200 OK
Apple-Response: YS0IgunAPZc/j5D3uTF18kLHY5vtiEvwE/8i6JA+VEWeOOdXeLaps7DOPcW+oX2eh75hfN83WJ02pmtTtWsDhCsUlEQs3FXtzoerd6JWApIDyCloIJ9TCStGO1cHHZ9vhtUNnkJ0L1EEP8bJSyHXcPIhMfFhUneqbPaUH8+KZFrKEb9AwV4pp5qPthjb9+6U/xZ1iomBCpukCWxlcuRRXbsJDEW31dqIhEVmeXY64xmRBbchNnCgGAsJIvASGWL+RxN2Y/Kbi3v/4kAvwWpNO4n7cEa/Hrv+FLdk9yklU8QJ7o6Xm5HGnMWOj7IfDngyNdvGHj/KY+8Kz4QaG+OilQ
Audio-Jack-Status: connected; type=analog
CSeq: 2

SETUP rtsp://192.168.1.36/34202083 RTSP/1.0
Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=6001;timing_port=6002
CSeq: 3
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e

RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 3
Session: DEADBEEF
Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=6001;timing_port=6002;server_port=6000

RECORD rtsp://192.168.1.36/34202083 RTSP/1.0
Range: npt=0-
RTP-Info: seq=0;rtptime=0
CSeq: 4
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e
Session: DEADBEEF

RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 4

SET_PARAMETER rtsp://192.168.1.36/34202083 RTSP/1.0
Content-Type: text/parameters
Content-Length: 18
CSeq: 5
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e
Session: ݼDBEEF

volume: 0.000000
RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 5

SET_PARAMETER rtsp://192.168.1.36/34202083 RTSP/1.0
RTP-Info: rtptime=0
Content-Type: application/x-dmap-tagged
Content-Length: 58
CSeq: 6
User-Agent: iTunes/7.6.2 (Windows; N;)
Client-Instance: 8a8cb157a7a1102e
Session: 5

mlit\00\00\002minm\00\00\00
ROAP PLAY\00asar\00\00\00Via BoBo's NAS\00asal\00\00\00\00RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 6


Again there seems to be some corruption of the Session parameter in the first SET_PARAMETER message.
Comment 9 Fernando Herrera 2013-02-17 18:38:57 UTC
The garbage you are seeing in raop_play from git master is causes by a wrong initialization secuence. The current code does:

        raopcl_connectcontrol(raopcld, 6001);
        raopcl_connecttime(raopcld, 6002);

but raopcl_connectcontrol starts a thread (raopcl_sync) which tries to write to the time server socket (raopcld->tfd) which is only initialized after reaopcl_connecttime, so it ends up writing to 0 (standard output). That is why you see those packets on the console.

Changing the order to:

        raopcl_connecttime(raopcld, 6002);
        raopcl_connectcontrol(raopcld, 6001);

should fix that problem. 

But even fixing this minor problem, it is not working :)
Comment 10 Rune K. Svendsen 2013-02-17 19:31:35 UTC
Created attachment 75000 [details]
Handshake between shairport and iPhone

Here's a handshake I recorded between shairport and an iPhone
Comment 11 Fernando Herrera 2013-02-17 20:25:14 UTC
First, the shairport uninitialized value error can be skipped just not sending the application/x-dmap-tagged parameter. Comment the line:

if(raopcl_set_content(raopld->raopcl, "ROAP PLAY", "Via BoBo's NAS", "")) goto erexit;

anyway sharipoint.pl is not handling that data.

Looking at your output it seems that raop_play is not decoding anything (just a "done" without any external decoder output). Are you trying to send an mp3 without having mpg321 installed? Yes, raop_play is bad reporting errors.

After this you should get the client sending the stream. 

Next problem is that either raop_play is sending the stream badly or hairtunes is playing it badly. I get this:


hairtunes: hairtunes.c:363: alac_decode: Assertion `outsize == (4*frame_size)' failed.

Commenting that assertion on shairport plays audio on the server side, however audio quality is bad, shairpoint complaints about "overrun." and raop_play about "ERR: raopcl_sync: read error: Connection refused".

Anyway I think this bug should be only about a pulseaudio raop udp implementation and we should move the discussion about raop_play+shairpoint somewhere else :)
Comment 12 Martin 2013-02-20 13:13:44 UTC
Are there any news on this? Maybe someone else has a sucessful test now?
Sadly there are no more changes since the initial commit of raop_UDP until now.

Beside "shareplay", also "airfoil speakers" is a listener, implemented in UDP.
Would be a great improvement if raop_UDP is working.
Comment 13 Martin 2013-02-25 12:05:06 UTC
> Commenting that assertion on shairport plays audio on the server side,
> however audio quality is bad, shairpoint complaints about "overrun." and
> raop_play about "ERR: raopcl_sync: read error: Connection refused".

Sadly, I got a similar error in shairtunes.
I can hear the audio but it's bad stuttering!

Sorry for the bad formatting, I have no better here at the moment:

accepted connection from 127.0.0.1
started client thread
processing requests from 127.0.0.1
request line: OPTIONS * RTSP/1.0
request line: CSeq: 1
request line: User-Agent: iTunes/7.6.2 (Windows; N;)
request line: Client-Instance: c831207aade1749d
request line: REQUEST BODY
== REQUEST REPLY ==
RTSP/1.0 200 OK
CSeq: 1
Audio-Jack-Status: connected; type=analog
Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEAR
DOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER
request line: ANNOUNCE rtsp://127.0.0.1/342061326 R TSP/1.0
request line: Content-Type: application/sdp
request line: Content-Length: 563
request line: CSeq: 2
request line: User-Agent: iTunes/7.6.2 (Windows; N;
)
request line: Client-Instance: c831207aade1749d
request line: Apple-Challenge: /CFiZhd44jo1zUlH0IFr
2Q
request line:

REQUEST BODY
v=0
o=iTunes 342061326 0 IN IP4 127.0.0.1
s=iTunes
c=IN IP4 127.0.0.1
t=0 0
m=audio 0 RTP/AVP 96
a=rtpmap:96 AppleLossless
a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100
a=rsaaeskey:uXBBZ4fFX1uXXUKzN0AQcVLmkg2Jx2CQJ2MHv9T
l5CQEW6uiKoiTNP9Sc3NfhkRYdJ42e7MEz7C3E2+vRPM6mkBb9YBFadh/CTB4tz97eas/CTyAZbpZ5tB
CkWQxS6l5FTPvrf936HAuDvhnRhAF9PFJ7zo1JIip9bVKPB3SnS1i0QpTTh8lgbQ++g/tKZWCBjOK/+V
kPXAYN7qdLzr9MotIONfAtR6TAh5z9/qCiy6qAHKqqisxr62WhCCC/sAxYdSFCeHzXzJLarsX99qbMHQ
OAhTVHQ6of2h1sXLH+9P1wdI/Qrm4y2bnEf+U+9LdOUzIbFsOmaWE0xhR8/b8oA
a=aesiv:lph8AXLMsF6a6oZcwGcX2Q

== REQUEST REPLY ==
RTSP/1.0 200 OK
Apple-Response: sSiNPprm1qhxRI6Nkkj/sRUiPb3Xv5n7k1Q
HjCxkFyT/24CcLPrVS3oBrZKCNSG0/ZeHrfz8wpp+OXW5UIHbSspewv6aRSj/vvkYUkfQ6NAWBLmiRh0
Q2ss4Dm6+WLdLxeNq/GU9pj3NhI9DjcOoR8UTnOy93oQyCKmc5mpa5+k7dn9w8ihq2jOxWuD7TDCdoIi
ceMKDYoC67YLIKepXJXPsRb1dS/helbVST5g6Je4lBdaqjVL7Se9pEVz/Gkp25WUB50OzL7zDJTYDzpb
JCX5/to+/LUofQAb+3/GfZSThHNz2Kr+8eIG77BGf1bUUZRFmC74Tnlj68CLLOmnP/w
CSeq: 2
Audio-Jack-Status: connected; type=analog

request line: SETUP rtsp://127.0.0.1/342061326 RTSP
/1.0
request line: Transport: RTP/AVP/UDP;unicast;interl
eaved=0-1;mode=record;control_port=6001;timing_port=6002
request line: CSeq: 3
request line: User-Agent: iTunes/7.6.2 (Windows; N;
)
request line: Client-Instance: c831207aade1749d
request line:
REQUEST BODY
controlPort: 6001 serverPort: 6000 timingPort: 6002

I/Decoder (31021): decodeInit: enter
W/Decoder (31021): start fill: 50
I/Decoder (31021): decodeInit: exit
I/Decoder (31021): started RTP thread
I/AndroidDecoder(31021): decoder task thread started
D/dalvikvm(31021): GC_FOR_ALLOC freed 299K, 6% free 9254K/9768K, paused 21ms, to
tal 21ms
I/dalvikvm-heap(31021): Grow heap (frag case) to 9.229MB for 176416-byte allocat
ion
D/dalvikvm(31021): GC_FOR_ALLOC freed 0K, 6% free 9426K/9944K, paused 19ms, tota
l 19ms

== REQUEST REPLY ==
RTSP/1.0 200 OK
Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode
=record;control_port=6001;timing_port=6002;server_port=6000
Session: Blargh
CSeq: 3
Audio-Jack-Status: connected; type=analog

I/AudioService(  391):  AudioFocus  requestAudioFocus() from android.media.Audio
Manager@41a9d660com.bubblesoft.android.utils.a@41a77ab8
I/AndroidDecoder(31021): created AudioTrack buffer size: 100ms (17640 bytes)
I/Decoder (31021): flush
I/Decoder (31021): flush enter
I/Decoder (31021): flush resync enter
I/Decoder (31021): flush resync exit
I/Decoder (31021): flush exit
E/raop_play(31068): bind error: Address already in use
request line: RECORD rtsp://127.0.0.1/342061326 RTS
P/1.0
request line: Range: npt=0-
request line: RTP-Info: seq=0;rtptime=0
request line: CSeq: 4
request line: User-Agent: iTunes/7.6.2 (Windows; N;
)
request line: Client-Instance: c831207aade1749d
request line: Session: Blargh
request line:
REQUEST BODY

== REQUEST REPLY ==
RTSP/1.0 200 OK
CSeq: 4
Audio-Jack-Status: connected; type=analog

request line: SET_PARAMETER rtsp://127.0.0.1/342061
326 RTSP/1.0
request line: Content-Type: text/parameters
request line: Content-Length: 18
request line: CSeq: 5
request line: User-Agent: iTunes/7.6.2 (Windows; N;
)
request line: Client-Instance: c831207aade1749d
request line: Session: Blargh
request line:
REQUEST BODY
volume: 0.000000
I/Decoder (31021): set volume
set volume: 0.0

== REQUEST REPLY ==
RTSP/1.0 200 OK
CSeq: 5
Audio-Jack-Status: connected; type=analog

I/ActivityManager(  391): START u0 {act=android.intent.action.MAIN cmp=com.bubbl
esoft.android.airbubble/.SettingsActivity} from pid 31021
D/dalvikvm(  391): GC_FOR_ALLOC freed 1004K, 21% free 23908K/30240K, paused 71ms
, total 73ms
D/dalvikvm(31021): GC_CONCURRENT freed 281K, 5% free 9545K/9944K, paused 4ms+3ms
, total 35ms
I/ActivityManager(  391): Displayed com.bubblesoft.android.airbubble/.SettingsAc
tivity: +224ms
request line: SET_PARAMETER rtsp://127.0.0.1/342061
326 RTSP/1.0
request line: RTP-Info: rtptime=0
request line: Content-Type: application/x-dmap-tagg
ed
request line: Content-Length: 58
request line: CSeq: 6
request line: User-Agent: iTunes/7.6.2 (Windows; N;
)
request line: Client-Instance: c831207aade1749d
request line: Session: Blargh
request line:
REQUEST BODY
mlit+Ç+Ç+Ç2minm+Ç+Ç+Ç
ROAP PLAY+Çasar+Ç+Ç+ǤVia BoBo's NAS+Çasal+Ç+Ç+Ç?+Ç

ignoring SET_PARAMETER with Content-Type applicatio
n/x-dmap-tagged

== REQUEST REPLY ==
RTSP/1.0 200 OK
CSeq: 6
Audio-Jack-Status: connected; type=analog

W/AudioTrack(31021): releaseBuffer() track 0x5df6ab90 name=0 disabled, restartin
g
I/Decoder (31021): buffer_get_frame: overrun
Comment 14 Martin Blanchard 2013-04-11 12:49:18 UTC
I got raop_play to work with shairport (running raop_play via CLI, don't know if the entire app is working) through a Wifi network. I posted a small patch in the SourceForge traker a while ago :
http://sourceforge.net/tracker/?func=detail&aid=3577877&group_id=119473&atid=684238
More or less the same fix as Fernando Herrera (comment #9) proposed btw, but different solution.

I also started to implement the UDP stuffs in a pulseaudio sink, based on Christophe Fergeau initial work :
http://git.zx2c4.com/pulseaudio-raop2/ (Christophe code)
http://repo.or.cz/w/pulseaudio-raopUDP.git/shortlog/refs/heads/raop (Mine)

I'm not working on it anymore : as far as I remember, I was stuck because of a stupid UDP socket connection problem...
Comment 15 Fix raop streaming 2013-06-04 18:09:31 UTC
Pulseaudio 4.0 just came out. Has this been fixed? This bug has been open for ONE YEAR, SIX MONTHS, AND 24 DAYS. I find it exhaustingly embarrassing that this major project can't support a simple UDP stream. 

XBMC just added airplay support. But guess what. It is completely useless until this bug is closed. You'd think the most popular music device of all time would be able to send a stream to Pulse Audio.

NOPE. I will check in periodically on this bug. It needs attention. UDP was only created in 1980, not a very standard protocol right?
Comment 16 Tanu Kaskinen 2013-06-05 08:11:12 UTC
(In reply to comment #15)
> Pulseaudio 4.0 just came out. Has this been fixed? This bug has been open
> for ONE YEAR, SIX MONTHS, AND 24 DAYS. I find it exhaustingly embarrassing
> that this major project can't support a simple UDP stream. 
> 
> XBMC just added airplay support. But guess what. It is completely useless
> until this bug is closed. You'd think the most popular music device of all
> time would be able to send a stream to Pulse Audio.
> 
> NOPE. I will check in periodically on this bug. It needs attention. UDP was
> only created in 1980, not a very standard protocol right?

I can't talk for other maintainers, but I have zero interest in spending time trying to implement a proprietary protocol. I don't have any AirPlay devices, and I won't buy them either.

I'm happy to receive patches for this bug, though.
Comment 17 Adam 2013-07-06 04:47:06 UTC
I would actually like to see this functionality added as well. There are many reports across the web (reddit, arch, fedora, raspberry pi) where this is causing issues. 

I'm willing to put up a reward for getting this done. The first developer to get Airplay working with iOS 6 _and_ 7 gets $200 to the paypal address of their choice.

But it must work for iOS 6 and the upcoming 7 on PulseAudio version 3.x and 4.x.
Comment 18 Matthias 2013-08-03 11:59:25 UTC
Created attachment 83577 [details] [review]
Patch to raop_client.c
Comment 19 Matthias 2013-08-03 12:01:36 UTC
(In reply to comment #14)
> I got raop_play to work with shairport (running raop_play via CLI, don't
> know if the entire app is working) through a Wifi network. I posted a small
> patch in the SourceForge traker a while ago :
> http://sourceforge.net/tracker/
> ?func=detail&aid=3577877&group_id=119473&atid=684238
> More or less the same fix as Fernando Herrera (comment #9) proposed btw, but
> different solution.
> 
> I also started to implement the UDP stuffs in a pulseaudio sink, based on
> Christophe Fergeau initial work :
> http://git.zx2c4.com/pulseaudio-raop2/ (Christophe code)
> http://repo.or.cz/w/pulseaudio-raopUDP.git/shortlog/refs/heads/raop (Mine)
> 
> I'm not working on it anymore : as far as I remember, I was stuck because of
> a stupid UDP socket connection problem...

In the first place, please accept my apologies for posting this here - I did not find a better way to contact you.

Hi Martin,

thank you for your work on http://repo.or.cz/w/pulseaudio-raopUDP.git/shortlog/refs/heads/raop. I followed this bug report for about two weeks and started messing around with your version of Christophe's code.

I also ran into the UDP socket problem: pulseaudio would connect to my Cambridge Audio Minx Air 200 (further on: remote device) just fine and starting to RECORD. However, as soon as the remote device sends timing packets it would end up in an infinite loop as pulseaudio would not react on the packets sent by the remote device. Tcpdump showed packets coming from $REMOTE_TIMING_PORT to local ip on $DEFAULT_TIMING_PORT and IGMP port unavaible messages bouncing back.

Digging through man pages of bind() and connect() I finally found the bug in raop_client.c. In rtsp_cb(), lines 667 and 668 open_udp_socket() is called with c->control_port and c->timing_port respectively. open_udp_socket() binds the socket with port c->control_port or c->timing_port (read from MDNS) - however, c->timing_port is the _sending_ port on the remote device. Local timing_port should be DEFAULT_TIMING_PORT. I renamed the parameter should_bind to local_port in the definition of open_udp_socket() and used this to bind().

With this modification, the timing packets are received correctly, timing responses are sent and my host starts sending audio packets. There are some errors regarding "invalid control packets" which i plan to have a look at later on.

I attached a patch with my changes in raop_client.c. I had to reconstruct it from my sources using vi as my IDE (Eclipse CDT) seems to have changed every single line in the file...

Please forgive any errors in the patch as this is my first time programming in C - I code ABAP for a living. Besides that, I am happy to help out with testing and will report back if I see any progress.

At last, could any of you tell me how to get shairplay (or any other software) to work (to the point mentioned above) with pulseaudio? pulseaudio does not seem to find the MDNS entry of my local copy of shairplay. avahi_discover reports a similar entry as that by my remote device; however, it just does not show up in the list of available devices in the pulseaudio device list.
Comment 20 Adam 2013-08-07 18:23:35 UTC
Glad to see some progress on this, keep up the good work ladies and gents!
Comment 21 Hajime Fujita 2013-08-08 04:26:15 UTC
@Martin and Matthias,

First, thank you guys (and Christophe, of course!) for your interest and hard work on RAOP2. I realized the same UDP port issue which Matthias described, and wrote a patch independently.

https://github.com/hfujita/pulseaudio-raop2/commit/bcec418665ae3650471acc59b9b849cc55646843

In my patch it will first try to bind the local port, since some of the default port might already be used by other applications, then announce that port to the server (AirPlay device).

I'm actually not working on shairport (working on my AV receiver), but I think we can share most of the findings.
Comment 22 Matthias 2013-08-08 04:57:20 UTC
(In reply to comment #21)
> @Martin and Matthias,
> 
> First, thank you guys (and Christophe, of course!) for your interest and
> hard work on RAOP2. I realized the same UDP port issue which Matthias
> described, and wrote a patch independently.
> 
> https://github.com/hfujita/pulseaudio-raop2/commit/
> bcec418665ae3650471acc59b9b849cc55646843
> 
> In my patch it will first try to bind the local port, since some of the
> default port might already be used by other applications, then announce that
> port to the server (AirPlay device).
> 
> I'm actually not working on shairport (working on my AV receiver), but I
> think we can share most of the findings.

Hajime, I just found a bug in someone elses code and patched it - thanks should go to Martin and Christophe for implementing UDP in the raop sink.

Btw: My patched version of Martin's code works with shairplay (in the sense of "it plays a song from my host remotely") but does not work with my Minx Air 200. I also have a Pioneer VSX-922 to test with. I will try that device next.

I also tried node_airtunes by Laurent Perrin (https://github.com/radioline/node_airtunes) and it worked out of the box with my Minx Air (did not test with shairplay yet). Its implemented in Javascript and C++ using node.js and seems pretty mature. It uses ALAC to encode audio frames (whereas raop_udp  just adds an ALAC header and sends PCM frames at the moment).

I compared the RTP/RTSP/RTCP streams of both implementations (to my Minx Air 200) and found the following differences which I plan to have a deeper look on:

* raop_udp (as mentioned before) sends PCM frames with an ALAC header and encrypts these with an AES key. If I read http://git.zx2c4.com/Airtunes2/about/#streaming-audio-to-an-airtunes-2-compatible-server correctly, one should either send PCM frames (with an ALAC header?) unencrypted or encode the frames with ALAC and optionally encrypt them (as does node_airtunes).

* Due to that (as I suspect at the moment) packets sent by node_airtunes are much smaller (86 bytes) than those sent by raop_udp (~ 1470 bytes). As this is close to the network MTU (at least in my setup) this _could_ be the reason why my remote device does not play songs remotely due to network issues. UDP is connectionless in the end. My remote shairplay box is connected to my WIFI bridge via cabled ethernet, whereas the Minx Air 200 is connected via WIFI. As the Minx also has a RJ-45 connector, I will try to connect it using an ethernet cable and test again.

My next steps after testing the cabled setup would be:

1. Remove the ALAC header from the frames sent by raop_udp.
2. Use Apple's free implementation of ALAC to encode frames with ALAC before sending if 1. does not work.

I will report back as soon as I found some time to work on this.
Comment 23 Hajime Fujita 2013-08-10 02:35:55 UTC
Hi Matthias,

I'm afraid that this could be out of the topic from this issue, but I have Pioneer VSX-43 and it does not play music with my latest patch (in comment #21). If I try to stream music from PulseAudio, the reciver turns into the AirPlay streaming mode, so the setup phase seems to be woking. I also tried to use node-airtunes before but for some reason it did not work (node-airtunes itself did not launch) in my environment.

I'm looking forward to hearing about your further experiments. Using true ALAC encoding sounds promissing.
Comment 24 Matthias 2013-08-23 10:37:57 UTC
Hi all,

I'm in a hurry as I'm leaving for the holidays tonight.

Just wanted to share my work of the last weeks. I tried to set up a git fork of http://repo.or.cz/w/pulseaudio-raopUDP.git/shortlog/refs/heads/raop at http://repo.or.cz/w/pulseaudio-raopUDP/pulseaudio-raop-alac.git but could not commit my changes. Don't ask why - it's also my first time working with git.

Will try again after my vacation.

Until then, this is the state of play:

1. I wrote a simple wrapper library for Apple's C++ ALAC library. You can fetch it from http://repo.or.cz/w/libsalac.git. 
It needs libalac from https://github.com/TimothyGu/alac.
After the usual

%autoreconf -i -a && ./configure && make && make install

copy "libsalac.h" from "/usr/local/include/libsalac-1.0/src" to /usr/local/include/libsalac-1.0".

2. I modified my copy of pulseaudio (see the attached patch) to use libsalac (configure.ac and Makefile.am).

3. I used libsalac to use Apple's ALAC Encoder to encode audio packets with ALAC. Careful: I set the initial volume to "-21" in the source. This is obviously a workaround as the initial volume was not set correctly and audio was played near mute.

4. As a result, I am now able to play music via pulseaudio to my Minx Air 200. There are glitches every second (I suspect the sync packets are not quite right).

Please give it a try.

I also started to implement a packet buffer for resending. I will commit that when it reaches a stable state.
Comment 25 Matthias 2013-08-23 10:38:47 UTC
Created attachment 84512 [details]
Patch to RAOP module to use LIBSALAC
Comment 26 Hajime Fujita 2013-08-25 23:50:48 UTC
Thanks Matthius,
Unfortunately it seems that your patch is for some irrelevant files. Looking forward to hearing from you again after you come back from vacation.

Here are several outcomes of my research these days.

First, I noticed that there were several encryption methods defined in the protocol[1]. Martin's code only supports RSA and always does encryption. This may work for Apple devices, but my VSX-43 for example only supports et=0 (no encryption) and et=4 (MFiSAP). This was the reason that I could not hear any sound from my device.

I wrote a patch for automatically switching RSA encryption, depending on device's capability announce. (before that I upgraded the code base to v4.0)
https://github.com/hfujita/pulseaudio-raop2

I also embed ALAC encoder to raop module (which I haven't made public yet -- since I'm not 100% sure about license conditions). Actually, VSX-43 played sounds either with or without ALAC encoding.

[1] http://nto.github.io/AirPlay.html#servicediscovery-airtunesservice

So Matthuis, your Minx Air 200 required ALAC compression, (i.e. it did not accept uncompressed PCM+ALAC header) right?

I'm now having an issue that the sound skips every second, propably the same issue as you have. I'm now trying to understand how the time is managed inside PulseAudio and figure out how to fix it.
Comment 27 Hajime Fujita 2013-08-27 05:04:17 UTC
I fixed the sleep timing calculation and latency calculation, and mostly resolved sound glitch issue (at least in VSX-43).

I wrote some instruction about how to use my tree. Please give it a try. Any reports are welcome.
http://hfujita.github.io/pulseaudio-raop2/
Comment 28 Hajime Fujita 2013-08-28 04:23:25 UTC
I've been testing my code using VLC, and it worked reasonably well so far.
Today I tested Rhythmbox and found that the sound quality was terrible. (Very rough sound). This difference is observed even for the same mp4 file.
I have so far no clue about what's going on, but will continue to investigate this issue.
Comment 29 Karl Stavestrand 2013-09-01 10:38:24 UTC
hi Hajime Fujita

tested your raop2 on shairport (https://github.com/abrasive/shairport). Can confirm that it works 100% Playing music with spotify on ubuntu 13.04. 

Great work, I have been waiting almost a year for this!
Comment 30 Matthias 2013-09-01 13:24:11 UTC
(In reply to comment #28)
> I've been testing my code using VLC, and it worked reasonably well so far.
> Today I tested Rhythmbox and found that the sound quality was terrible.
> (Very rough sound). This difference is observed even for the same mp4 file.
> I have so far no clue about what's going on, but will continue to
> investigate this issue.

Hi Hajime,

I'm back from my vacation - the first thing I did when I entered my home was to boot up my laptop and compile your version of pulseaudio :).

First of all, congratulations and thanks for the great job. Second: I also encountered your "rough sound" issue when playing music with 'banshee'. Reading your entry here I wondered what that expression meant, but I can't think of a better description. Sounds like my Minx Air turned into a 20$ bluetooth speark ;).

I used to test my patches with 'banshee' (as it's preinstalled with ubuntu 12.04) and 'gst123'. I also used 'banshee' to test your version of the raop module - which resulted in the "rough sound" described above.

Today I merged my work on ALAC encoding into your branch. The sound distortion (using banshee) is gone now. I confirmed that using my Minx Air using RSA encryption and without encryption (by commenting out encryption in raop_client.c).

That leaves me at a point where I don't want to be: This is not a programming, but a license issue. As pointed out in the ALAC trac [1], the license of the ALAC codec part is ambiguous (Apple Licence vs. Apache V2.0).

Though, as I already wrote: Timothy Gu created a debian package of Apple's (tm) ALAC library [2]. I used his library to create a simple encoder library [3].
My patch - as the time of writing it's incomplete and more a proof of concept - uses these libraries (read: libsalac which in turn uses libalac).

I'm not sure if this "library use" could affect the license of any software that uses libsalac or libalac. Any software license laywers here ;)?

So, back to the technical discussion: It sounds to me (pun intended) as if the "rough sound issue" can be resolved using "true" ALAC encoding - and in turn, that present "encoding" does not work correctly in some situations. I've got no idea why this should affect rhythmbox, gst123 and banshee but not vlc (wild guess: are rhythmbox, gst123 and banshee built with gstreamer support?).

I did a packet dump of some playback with your branch and libsalac. It shows series of retransmit requests. I am currently working on a packet retransmit buffer implementation.

Until then, please find attached a patch for libsalac to Hajime's branch.
Works for me with my Minx Air. As the license issue is still unresolved, this is just a proof-of-concept patch and should not be included in any software distribution whatsoever as I do not want to be hunted by Apple's (tm) laywers.

[1]: Bug #3 http://alac.macosforge.org/trac/ticket/3
[2]: Timothy Gu's LibALAC: https://github.com/TimothyGu/alac
[3]: LibSALAC: http://repo.or.cz/w/libsalac.git

Greetings,

Matthias
Comment 31 Matthias 2013-09-01 13:25:51 UTC
Created attachment 85009 [details] [review]
Patch to Hajime's RAOP module to use LIBSALAC
Comment 32 Adam 2013-09-01 19:13:47 UTC
I also just got back from vacation. Excited to see even more progress on this. I'm going to sit down with my Fedora 19 box and try and replicate Hajime's instructions here: http://hfujita.github.io/pulseaudio-raop2/

I will let you know how it goes with my hardware. I'm using the iPhone 5 on iOS 6.1.2 jailbreak to stream to my Fedora HTPC via HDMI audio.
Comment 33 Adam 2013-09-01 19:15:41 UTC
Forgot to mention that I will add/test Matthias' patches and work as well, depending upon which codebase he is working off of etc.
Comment 34 Hajime Fujita 2013-09-01 21:25:24 UTC
Hi Karl,

Thank you for reporting the result. I'll add shairport to the "confirmed device" list.
Comment 35 Hajime Fujita 2013-09-01 21:28:45 UTC
Hi Matthias,

Thank you so much for the testing and reporting.

By looking at your patch for ALAC support, I found out that the real issue might not be in the ALAC encoding, but in the raw buffer handling.
I wrote a one-line patch inspired by your patch.
https://github.com/hfujita/pulseaudio-raop2/commit/3b35e02e473873f64e2a5967f4cc55e05ae9d00a
This fixes the "rough sound" issue in my VSX-43+Ryhthmbox, even without Apple's codec integration.
I'm happy to hear any results from testing this patch.
(However, the sound from Adobe Flash is still terrible. I suppose this is due to a different cause.)

Regarding ALAC licensing, I actually had a similar concern. That's why I haven't made my ALAC integration branch public, while I already have it locally.

Since I'm not a law expert in any sense, the following should not be taken seriously :)
In the most optimistic way of thinking, header section in each ALAC source file states that the file is licensed under the Apache License, so I hope we can just believe it.
However, still I'm not sure if it's okay to link Apache Licensed library into PulseAudio. The license file in the pulseaudio top directory says that the server side implementation of PA should be treated as GPL-licensed. So the question would be "can we link Apache Licensed library to GPL product"?

One possible workaround would be to find another encoder implementation, such as in libav.
Comment 36 Matthias 2013-09-01 22:12:46 UTC
(In reply to comment #35)
> Hi Matthias,
> 
> Thank you so much for the testing and reporting.
> 
> By looking at your patch for ALAC support, I found out that the real issue
> might not be in the ALAC encoding, but in the raw buffer handling.
> I wrote a one-line patch inspired by your patch.
> https://github.com/hfujita/pulseaudio-raop2/commit/
> 3b35e02e473873f64e2a5967f4cc55e05ae9d00a
> This fixes the "rough sound" issue in my VSX-43+Ryhthmbox, even without
> Apple's codec integration.
> I'm happy to hear any results from testing this patch.
> (However, the sound from Adobe Flash is still terrible. I suppose this is
> due to a different cause.)
> 
> Regarding ALAC licensing, I actually had a similar concern. That's why I
> haven't made my ALAC integration branch public, while I already have it
> locally.
> 
> Since I'm not a law expert in any sense, the following should not be taken
> seriously :)
> In the most optimistic way of thinking, header section in each ALAC source
> file states that the file is licensed under the Apache License, so I hope we
> can just believe it.
> However, still I'm not sure if it's okay to link Apache Licensed library
> into PulseAudio. The license file in the pulseaudio top directory says that
> the server side implementation of PA should be treated as GPL-licensed. So
> the question would be "can we link Apache Licensed library to GPL product"?
> 
> One possible workaround would be to find another encoder implementation,
> such as in libav.

Hi Hajime,

I'm not a law expert either. To be honest, I hate to dig through compatibility descriptions of "free" software licenses instead of happily hacking a packet buffer while listening to music over Airtunes 2 (tm) by Apple (tm) (I'll add an extra (tm) here and there just to be sure ;)).

Just to clarify: I would be happy to release libSALAC under LGPLv3. PA would only link to libSALAC. Library use of a LGPL library should be ok for GPL code. libSALAC, in turn, could "library use" Apache V2.0 ALAC. But that's just my understanding - I'm not a software lawyer (and I don't want to be one).

You mentioned a "huge latency" in your project description on github [1]. If by "huge latency" you mean the interval of time between "playing a sound" and the _first time_ your airplay device plays that sound, I could think of a way to circumvent this:

1. Implement a circular packet buffer (I am about to do this).
2. Start filling the circular buffer when module_raop_sink is loaded (i. e. in module-raop-sink.c function thread_func, whether the sink is in a "running" state or not).
3. Send packets from the packet buffer using pa_raop_client_send_audio_packet (as in the current implementation).
4. As a side effect, one could easily implement packet resending by using the sequence information of the packets in the circular buffer (which in the end is just an array).

I also noticed this delay - looking at the packet dump of a sound clip played via airtunes it seems as if:

1. pa_sink_render_full (in module-raop-sink.c) is subsequently called after the sink is in running state, but
2. the playback starts *after the first three timing packets are exchanged* (after RTSP RECORD).

As a result, there is a gap (in matter of time) between the moment when pulseaudio moves the sink from your previously selected device to the airplay device and the point in time where the timing packet exchange is done.

If you listen to several sound clips (read: songs of a playlist) subsequently, the interval between the player announcing a new song and start of playback is minimal.

However, there will always be a delay between sound data relayed to PA and playback on the remote device.

Hajime, would you mind if I forked your project github to test my packet buffer?

Good night,

Matthias

[1]: pulseaudio-raop2 github: http://hfujita.github.io/pulseaudio-raop2/
Comment 37 Hajime Fujita 2013-09-01 22:45:58 UTC
Hi Matthias,

> If by "huge latency" you mean the interval of time between "playing a sound"
>  and the _first time_ your airplay device plays that sound,
> I could think of a way to circumvent this:

I meant for example,

1. launch a movie player and start playing any movie
2. the picture starts moving almost immediately but the sound track have to wait until the Airplay device is initialized (usually takes a few seconds)
3. Therefore there will be a huge gap between the picture and the sound track.

I'm not sure how the circular buffer would resolve this issue. (Buffer in general sounds like increasing the delay.)
I think the fundamental issue is that a client (movie/music player) thinks that the audio device (PulseAudio) is ready and starts playing the picture, while atcually it is not ready yet.

I guess there should be some way to tell the application that "we are not ready yet", or at least tell some latency. I think we need to understand more about PulseAudio internals.

> Hajime, would you mind if I forked your project github to test my packet buffer?

Of course not :) Anyone is welcome to fork from my tree.

However, please note that I'm now completely reformatting all patches (including Martin's) so that
* they could support both TCP and UDP protocols at the same time
* they could easily be merged into master

So in near future my current "v4.0+raop" branch will be revoked and I'll move to the new, clean branch.
However even in that case, I think I can take care of moving your additinal patches to the new branch if these patches are relatively small.
Comment 38 Matthias 2013-09-02 07:00:31 UTC
Hi Hajime,

> I think the fundamental issue is that a client (movie/music player) thinks
> that the audio device (PulseAudio) is ready and starts playing the picture,
> while atcually it is not ready yet.

That sounds reasonable.

> I guess there should be some way to tell the application that "we are not
> ready yet", or at least tell some latency. I think we need to understand
> more about PulseAudio internals.

I had a quick look through the developer documentation but did not find anything. However, if you look at module-raop-sink.c, function sink_process_msg, it looks to me as if the connect sequence is called when a PA_SINK_RUNNING state is set. Perhaps we could do the connect (and only this) when PA_SINK_INIT state is set.
Setting PA_SINK_RUNNING (in my understanding) is set when the sink is connected and should play audio - this is were audio playback (i.e. sending sync and audio packets) should happen.

Btw.: I just tested your latest patch (with the memchunk offset) and kindly ask you to put "Cambridge Audio Minx Air 200" on your supported device list. Works like a charm :).

Greetings,

Matthias
Comment 39 Karl Stavestrand 2013-09-02 18:20:57 UTC
> (However, the sound from Adobe Flash is still terrible. I suppose this is
> due to a different cause.)

This might not be relevant, but playing music from flash was a particular problem also with the original pulseaudio over network protocol.

However there was a workoround:

http://askubuntu.com/questions/122148/flash-in-browsers-does-not-play-sound-accurately-using-pulse-network-audio

In the first answer here the culprit is pointed out to be: "that Flash is somehow sending the sound to PulseAudio in such a way that it creates a lot of network traffic (think lots of tiny packets, not bandwidth); this overwhelms the network "tunnel" PulseAudio has created"

the workaround was the to "cut out the middle man" by disabling discovering of airtune devices and just starting the web browser like this:

PULSE_SERVER=192.168.0.4:4713 firefox &

I havent been able to test this with Hajime's RAOP module though.

Taking a quick look at wireshark I can se that a lots of tiny packets is not the problem at least not here. And the bandwith is about the same ca 180kbit/s with flash as with working aplications. 

The only diference I can se is that the Flash aplication is sending music packets over the network even when it is paused.

Hope this information can be useful in someway.
Comment 40 Hajime Fujita 2013-09-03 03:47:05 UTC
Hi,

I have reformatted all the patches and uploaded to this `raop2-for-merge` branch.
https://github.com/hfujita/pulseaudio-raop2/commits/raop2-for-merge

Now it is supposed to support both TCP/UDP protocols.
Because I don't have any TCP devices (e.g. old Airport Express?), I would be very happy if someone could test it on TCP devices.

I'll later update the instruction to use it.
Comment 41 Hajime Fujita 2013-09-03 03:48:58 UTC
Hi Matthias,

> Perhaps we could do the connect (and only this) when PA_SINK_INIT state is set.

This might be a good idea. I'll later try this.

> Btw.: I just tested your latest patch (with the memchunk offset) and kindly ask you to put "Cambridge Audio Minx Air 200" on your supported device list. Works like a charm :).

Thank you for reporting. I've put the device on the list.
Comment 42 Hajime Fujita 2013-09-03 04:00:25 UTC
Hi Karl,

> PULSE_SERVER=192.168.0.4:4713 firefox &

I've tested that workaround but didn't get a good result either.

It seems that Flash does not understand the correct sampling rate. This guess is based on the following facts:
* When I play sound from Flash, apparently its tempo gets slower than the original.
* By looking at the packet dump, there are sometimes sequences of packets whose audio payload is all zero (0x00). Probably this "silence" frames cause sound jumps and result in sound glitch.
* When I look at the net, many people are complaining about the sample rate issue in Flash

For example, I found several discussions in Chrome+Flash.
* http://code.google.com/p/chromium/issues/detail?id=229918
* https://code.google.com/p/chromium/issues/detail?id=32757#c64
However, the workaround there (setting default-sample-rate to 48000) did not work.
Comment 43 Matthias 2013-09-03 21:37:56 UTC
Hi Hajime, hi all,

(Bear with me, this is still my first time coding C). 
I implemented a packet retransmission buffer (see attached patch).
It's a simple circular buffer. Each sent audio packet is stored in the buffer, discarding the oldest packet. Buffer size is set to 1000 packets, according to [1].

At a retransmission request from the remote device, the requested packet is retrieved from the buffer and re-send with a retransmission header.

Hajime: The attached patch is against your v4.0+raop branch. I tried to keep the patch to raop_client.c small - it would be great if you could merge it into your raop2-for-merge branch. I could help out if you are short on time.

I also attached a snippet of a packet dump with my Minx Air. Packets 7 and 8 and 17 and 18, respectively, show a successful retransmission request and reply.

All: Please test it and report back if you find any flaws.

Btw.: Hajime, you can add another device to your supported device list: Pioneer VSX-922.

While testing the packet buffer I noticed that the volume level set via PA (I use the gnome applet shipped with ubuntu 12.04) is not linear. Looking at the code and comparing it to [2], I think that the volume calculation is wrong. In the code, volume is calculated between VOLUME_MIN (-144) and VOLUME_MAX (0). I think it should be calculated between (-30.0) and (0.0), as seen in [2].
Fixing this should be easy.

Greetings,

Matthias


[1]: http://git.zx2c4.com/Airtunes2/about/#constants
[2]: http://git.zx2c4.com/Airtunes2/about/#id29
Comment 44 Matthias 2013-09-03 21:39:10 UTC
Created attachment 85150 [details] [review]
Packet retransmission buffer addition to Hajime's v4.0+raop branch
Comment 45 Matthias 2013-09-03 21:40:15 UTC
Created attachment 85151 [details]
Sample PCAP snippet of two retransmissions
Comment 46 Hajime Fujita 2013-09-04 05:45:09 UTC
Created attachment 85168 [details] [review]
A patch to Matthias's packet retransmission patch (85150)

This patch provides several fixes to Matthias's patch, so that it works for Pioneer VSX-43.
This is an experimental patch with several dirty hacks, thus should not be considered for immediate merging.
Comment 47 Hajime Fujita 2013-09-04 05:47:19 UTC
Hi Matthias,

Thank you for taking a crack on the packet retransmission feature.
I was so excited that I almost forgot to have a dinner. :)

I tried your patch (on v4.0+raop at this moment), but unfortunately it did not work, in a sense that I still hear some sound jump when a packet retransmission happens.

After a quick research, I found several issues:
1. "payload type" of the retransmission packet should be 86 (0x56) [1]
2. retransmission packets should be sent to the control port [1], not to the streaming port
3. based on observation of a packet sequence from iPad to VSX-43, the retransmission packet seems to have a special structure. shairport's implementation for retransmission handling [2] supports this observation. 

struct retrans_pkt_hdr {
    uint16_t rtphdr; /* = htons(0x80d6), where 0xd6 = 0x56(payload type) + 0x80(marker == on) */
    uint8_t a; /* unknown; seems always 0x01 */
    uint8_t b; /* unknown; seems some random number around 0x20~0x40  */
    uint16_t orig_rtphdr; /* maybe; seems always htons(0x8060) */
    uint16_t retrans_seq_num; /* in network byte order */
    uint32_t retrans_ts; /* RTP timestamp in the original packet */
};

I almost figured out the structure, but I couldn't figure out meanings in 'a' and 'b' members above.

I wrote a patch for Matthias's patch (attachment #85150 [details] [review]) to try the above idea.
I also included a simple packet loss injection scheme to test the feature efficiently. It intentionally skips 2 consecutive frames (~= 16ms data) per each second.
(Of course, this dirty hack should be removed in the final version.)

This works for VSX-43. It is difficult to tell if it works by listening to the music, but I confirmed by the following method.
1. if I disable the retransmission under packet loss injection, the device asks for retransmission of the same packet (seq. no.) again and again, and I see a burst of log output.
2. if I enable the retransmission, retransmission request per packet appears only once. This proves that the device accepts the retransmission packet.
See send_audio_packet() in raop_client.c to try this.

I'm not sure if this works for other devices, as I'm not 100% sure about the retransmission packet format.

[1]: http://nto.github.io/AirPlay.html#audio-rtpstreams
[2]: https://github.com/abrasive/shairport/blob/master/hairtunes.c#L476

Besides the functionality issues above, I have a few questions regarding design.
1. in send_audio_packet(), it seems dangerous to use (retrans_seq_num > 0) as a flag, as the sequence number may wrap (1 in 65536).
2. I'm not sure if we really need to malloc buffer at resend_packets(). Can't we just use the buffer stored in the pb? Since malloc may fail, I'd rather avoid calling malloc whenever possible.

[3]: http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/CodingStyle/

> Btw.: Hajime, you can add another device to your supported device list: Pioneer VSX-922.

Done. Thank you for reporting.

> I think that the volume calculation is wrong.

Thank you for pointing this out. I'll keep this in mind.
Comment 48 Hajime Fujita 2013-09-04 05:48:51 UTC
For those who want to try my new `raop2-for-merge` branch: (assuming that you already cloned my repo)

Try
$ git fetch origin
$ git checkout -b raop2-for-merge origin/raop2-for-merge

To go back to the "v4.0+raop" branch (I apologize for the terrible branch name...),
$ git checkout v4.0+raop
Comment 49 Matthias 2013-09-04 07:27:48 UTC
Hi Hajime,

sorry for keeping you up late with that patch. I would not have sent it if it had not worked for me. Thank you for testing and (hopefully) fixing it!

>I tried your patch (on v4.0+raop at this moment), but unfortunately it did not work, in a sense that I still hear some sound jump when a packet retransmission happens.

I had a similar approach to yours regarding packet retransmission testing (i. e. intentionally skipping packets in send_audio_packet()). When running this version with retransmission disabled, result were exactly as you describe - the remote device kept sending retransmit request. With retransmission enabled, the device sent exactly one retransmit request (assuming that means the remote device accepted the re-sent packet).
A possible explanation could be that my remote device accepts re-sent audio packets at the streaming port (regardless of payload type(!)) if the sequence number matches the one requested.

> 1. "payload type" of the retransmission packet should be 86 (0x56) [1]
> 2. retransmission packets should be sent to the control port [1], not to the
> streaming port
> 3. based on observation of a packet sequence from iPad to VSX-43, the
> retransmission packet seems to have a special structure. shairport's
> implementation for retransmission handling [2] supports this observation. 

Should have looked up [1] and [2] before sending this patch :/.

> struct retrans_pkt_hdr {
>     uint16_t rtphdr; /* = htons(0x80d6), where 0xd6 = 0x56(payload type) +
> 0x80(marker == on) */
>     uint8_t a; /* unknown; seems always 0x01 */
>     uint8_t b; /* unknown; seems some random number around 0x20~0x40  */
>     uint16_t orig_rtphdr; /* maybe; seems always htons(0x8060) */
>     uint16_t retrans_seq_num; /* in network byte order */
>     uint32_t retrans_ts; /* RTP timestamp in the original packet */
> };
> 
> I almost figured out the structure, but I couldn't figure out meanings in
> 'a' and 'b' members above.
> I'm not sure if this works for other devices, as I'm not 100% sure about the
> retransmission packet format.

I will test retransmission with Airtunes and my Minx Air. Maybe we can get some information about the retransmission scheme from that.

> Besides the functionality issues above, I have a few questions regarding
> design.
> 1. in send_audio_packet(), it seems dangerous to use (retrans_seq_num > 0)
> as a flag, as the sequence number may wrap (1 in 65536).

Maybe a better way would be to add a flag parameter for retransmission and another parameter for the sequence number (with c->seq as default).

> 2. I'm not sure if we really need to malloc buffer at resend_packets().
> Can't we just use the buffer stored in the pb? Since malloc may fail, I'd
> rather avoid calling malloc whenever possible.

I'm pretty sure there are better ways to do what I did :). As mentioned, my experience in C (or anything without automatic memory management) boils down to two weeks of patching.
Any feedback is welcome.

Will try your version with my device tonight and report back.

Greetings,

Matthias

>[1]: http://nto.github.io/AirPlay.html#audio-rtpstreams
>[2]: https://github.com/abrasive/shairport/blob/master/hairtunes.c#L476
>[3]: http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/CodingStyle/
Comment 50 Hajime Fujita 2013-09-04 13:26:42 UTC
Hi Matthias,

Come to think of it, when I first try your original patch, I didn't see retransmission request burst, that means it also worked for my device. Sorry for having jumped into the wrong conclusion.

> A possible explanation could be that my remote device accepts re-sent audio packets at the streaming port > (regardless of payload type(!)) if the sequence number matches the one requested.

This would make sense. In principle there is no order guarantee for UDP/IP, so the device should expect reordered/delayed packet.
So if the device ignores the payload type field, the behavior would be the same as for natural reordered packet.


BTW, by reading raop_packet_buffer.c, I realized that pb_get_packet() did not work (i.e. did not copy the data to the given user buffer).
> packet_data = packet->packet;
This is just manipulating the pointer, not touching the data referenced by the pointer.

FYI, in C, it should have been either:
    for (i = 0; i < len; i++)
        packet_data[i] = packet->packet[i];
or
    memcpy(packet_data, packet->packet, len);

As a result, even the retransmission packet was accepted by the device, we could not expect to hear a meaningful sound from it.
Probably this is the reason that I experienced sound glitch on retransmission when I tried your patch first.

If you don't mind, I'll take a look and rewrite the code. (At the same time I'll adapt the code to the latest raop2-for-merge branch.)


Thanks,
Hajime
Comment 51 Matthias 2013-09-04 14:26:07 UTC
(In reply to comment #50)
Hi Hajime,

> > A possible explanation could be that my remote device accepts re-sent audio packets at the streaming port > (regardless of payload type(!)) if the sequence number matches the one requested.
> 
> This would make sense. In principle there is no order guarantee for UDP/IP,
> so the device should expect reordered/delayed packet.
> So if the device ignores the payload type field, the behavior would be the
> same as for natural reordered packet.

That's right - though I think the receiver should not simply ignore the payload type. But that's not for me to decide.

> BTW, by reading raop_packet_buffer.c, I realized that pb_get_packet() did
> not work (i.e. did not copy the data to the given user buffer).
> > packet_data = packet->packet;
> This is just manipulating the pointer, not touching the data referenced by
> the pointer.
> 
> FYI, in C, it should have been either:
>     for (i = 0; i < len; i++)
>         packet_data[i] = packet->packet[i];
> or
>     memcpy(packet_data, packet->packet, len);

I think that adds me to the mass of people who don't get C pointer semantics right :). Thank you for clarification. 

> As a result, even the retransmission packet was accepted by the device, we
> could not expect to hear a meaningful sound from it.
> Probably this is the reason that I experienced sound glitch on
> retransmission when I tried your patch first.

Makes sense to me - maybe in my real-world capture case I just overheard the glitch... it was late last night.
 
> If you don't mind, I'll take a look and rewrite the code. (At the same time
> I'll adapt the code to the latest raop2-for-merge branch.)

Feel free to do so.

Greetings,

Matthias
Comment 52 Hajime Fujita 2013-09-06 05:01:11 UTC
(In reply to comment #51)
Hi Matthias,

Okay, actually it took more than I had originally expected, but I finally fixed a bunch of bugs (some made by you, others by me) and integrated the packet retransmission feature. It works quite well in my environment, but needless to say it should be tested in many environments!
https://github.com/hfujita/pulseaudio-raop2/commits/raop2-for-merge

All:
I updated the document for the latest `raop2-for-merge` branch.
http://hfujita.github.io/pulseaudio-raop2/
Comment 53 Adam 2013-09-14 21:20:28 UTC
Greetings, I have checked out the code from the git repo posted with latest patches (thanks guys!) and was able to compile it on my Fedora box after figuring out which libraries I needed in their Fedora equivalent. 

The one thing I noticed while compiling was that I had to disable the "--enable-x11" flag, I'm not sure if I'm missing another library or if Fedora just does things in such a different way that I need to use a more extensive config flag.

It did compile without issues other than that. However, the next step when I launch paprefs to enable the Apple airplay stuff, and the checkboxes are greyed out. I am not sure how to proceed. I tried doing an strace to perhaps get some sort of logical hint I might follow. But have nothing.

Ideas? I can screenshot what I'm seeing but its pretty straight forward.
Comment 54 Hajime Fujita 2013-09-14 21:27:17 UTC
(in reply to comment #53)

Hi Adam,

Since I'm not using Fedora right now, so this is just a guess, but how about installing pulseaudio-module-zeroconf package?
http://blog.kosmokaryote.org/2010/05/sending-music-to-airport-express-from.html
Comment 55 Tanu Kaskinen 2013-09-16 04:52:09 UTC
(In reply to comment #53)
> It did compile without issues other than that. However, the next step when I
> launch paprefs to enable the Apple airplay stuff, and the checkboxes are
> greyed out. I am not sure how to proceed. I tried doing an strace to perhaps
> get some sort of logical hint I might follow. But have nothing.
> 
> Ideas? I can screenshot what I'm seeing but its pretty straight forward.

paprefs requires module-gconf to be loaded in pulseaudio. Did you compile pulseaudio with gconf support?
Comment 56 paulw 2013-09-22 17:22:19 UTC
Thank you very much to Fujita-san, Matthias et al for all your efforts on this. I have been hoping for this for quite a while (e.g. http://lists.freedesktop.org/archives/pulseaudio-discuss/2013-June/017490.html) and am very grateful you have tackled this.  It seems to be working very nicely on Linn DS devices too.
Regards,
Paul
Comment 57 bugs-pulseaudio 2013-09-29 15:27:53 UTC
This is indeed a great work. It works like a charm with my local XBMC. However, the branch from Hajime Fujita doesn't support IPv6. It should at least ignore it, but I think it's better to implement it the right way. See https://github.com/hfujita/pulseaudio-raop2/issues/1 for some details.
Comment 58 Adam 2013-10-27 17:06:52 UTC
(In reply to comment #54)
> (in reply to comment #53)
> 
> Hi Adam,
> 
> Since I'm not using Fedora right now, so this is just a guess, but how about
> installing pulseaudio-module-zeroconf package?
> http://blog.kosmokaryote.org/2010/05/sending-music-to-airport-express-from.
> html

I will try this out and get back to you when I get some time. Thanks!
Comment 59 Adam 2013-12-18 07:44:39 UTC
This is the error I receive:

Established under name 'D4B962083E3D@ShairPort 15106 on localhost.localdomain'
Audio StreamWGyL38pFImADnW9jHQ4nXg==Failed to create secure directory (/run/user/0/pulse): Permission denied
Failed to create secure directory (/run/user/0/pulse): Permission denied
ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused

FATAL: Could not open ao device


I have the pulseaudio daemon running as my XBMC user.
Comment 60 A 2014-05-11 18:23:16 UTC
Using Fujita's code on ubuntu I lose my local sound devices. How do I retain my loxal sound devices, is it code or config driven?
Comment 61 Alex Zuepke 2014-06-21 15:13:43 UTC
(In reply to comment #60)
> Using Fujita's code on ubuntu I lose my local sound devices. How do I retain
> my loxal sound devices, is it code or config driven?

I tried Hajime's raop2-for-merge2 branch on my Linux Mint 16 today.
I observed the same effect (no local sound devices). Both local sound and the connection to my AppleTV 3 failed with "invalid sample format specification" in the logs, which made me curious.

I tracked the problem: the bug seems to be in PA's handling of configuration data. If no explicit value is provided, PA should use the default value. Instead, garbage on stack was returned. The following patch fixes the default initialization, and both local sound and AppleTV are working fine now.

diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 432e480..6855b6a 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -369,6 +369,7 @@ int pa_modargs_get_sample_rate(pa_modargs *ma, uint32_t *rate) {
 
     pa_assert(rate);
 
+    rate_local = *rate;
     if ((pa_modargs_get_value_u32(ma, "rate", &rate_local)) < 0 ||
         rate_local <= 0 ||
         rate_local > PA_RATE_MAX)
Comment 62 Hakan Fouren 2014-06-23 05:56:52 UTC
Successfully compiled and used Fujita-san's Pulseaudio-raop2 fork on Trusty (Ubuntu 14.04), but I had to apply the patch in the attached URL to make it compile...

Anyhow, now works like a charm on my Bowers & Wilkins Zeppelin air, so if Fujita-san wants, it can be added to the list of supported devices...

Thanks for the great work!
Comment 63 Hakan Fouren 2014-06-23 05:58:50 UTC
Oops, put the URL where it should not be, here it is instead:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=736877
Comment 64 Hajime Fujita 2014-10-12 00:50:10 UTC
A,
> Using Fujita's code on ubuntu I lose my local sound devices. How do I retain my loxal sound devices, is it code or config driven?

Maybe you could try "Configuration file for local devices" in the instruction. http://hfujita.github.io/pulseaudio-raop2/

Alex,
Thanks, actually it was fixed in the mainstream: https://github.com/pulseaudio/pulseaudio/commit/ff06e24eb5e4c00c2b80172dbce0971376fc167f
So I believe this should be in the tree now (as Colin Leroy rebased in against master recently).

Hakan,
Thank you for trying and reporting.
Comment 65 Tanu Kaskinen 2017-01-18 23:58:59 UTC
I think this bug can be considered fixed now that the raop2 patches have landed in the "next" branch. The code will be released in PulseAudio 11.0.


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.