How to control airplay on client side

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:

  1. receives mDNS resolve command: Active-Remote and DACP-ID from shairport

  2. receives 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 !

你可能感兴趣的:(dacp,airplay)