Version 0.3
The original version 0.2 can be found at http://tapjam.net/daap/
- Introduction
- DAAP Overview
- DAAP Packet Format
- Client/Server conversation overview
- server-info
- [content-codes] (#2-content-codes)
- [login] (#3-login-and-session-id)
- [update] (#4-update-and-server-revision)
- [databases] (#5-database-list)
- [items] (#6-song-list)
- [containers] (#7-playlist-list)
- [playlist] (#8-playlist)
- [song] (#9-stream-song)
- [song artwork] (#10-song-artwork)
- [Appendix A - Content Codes] (#appendix-a---content-codes)
- [Appendix B - Content Types] (#appendix-b---content-types)
- [Appendix C - Protocol Changes] (#appendix-c---protocol-changes)
- [Changes to this document] (#changes-to-this-document)
Apple introduced the Digital Audio Access Protocol (DAAP) with iTunes 4, to allow iTunes to share its library with other machines also running iTunes. iTunes uses Bonjour 1, specifically the multi-cast dns and service discovery bits, to find other local machines that happen to be running iTunes and also sharing their libraries.
DAAP, in a nutshell, is a fairly simple protocol. Once iTunes has found another instance, it will connect to, and download basic server information about that instance. It will then download a dictionary of attributes that will be used at various other points in the conversation. iTunes then makes a series of DAAP requests to download the list of songs from the server, as well as the various playlists of those songs that the server has shared. Finally, when iTunes wishes to stream a song from the server, once again, a request is made to the server to stream the selected track back to iTunes. In addition, there is a provision for allowing the client to pick up on when changes are made to the server.
The iTunes implementation of DAAP imposes some restrictions. Most notably, it limits a user to 5 connections/sessions at any given point. Note, that a session doesn't necessarily have to be actively streaming music to be counted to that five connections. (The limit on connections is actually 10, as a client may have up to two connections to the server - one for the database/playlist updates, the other for streaming).
Interestingly enough, the iTunes implementation of DAAP does not restrict access to the local network. This has lead to the rise of a number of iTunes server repositories, including one that uses the DAAP protocol to download the playlists of servers registered with it, and uses that information to provide a search engine.
At it's heart, DAAP is the latest in a long line of protocols that uses HTTP as it's underlying transport mechanism. It appears to prefer using the connection-oriented HTTP/1.1. Though there is also some support, it seems, for HTTP/1.0. This document will focus on the HTTP/1.1 implementation, and assume that the HTTP/1.0 implementation is around for some strange legacy reason.
(Note: the specifics of writing HTTP clients and servers are beyond the scope of this document. There are a number of excellent libraries and resources out there for providing this functionality, however).
While the requests are normal URLs (documented further below), the responses have the mime-type application/x-dmap-tagged. This type appears to be a flattened form of a plist or xml formatted file. (In fact, it would be fairly trivial to write an x-dmap-tagged to xml converter, with just a little bit of foreknowledge on the specific tags).
The basic format of DAAP (expanded on below) is:
tag name | size | data |
---|---|---|
4 byte ASCII | 4 byte int | size bytes |
where the length of the data is determined by the size. The 4 byte tag can be represented as either an integer, or as a four character string. (Of course, the characters in that string form the bytes of the 4 byte integer, highest order byte first - e.g. the code 'minm' has the numeric value 1835626093. This jumping back and forth comes in real handy in writing code to deal with daap, as it allows you to use switch/jump tables to react to the various codes, with a minimal amount of hash-table lookup/string conversion/etc...)
Very early on in the DAAP conversation, a 'map' of content codes, their long name, and their types gets sent back. This map is used to provide the basic description for a number of fields used in various points further down. See appendix A for a full listing of codes used in the iTunes conversation.
An example, simple dmap-tagged block, the response to a /login request:
0000000 155 154 157 147 000 000 000 044 155 163 164 164 000 000 000 004
m l o g \0 \0 \0 $ m s t t \0 \0 \0 004
0000020 000 000 000 310 155 154 151 144 000 000 000 004 000 000 037 336
\0 \0 \0 310 m l i d \0 \0 \0 004 \0 \0 037 336
or:
NOTE: For some reason, iTunes sometimes lies about the size of the response that its sending back, claiming to be sending back more than it actually ends up sending. No reason for this has been found, and iTunes behaves properly if sent a packet with the correct size, so it seems to be a non issue (save for possibly sloppy coding on apple's part?). The login response may be padded for an extra field that doesn't always get dropped in (but iTunes always counts it).
For container types (e.g. content codes with a type 12, or list), the data can, itself contain more code/size/data blocks. Lists will contain multiple code/size/data blocks, one for each item in the list.
All sizes are in bytes and count only the following data block.
Here's a quick diagram of the flow of requests/responses in a DAAP session:
client server Description
---/server-info----> Request for server info
<-------msrv-------- server info response
---/content-codes--> request for content-codes
<-------mccr-------- content-codes response
---/login----------> login
<-------mlog-------- login response (with sessionid)
---/update---------> update request
<-------mupd-------- update response (with server rev)
---/databases------> database request
<-------avdb-------- data base response (with dbid)
---/db/id/items----> request songs
<-------adbs------- list of songs in database
-/db/id/containers-> request playlists
<-------aply-------- list of playlists in database
-/db/id/c/id/items-> request playlist
<-------apso-------- list of songs in playlist
-/db/id/c/id/items->
<-------apso--------
-/db/id/c/id/items->
<-------apso--------
-/db/id/c/id/items->
<-------apso--------
-/db/id/items/x.mp3-> request mp3
<--stream-mp3-file--- stream'd mp3
(For some reason, the iTunes server flags the content type as x-dmap-tagged for the streamed mp3 - i don't know if this is laziness on apple's part, or an intent to trip up web browsers trying to download tracks).
The following sections will describe the requests and responses in detail. These sections will be expanded on ures are discovered and documented. (Note: daap://server can be interchanged with http://server:3689 for purposes of coding/using command line utilities such as curl/wget to poke at things)
Request: daap://server/server-info (or http://server:3689/)
Response: msrv
Description: Provides basic negotiation info on what the server does and doesn't support and protocols.
Content: (See appendix A for detailed information on codes)
- msrv
- mstt - status
- apro - daap protocol
- msix - does the server support indexing?
- msex - does the server support extensions?
- msup - does the server support update?
- msal - does the server support auto-logout?
- mstm - timeout interval
- mslr - is login required?
- msqy - does the server support queries?
- minm - server name
- msrs - does the server support resolve? (needs persistent ids)
- msbr - does the server support browsing?
- mspi - does the server support persistent ids?
- mpro - dmap protocol version
Request: daap://server/content-codes
Response: mccr
Description: Provides a dictionary of content codes, names, and size information.
Content: ( See Appendix A for detailed code information )
mccr
mstt status
mdcl dictionary entry (one for each type)
mcna item name
mcnm item number
mcty item type
Request: daap://server/login
Response: mlog
Description: Provides session information to use for the rest of the session. If requiresLogin is set in the server-info, then from this point on, a basic http authentication header needs to be included with the request. It is basically a header of the form: Authentication: base64encodedusernamepassword where the base64 encoded password is the string: username:password base64 encoded (obviously replacing username and password with the appropriate values). Username appears to be meaningless. A shame - i like the idea of being able to do user level permissions.
Content:
mlog
mstt status
mlid session id (used in remaining requests <sid> below)
Request: daap://server/update?session-id=<sid>
Response: mupd
Description: There seem to be two forms update requests. The first is the update when logging in, to get the initial data. The second (not yet documented) is to catch updates/changes to the server database. Note the addition of the session-id from the login.
Conent:
mupd
musr the server revision (<rid> below)
mstt
Request: daap://server/databases?session-id=<sid>&revision-id=<rid>
Response: avdb
Description: This provides a list of databases served up by the server. At present, it appears that only one db per server is the norm, but it may be possible to have multiple servers.
Content:
avdb
mstt - status
muty - update type - always 0 for now
mtco - total number of matching records
mrco - total number of records returned
mlcl - listing of records
mlit - single record
miid - database id (<dbid> in subsequent requests)
mper - database persistent id
minm - database name
mimc - number of items (songs) in the database
mctc - number of containers (playlists) in the database
Request: daap://server/databases/<dbid>/items?type=music&meta=<list of fields>&session-id=<sid>&revision-id=<rid>
Response: apso
Description: This is the list of songs in the database. Every song. Note given the format of the records and the request, I believe that it's possible to have other types as well. The standard list of fields is (e.g. the value for meta):
dmap.itemid,dmap.itemname,dmap.itemkind,dmap.persistentid,
daap.songalbum,daap.songartist,daap.songbitrate,
daap.songbeatsperminute,daap.songcomment,daap.songcompilation,
daap.songcomposer,daap.songdateadded,daap.songdatemodified,
daap.songdisccount,daap.songdiscnumber,daap.songdisabled,
daap.songeqpreset,daap.songformat,daap.songgenre,
daap.songdescription,daap.songrelativevolume,
daap.songsamplerate,daap.songsize,daap.songstarttime,
daap.songstoptime,daap.songtime,daap.songtrackcount,
daap.songtracknumber,daap.songuserrating,daap.songyear,
daap.songdatakind,daap.songdataurl,com.apple.itunes.norm-volume
Note that in the response, it is very important that itemtype(mikd)
be returned first for each record. It looks like the order of the
remaining fields can be arbitrary, but it is vital that itemid
be first. It also appears to be permissable to leave out fields
that are request. I'd imagine there is some subset of fields
that is required, however.
Response:
apso
mstt - status
muty - update type (0)
mtco - matched items
mrco - items in message
mlcl - list of items
mlit - single item (one per song)
mikd - item kind (2 for music)
miid - item id
minm - item name (e.g. the trackname)
... - the remaining fields for the track
Request: daap://server/databases/<dbid>/containers?meta=<containermeta>&session-id=<sid>&revision-id=<rid>
Response: aply
Description: provides a list of the playlists stored on the server, as well as some basic information about them, depending on the metadata that the client asks for. So far, iTunes seems to ask for:
dmap.itemid,dmap.itemname,dma.persistentid,
com.apple.itunes.smart-playlist
Content:
aply
mstt - status
muty - update type(0)
mtco - matched items
mrco - items in response
mlcl
mlit - playlist entry (one per playlist)
miid - the playlist's itemid (plid below)
mper - the playlist's persistent id
minm - the playlist's name
mimc - the number of items in the playlist
aeSP (optional) - is it a smart playlist
Request: daap://server/databases/<dbid>/containers/<plid>/items?type=music&meta=<playlistmeta>&session-id=<sid>&revision-id=<rid>
Response: apso
Description: provides a list of items in the given playlist, providing the requested meta information for each item. The most commonly requested items seem to be:
dmap.itemkind,dmap.itemid,dmap.containeritemid
Content:
apso
mstt - status
muty - update type(0)
mtco = matched items
mrco - returned items
mlcl - list of playlist entries
mlit - single playlist entry (one per song)
mikd - item kind (2 for music)
miid - itemid of the song
containeritemid - id in the container
Request: daap://server/databases/<dbid>/items/<itemid>.mp3?session-id=<sid>
Response: streamed mp3
Description: Request a song from the server. Note that it is possible to use the 'range' http header to specify the range of a song, e.g. the start and end bytes for the track. It's important for a server to support this, in case itunes users decide to jump around.
Content: the raw mp3 stream
Request: daap://server/databases/<dbid>/items/<itemid>/extra_data/artwork?session-id=<sid>
Response: image
Description:
Content: Image as jpeg or png (see content-type header in response from server)
This is a list of the content codes used by iTunes and DAAP server implementations.
Code | Type | Name | Description |
---|---|---|---|
mdcl | list | dmap.dictionary | a dictionary entry |
mstt | int | dmap.status | the response status code these appear to be http status codes, e.g. 200 |
miid | int | dmap.itemid | an item's id |
minm | string | dmap.itemname | an items name |
mikd | byte | dmap.itemkind | the kind of item. So far, only '2' has been seen, an audio file? |
mper | long | dmap.persistentid | a persistend id |
mcon | list | dmap.container | an arbitrary container |
mcti | int | dmap.containeritemid | the id of an item in its container |
mpco | int | dmap.parentcontainerid | |
msts | string | dmap.statusstring | |
mimc | int | dmap.itemcount | number of items in a container |
mrco | int | dmap.returnedcount | number of items returned in a request |
mtco | int | dmap.specifiedtotalcount | number of items in response to a request |
mlcl | list | dmap.listing | a list |
mlit | list | dmap.listingitem | a single item in said list |
mbcl | list | dmap.bag | |
mdcl | list | dmap.dictionary | |
msrv | list | dmap.serverinforesponse | response to a /server-info |
msau | byte | dmap.authenticationmethod | (should be self explanitory) |
mslr | byte | dmap.loginrequired | |
mpro | version | dmap.protocolversion | |
apro | version | daap.protocolversion | |
msal | byte | dmap.supportsuatologout | |
msup | byte | dmap.supportsupdate | |
mspi | byte | dmap.supportspersistentids | |
msex | byte | dmap.supportsextensions | |
msbr | byte | dmap.supportsbrowse | |
msqy | byte | dmap.supportsquery | |
msix | byte | dmap.supportsindex | |
msrs | byte | dmap.supportsresolve | |
mstm | int | dmap.timeoutinterval | |
msdc | int | dmap.databasescount | |
mccr | list | dmap.contentcodesresponse | the response to the content-codes request |
mcnm | int | dmap.contentcodesnumber | the four letter code |
mcna | string | dmap.contentcodesname | the full name of the code |
mcty | short | dmap.contentcodestype | the type of the code (see appendix b for type values) |
mlog | list | dmap.loginresponse | response to a /login |
mlid | int | dmap.sessionid | the session id for the login session |
mupd | list | dmap.updateresponse | response to a /update |
msur | int | dmap.serverrevision | revision to use for requests |
muty | byte | dmap.updatetype | |
mudl | list | dmap.deletedidlisting | used in updates? (document soon) |
avdb | list | daap.serverdatabases | response to a /databases |
abro | list | daap.databasebrowse | |
abal | list | daap.browsealbumlistung | |
abar | list | daap.browseartistlisting | |
abcp | list | daap.browsecomposerlisting | |
abgn | list | daap.browsegenrelisting | |
adbs | list | daap.databasesongs | response to a /databases/id/items |
asal | string | daap.songalbum | the song ones should be self exp. |
asar | string | daap.songartist | |
asbt | short | daap.songsbeatsperminute | |
asbr | short | daap.songbitrate | |
ascm | string | daap.songcomment | |
asco | byte | daap.songcompilation | |
asda | date | daap.songdateadded | |
asdm | date | daap.songdatemodified | |
asdc | short | daap.songdisccount | |
asdn | short | daap.songdiscnumber | |
asdb | byte | daap.songdisabled | |
aseq | string | daap.songeqpreset | |
asfm | string | daap.songformat | |
asgn | string | daap.songgenre | |
asdt | string | daap.songdescription | |
asrv | byte | daap.songrelativevolume | |
assr | int | daap.songsamplerate | |
assz | int | daap.songsize | |
asst | int | daap.songstarttime | (in milliseconds) |
assp | int | daap.songstoptime | (in milliseconds) |
astm | int | daap.songtime | (in milliseconds) |
astc | short | daap.songtrackcount | |
astn | short | daap.songtracknumber | |
asur | byte | daap.songuserrating | |
asyr | short | daap.songyear | |
asdk | byte | daap.songdatakind | |
asul | string | daap.songdataurl | |
aply | list | daap.databaseplaylists | response to /databases/id/containers |
abpl | byte | daap.baseplaylist | |
apso | list | daap.playlistsongs | response to /databases/id/containers/id/items |
prsv | list | daap.resolve | |
arif | list | daap.resolveinfo | |
aeNV | int | com.apple.itunes.norm-volume | |
aeSP | byte | com.apple.itunes.smart-playlist |
(Note: the ones in parenthesis are assumed based on the arrangement of types, however they have not yet been confirmed)
id | type | size | description |
---|---|---|---|
1 | byte | 1 byte | |
2 | (unsigned byte) | 1 byte | |
3 | short | 2 byte | |
4 | (unsigned short) | 2 byte | |
5 | int | 4 byte | |
6 | (unsigned int) | 4 byte | |
7 | long | 8 byte | |
8 | (unsigned long) | 8 byte | |
9 | string | variable | Encoded as UTF-8 |
10 | date | 4 byte | 4 byte int as seconds since 1.1.1970 (UNIX timestamp) |
11 | version | 4 byte | represented as 4 single bytes, e.g. 0.1.0.0 or as two shorts, e.g. 1.0 |
12 | list | variable |
With iTunes 4.0.1 apple introduced a handful of changes that break compatability with iTunes 4.0. The changes are simply:
- the version number is bumped to 2.0, from 1.0
- the sessionid needs to be an unsigned integer, it looks like the way session id's are created caused a signed sessiondid, e.g. -9184 as a signed int, the string sessionid needs to be unsigned, e.g. 4294958112 - I don't know if this goes for all numeric values, or just sessionid
- In Version 0.3
- Conversion into Markdown format
- Added appendix C - Protocol Changes
- Cleanup of chapters I, II and III
1 Apples implementation of the zeroconf standards Wikipedia. ↩