Most of the times airplay client just streaming audio to sound card. What if we want to skip the current playing song without any operations on iTunes ?
DACP (Digital Audio Control Protocol)
DACP solved this problem.
Digital Audio Control Protocol (DACP) is a protocol used by the iTunes and other audio player and server applications on Mac, Windows and Linux computers.
Acording to Unofficial AirPlay Protocol Specification. Airplay protocol includes a subset of DACP.
Send a HTTP request to iTunes DACP Server can pause the current playing song.
GET /ctrl-int/1/pause HTTP/1.1
Host: starlight.local.
Active-Remote: 1986535575
But, we need to find out the exact port of iTunes DACP Server and the value of Active-Remote
. They are hided in the shairport RTSP request header. RTSP is the protocol used in airplay audio streaming.
Airplay RTSP headers
Run shairport
with level 2 verbose mode, then you'll get all RTSP headers dumped.
$ ./shairport -vv
....
received request: SET_PARAMETER rtsp://192.168.1.102/9487978813838695974 RTSP/1.0
RTP-Info: rtptime=2141729804
CSeq: 7
DACP-ID: 3F66DB0A16A7EBD6
Active-Remote: 2319864219
Content-Type: text/parameters
Content-Length: 43
....
About 10~20 RTSP headers will be dumped in first few seconds airplay get connected. Every RTSP request contains DACP-ID
and Active-Remote
fields. These fields will be used for DACP controlling later.
mDNS
mDNS is a service discovery protocol (SDP) like uPNP. Apple's Bonjour is based on mDNS.
Both iTunes and shairport uses mDNS to publish their services. When iTunes airplay device list menu is expanded, it searchs all airplay clients by mDNS discovery. When airplay is streaming, iTunes' DACP service is published.
Avahi is an opensource mDNS implement. Use avahi-browse
command can find all shairport clients
= eth1 IPv4 6D2EDA9A5717@SugrCube_XXXX _raop._tcp local
hostname = [none-4.local]
address = [192.168.1.233]
port = [5002]
txt = ["md=0,1,2" "da=true" "pw=false" "txtvers=1" "vn=3" "sr=44100" "ss=16" "ch=2" "cn=0,1" "et=0,1" "ek=1" "sm=false" "tp=UDP"]
sr=44100 ss=16 ch=2
means audio is sample rate 44100, 16bit, 2 channel.
ONLY when shairport is running, iTunes DACP service can be discovered by avahi-browse
.
= eth1 IPv4 iTunes_Ctrl_816F839CE21BDC9B _dacp._tcp local
hostname = [xb-2.local]
address = [192.168.1.191]
port = [50971]
txt = []
In service name iTunes_Ctrl_816F839CE21BDC9B
. 816F839CE21BDC9B
is same as DACP-ID
in RTSP headers. DACP HTTP Request should be sent to 192.168.1.191:50971
.
Hacking shairport
Shairport doesn't save Active-Remote
or DACP-ID
by default, need to do some hacking.
in shairport rtsp.c: msg_handle_line()
, function handling RTSP Headers. Active-Remote
and DACP-ID
can be retrived:
char *active_remote = msg_get_header(msg, "Active-Remote");
char *dacp_id = msg_get_header(msg, "DACP-ID");
A patch for shairport is HERE.
Use libAvahi
Avahi is the best open source mDNS client & server so far. Both shairport and DACP client depend on it.
The making and deploying of avahi is complex. It depends on a f**king lot of libraries. In order to use libavahi-client:
- dbus-daemon must be running at background
- in avahi-daemon's configure file,
enable-dbus=true
must be specified. - in avahi make configure,
--with-dbus-system-address
must be specified as same as libdbus's. - must contains this avahi.conf in dbus's
system.d
directory. (according to this issue)
Or in Ubuntu, there is no configuration at all ..
Write DACP Client
Here is a small DCAP Client written by Sugr team. It's a daemon running at UDP port 3391:
receives mDNS resolve command:
Active-Remote
andDACP-ID
from shairportreceives iTunes control command:
previtem
/nextitem
form user
Paired with the hacked shairport, using nc
command send strings to UDP port 3391 can simply control current playing song.
# previous song
echo -ne "previtem" | nc -u -4 localhost 3391
# next song
echo -ne "nextitem" | nc -u -4 localhost 3391
# toggle between play and pause
echo -ne "playpause" | nc -u -4 localhost 3391
Thanks
Thanks to IV technology for technical discussing and sharing !