3860 lines
116 KiB
HTML
3860 lines
116 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
|
||
<meta http-equiv="Content-language" content="en"/>
|
||
<meta name="keywords" content="airplay,airtunes,protocol,specification"/>
|
||
<meta name="author" content="Clément Vasseur"/>
|
||
<title>Unofficial AirPlay Protocol Specification</title>
|
||
<link rel="stylesheet" type="text/css" href="AirPlay.css"/>
|
||
</head>
|
||
<body>
|
||
<div id="content">
|
||
<h1 class="title">Unofficial AirPlay Protocol Specification</h1>
|
||
<ul class="toc">
|
||
<li><a href="#introduction">1. Introduction</a></li>
|
||
<li><a href="#servicediscovery">2. Service Discovery</a></li>
|
||
<li><ul>
|
||
<li><a href="#servicediscovery-airtunesservice">2.1. AirTunes service</a></li>
|
||
<li><a href="#servicediscovery-airplayservice">2.2. AirPlay Service</a></li>
|
||
</ul></li>
|
||
<li><a href="#photos">3. Photos</a></li>
|
||
<li><ul>
|
||
<li><a href="#photos-httprequests">3.1. HTTP requests</a></li>
|
||
<li><a href="#photos-events">3.2. Events</a></li>
|
||
<li><a href="#photos-photocaching">3.3. Photo Caching</a></li>
|
||
<li><a href="#photos-slideshows">3.4. Slideshows</a></li>
|
||
</ul></li>
|
||
<li><a href="#video">4. Video</a></li>
|
||
<li><ul>
|
||
<li><a href="#video-httprequests">4.1. HTTP requests</a></li>
|
||
<li><a href="#video-events">4.2. Events</a></li>
|
||
</ul></li>
|
||
<li><a href="#audio">5. Audio</a></li>
|
||
<li><ul>
|
||
<li><a href="#audio-rtsprequests">5.1. RTSP requests</a></li>
|
||
<li><a href="#audio-rtpstreams">5.2. RTP Streams</a></li>
|
||
<li><a href="#audio-volumecontrol">5.3. Volume Control</a></li>
|
||
<li><a href="#audio-metadata">5.4. Metadata</a></li>
|
||
<li><a href="#audio-airportexpressauthentication">5.5. AirPort Express Authentication</a></li>
|
||
<li><a href="#audio-remotecontrol">5.6. Remote Control</a></li>
|
||
</ul></li>
|
||
<li><a href="#screenmirroring">6. Screen Mirroring</a></li>
|
||
<li><ul>
|
||
<li><a href="#screenmirroring-httprequests">6.1. HTTP requests</a></li>
|
||
<li><a href="#screenmirroring-streampackets">6.2. Stream Packets</a></li>
|
||
<li><a href="#screenmirroring-timesynchronization">6.3. Time Synchronization</a></li>
|
||
</ul></li>
|
||
<li><a href="#passwordprotection">7. Password Protection</a></li>
|
||
<li><a href="#contributing">8. Contributing</a></li>
|
||
<li><ul>
|
||
<li><a href="#contributing-data">8.1. Device Data</a></li>
|
||
<li><ul>
|
||
<li><a href="#contributing-data-linux">8.1.1. Linux</a></li>
|
||
<li><a href="#contributing-data-macos">8.1.2. macOS</a></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
<li><a href="#history">9. History</a></li>
|
||
<li><a href="#resources">10. Resources</a></li>
|
||
<li><ul>
|
||
<li><a href="#resources-ietfrfcs">10.1. IETF RFCs</a></li>
|
||
<li><a href="#resources-ietfdrafts">10.2. IETF drafts</a></li>
|
||
<li><a href="#resources-appleprotocols">10.3. Apple Protocols</a></li>
|
||
</ul></li>
|
||
</ul>
|
||
|
||
<h1 id="introduction">1. Introduction</h1>
|
||
<p>AirPlay is a family of protocols implemented by Apple to view various
|
||
types of media content on the <em>Apple TV</em> from any iOS device or iTunes.
|
||
In this documentation, “<em>iOS device</em>” refers to an iPhone, iPod touch or
|
||
iPad. The following scenarios are supported by AirPlay:</p>
|
||
|
||
<ul>
|
||
<li><p>Display photos and slideshows from an iOS device.</p></li>
|
||
<li><p>Stream audio from an iOS device or iTunes.</p></li>
|
||
<li><p>Display videos from an iOS device or iTunes.</p></li>
|
||
<li><p>Show the screen content from an iOS device or OS X Mountain Lion.
|
||
This is called <em>AirPlay Mirroring</em>. It requires hardware capable of
|
||
encoding live video without taking too much CPU, so it is only
|
||
available on iPhone 4S, iPad 2, the new iPad, and Macs with Sandy
|
||
Bridge CPUs.</p></li>
|
||
</ul>
|
||
|
||
<p>Audio streaming is also supported from an iOS device or iTunes to an
|
||
AirPort Express base station or a 3<sup>rd</sup> party AirPlay-enabled audio
|
||
device. Initially this was called <em>AirTunes</em>, but it was later renamed
|
||
to AirPlay when Apple added video support for the Apple TV.</p>
|
||
|
||
<p>This document describes these protocols, as implemented in Apple TV
|
||
software version 5.0, iOS 5.1 and iTunes 10.6. They are based on
|
||
well-known standard networking protocols such as <em>Multicast DNS</em>,
|
||
<em>HTTP</em>, <em>RTSP</em>, <em>RTP</em> or <em>NTP</em>, with custom extensions.</p>
|
||
|
||
<p>All these information have been gathered by using various techniques of
|
||
reverse engineering, so they might be somewhat inaccurate and
|
||
incomplete. Moreover, this document does not explain how to circumvent
|
||
any kind of security implemented by Apple:</p>
|
||
|
||
<ul>
|
||
<li><p>It does not give any RSA keys.</p></li>
|
||
<li><p>It does not explain how to decode iTunes videos protected with the
|
||
<em>FairPlay</em> DRM.</p></li>
|
||
<li><p>It does not explain the FairPlay authentication (SAPv2.5) used by iOS
|
||
devices and OS X Mountain Lion to protect audio and screen content.</p></li>
|
||
</ul>
|
||
|
||
<p>Please don’t e-mail me about this, I won’t reply. In fact, none of this
|
||
is actually required to be able to view media content on Apple TV.</p>
|
||
|
||
<h1 id="servicediscovery">2. Service Discovery</h1>
|
||
<p>AirPlay does not require any configuration to be able to find compatible
|
||
devices on the network, thanks to <a href="http://www.ietf.org/id/draft-cheshire-dnsext-dns-sd-11.txt">DNS-based service discovery</a>, based
|
||
on <a href="http://www.ietf.org/id/draft-cheshire-dnsext-multicastdns-15.txt">multicast DNS</a>, aka <em>Bonjour</em>.</p>
|
||
|
||
<p>An AirPlay device such as the Apple TV publishes two services. The first
|
||
one is <em>RAOP</em> (<a href="http://en.wikipedia.org/wiki/Remote_Audio_Output_Protocol">Remote Audio Output Protocol</a>), used for audio
|
||
streaming, and the other one is the AirPlay service, for photo and video
|
||
and in later versions also audio content.</p>
|
||
|
||
<h2 id="servicediscovery-airtunesservice">2.1. AirTunes service</h2>
|
||
<p class="caption">RAOP service from Apple TV</p>
|
||
|
||
<pre><code>name: 5855CA1AE288@Apple TV
|
||
type: _raop._tcp
|
||
port: 49152
|
||
txt:
|
||
txtvers=1
|
||
ch=2
|
||
cn=0,1,2,3
|
||
da=true
|
||
et=0,3,5
|
||
md=0,1,2
|
||
pw=false
|
||
sv=false
|
||
sr=44100
|
||
ss=16
|
||
tp=UDP
|
||
vn=65537
|
||
vs=130.14
|
||
am=AppleTV2,1
|
||
sf=0x4
|
||
</code></pre>
|
||
|
||
<p>The name is formed using the MAC address of the device and the name of
|
||
the remote speaker which will be shown by the clients.</p>
|
||
|
||
<p>The following fields appear in the TXT record:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>txtvers</code></td>
|
||
<td>1</td>
|
||
<td>TXT record version 1</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ch</code></td>
|
||
<td>2</td>
|
||
<td>audio channels: stereo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>cn</code></td>
|
||
<td>0,1,2,3</td>
|
||
<td>audio codecs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>et</code></td>
|
||
<td>0,3,5</td>
|
||
<td>supported encryption types</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>md</code></td>
|
||
<td>0,1,2</td>
|
||
<td>supported metadata types</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pw</code></td>
|
||
<td>false</td>
|
||
<td>does the speaker require a password?</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>sr</code></td>
|
||
<td>44100</td>
|
||
<td>audio sample rate: 44100 Hz</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ss</code></td>
|
||
<td>16</td>
|
||
<td>audio sample size: 16-bit</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tp</code></td>
|
||
<td>UDP</td>
|
||
<td>supported transport: TCP or UDP</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>vs</code></td>
|
||
<td>130.14</td>
|
||
<td>server version 130.14</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>am</code></td>
|
||
<td>AppleTV2,1</td>
|
||
<td>device model</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Audio codecs</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>cn</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>PCM</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>Apple Lossless (ALAC)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>AAC</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>AAC ELD (Enhanced Low Delay)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Encryption Types</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>et</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>no encryption</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>RSA (AirPort Express)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>FairPlay</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>MFiSAP (3rd-party devices)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>FairPlay SAPv2.5</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Metadata Types</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>md</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>text</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>artwork</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>progress</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<h2 id="servicediscovery-airplayservice">2.2. AirPlay Service</h2>
|
||
<p class="caption">AirPlay service</p>
|
||
|
||
<pre><code>name: Apple TV
|
||
type: _airplay._tcp
|
||
port: 7000
|
||
txt:
|
||
deviceid=58:55:CA:1A:E2:88
|
||
features=0x39f7
|
||
model=AppleTV2,1
|
||
srcvers=130.14
|
||
</code></pre>
|
||
|
||
<p>The following fields are available in the TXT record:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>type</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>model</code></td>
|
||
<td>string</td>
|
||
<td>AppleTV2,1</td>
|
||
<td>device model</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>manufacturer</code></td>
|
||
<td>string</td>
|
||
<td>Sound United</td>
|
||
<td>device manufacturer</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>serialNumber</code></td>
|
||
<td>string</td>
|
||
<td>LFKSDKERWER</td>
|
||
<td>device serial number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>fv</code></td>
|
||
<td>string</td>
|
||
<td>p20.1.505.140</td>
|
||
<td>device firmware version</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>osvers</code></td>
|
||
<td>string</td>
|
||
<td>12.2.1</td>
|
||
<td>device OS version</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>deviceid</code></td>
|
||
<td>string</td>
|
||
<td>58:55:CA:1A:E2:88</td>
|
||
<td>Device ID. Usually MAC address of the device</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>features</code></td>
|
||
<td>32 bit hex number,optional high order 32 bit hex number</td>
|
||
<td>0x39f7,0xE</td>
|
||
<td>
|
||
bitfield of supported features. This was originally a 32 bit value but
|
||
it has since been expanded to a 64 bit value. To support both these
|
||
types the mDNS value is encoded as two 32 bit values separated by comma
|
||
with the comma and second 32 bit value being optional.
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pw</code></td>
|
||
<td>boolean</td>
|
||
<td>1</td>
|
||
<td>server is password protected</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>acl</code></td>
|
||
<td>int64</td>
|
||
<td>0</td>
|
||
<td>Access control level</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>srcvers</code></td>
|
||
<td>string</td>
|
||
<td>380.10.1</td>
|
||
<td>airplay version</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>flags</code></td>
|
||
<td>20 bit hex number</td>
|
||
<td>0x804</td>
|
||
<td>bitfield of system flags</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pk</code></td>
|
||
<td>hex string</td>
|
||
<td>b4bf1e47e6aac0b5bb7cf9e2cd0fe5994d4d2f508a5bc6d518856c8842f69c0a</td>
|
||
<td>public key</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pi</code></td>
|
||
<td>UUID string</td>
|
||
<td>f27fd22b-2569-4f65-aa03-1c13dabf49b3</td>
|
||
<td>group_id / PublicCUAirPlayPairingIdentifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>psi</code></td>
|
||
<td>UUID string</td>
|
||
<td>67AE9DB4-6C03-4FF2-9D22-3E7C7DE94DBA</td>
|
||
<td>PublicCUSystemPairingIdentifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>gid</code></td>
|
||
<td>UUID string</td>
|
||
<td>3008B5C8-9BD3-4479-A564-90BFB3D780C0</td>
|
||
<td>group UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>gcgl</code></td>
|
||
<td>boolean</td>
|
||
<td>1</td>
|
||
<td>group contains group leader / Group contains discoverable leader</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>igl</code></td>
|
||
<td>boolean</td>
|
||
<td>1</td>
|
||
<td>is group leader</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>gpn</code></td>
|
||
<td>string</td>
|
||
<td>1</td>
|
||
<td>group public name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hgid</code></td>
|
||
<td>UUID string</td>
|
||
<td>CBCCCC31-29E6-4CBE-84ED-501F555172C9</td>
|
||
<td>home group UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hmid</code></td>
|
||
<td>string</td>
|
||
<td></td>
|
||
<td>household ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pgcgl</code></td>
|
||
<td>boolean</td>
|
||
<td>1</td>
|
||
<td>parent group contains discoverable leader</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pgid</code></td>
|
||
<td>UUID string</td>
|
||
<td>3008B5C8-9BD3-4479-A564-90BFB3D780C0</td>
|
||
<td>parent group UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tsid</code></td>
|
||
<td>UUID string</td>
|
||
<td>3008B5C8-9BD3-4479-A564-90BFB3D780C0</td>
|
||
<td>tight sync UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>rsf</code></td>
|
||
<td>64 bit hex number</td>
|
||
<td>0x0</td>
|
||
<td>required sender features</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>protovers</code></td>
|
||
<td>string</td>
|
||
<td>1.1</td>
|
||
<td>protocol version</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>vv</code></td>
|
||
<td>?</td>
|
||
<td>2</td>
|
||
<td>vodka version</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The <code>pw</code> field appears only if the AirPlay server is password protected.
|
||
Otherwise it is not included in the TXT record.</p>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-Features">2.2.1. Features</h3>
|
||
<p>The <code>features</code> bitfield allows the following features to be defined:</p>
|
||
|
||
<table id="airplay-features">
|
||
<tbody id="airplay-features-interactive" class="noscript">
|
||
<tr>
|
||
<th colspan="3">
|
||
Examples:
|
||
<select id="airplay-features-examples">
|
||
<option value="">Custom</option>
|
||
<option value="0x4A7FFFF7,0x4155FDE">4K Apple TV</option>
|
||
<option value="0x4A7FCA00,0x4156BD0">HomePod</option>
|
||
<option value="0x445D0A00, 0x1C340">Airport Express</option>
|
||
<option value="0x445F8A00, 0x1C340">Denon AVR-X3500H</option>
|
||
<option value="0x445F8A00, 0x1C340">Denon AVR-X1400H</option>
|
||
<option value="0x445F8A00, 0x1C340">Denon AVR-X4400H</option>
|
||
<option value="0x445C0A00, 0x1C340">Libretone LTH200</option>
|
||
<option value="0x445C0A00, 0x1C340">Libretone LTH200</option>
|
||
<option value="0x445F8A00, 0x1C340">Sonos Beam</option>
|
||
<option value="0x445F8A00, 0x1C340">Sonos One</option>
|
||
<option value="0x4A7FFFF7, 0xE">AirServer</option>
|
||
<option value="0x5A7FFFF7, 0x1E">Airplay</option>
|
||
<option value="0x40180200, 0x300">Required for multi-room audio</option>
|
||
</select>
|
||
<input type="text" id="airplay-features-input">
|
||
</th>
|
||
<tr>
|
||
</tbody>
|
||
<tbody id="airplay-features-output" class="noscript">
|
||
<tr>
|
||
<td colspan="3">
|
||
Decimal:
|
||
<span id="airplay-features-dec"></span>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="3">
|
||
Hex:
|
||
<span id="airplay-features-hex"></span>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="3">
|
||
mDNS: <span id="airplay-features-txt"></span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
<tbody>
|
||
<tr>
|
||
<th>bit</th>
|
||
<th>name</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</tbody>
|
||
<tbody>
|
||
<tr id="airplay-features-bit0">
|
||
<td>0</td>
|
||
<td><code>Video</code></td>
|
||
<td>video supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit1">
|
||
<td>1</td>
|
||
<td><code>Photo</code></td>
|
||
<td>photo supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit2">
|
||
<td>2</td>
|
||
<td><code>VideoFairPlay</code></td>
|
||
<td>video protected with FairPlay DRM</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit3">
|
||
<td>3</td>
|
||
<td><code>VideoVolumeControl</code></td>
|
||
<td>volume control supported for videos</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit4">
|
||
<td>4</td>
|
||
<td><code>VideoHTTPLiveStreams</code></td>
|
||
<td>http live streaming supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit5">
|
||
<td>5</td>
|
||
<td><code>Slideshow</code></td>
|
||
<td>slideshow supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit6">
|
||
<td>6</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit7">
|
||
<td>7</td>
|
||
<td><code>Screen</code></td>
|
||
<td>mirroring supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit8">
|
||
<td>8</td>
|
||
<td><code>ScreenRotate</code></td>
|
||
<td>screen rotation supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit9">
|
||
<td>9</td>
|
||
<td><code>Audio</code></td>
|
||
<td>audio supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit10">
|
||
<td>10</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit11">
|
||
<td>11</td>
|
||
<td><code>AudioRedundant</code></td>
|
||
<td>audio packet redundancy supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit12">
|
||
<td>12</td>
|
||
<td><code>FPSAPv2pt5_AES_GCM</code></td>
|
||
<td>FairPlay secure auth supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit13">
|
||
<td>13</td>
|
||
<td><code>PhotoCaching</code></td>
|
||
<td>photo preloading supported</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit14">
|
||
<td>14</td>
|
||
<td><code>Authentication4</code></td>
|
||
<td>Authentication type 4</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit15">
|
||
<td>15</td>
|
||
<td><code>MetadataFeature1</code></td>
|
||
<td>bit 1 of MetadataFeatures</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit16">
|
||
<td>16</td>
|
||
<td><code>MetadataFeature2</code></td>
|
||
<td>bit 2 of MetadataFeatures</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit17">
|
||
<td>17</td>
|
||
<td><code>MetadataFeature0</code></td>
|
||
<td>bit 0 of MetadataFeatures</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit18">
|
||
<td>18</td>
|
||
<td><code>AudioFormat1</code></td>
|
||
<td>support for audio format 1</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit19">
|
||
<td>19</td>
|
||
<td><code>AudioFormat2</code></td>
|
||
<td>support for audio format 2. This bit must be set for AirPlay 2 connection to work</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit20">
|
||
<td>20</td>
|
||
<td><code>AudioFormat3</code></td>
|
||
<td>support for audio format 3. This bit must be set for AirPlay 2 connection to work</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit21">
|
||
<td>21</td>
|
||
<td><code>AudioFormat4</code></td>
|
||
<td>support for audio format 4</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit22">
|
||
<td>22</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit23">
|
||
<td>23</td>
|
||
<td><code>Authentication1</code></td>
|
||
<td>Authentication type 1</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit24">
|
||
<td>24</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit25">
|
||
<td>25</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit26">
|
||
<td>26</td>
|
||
<td><code>HasUnifiedAdvertiserInfo</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit27">
|
||
<td>27</td>
|
||
<td><code>SupportsLegacyPairing</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit28">
|
||
<td>28</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit29">
|
||
<td>29</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit30">
|
||
<td>30</td>
|
||
<td><code>RAOP</code></td>
|
||
<td>RAOP is supported on this port. With this bit set your don't need the AirTunes service</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit31">
|
||
<td>31</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit32">
|
||
<td>32</td>
|
||
<td><code>IsCarPlay</code> / <code>SupportsVolume</code></td>
|
||
<td>Don’t read key from <code>pk</code> record it is known</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit33">
|
||
<td>33</td>
|
||
<td><code>SupportsAirPlayVideoPlayQueue</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit34">
|
||
<td>34</td>
|
||
<td><code>SupportsAirPlayFromCloud</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit35">
|
||
<td>35</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit36">
|
||
<td>36</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit37">
|
||
<td>37</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit38">
|
||
<td>38</td>
|
||
<td><code>SupportsCoreUtilsPairingAndEncryption</code></td>
|
||
<td>
|
||
<code>SupportsHKPairingAndAccessControl</code>,
|
||
<code>SupportsSystemPairing</code> and
|
||
<code>SupportsTransientPairing</code> implies
|
||
<code>SupportsCoreUtilsPairingAndEncryption</code></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit39">
|
||
<td>39</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit40">
|
||
<td>40</td>
|
||
<td><code>SupportsBufferedAudio</code></td>
|
||
<td>Bit needed for device to show as supporting multi-room audio</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit41">
|
||
<td>41</td>
|
||
<td><code>SupportsPTP</code></td>
|
||
<td>Bit needed for device to show as supporting multi-room audio</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit42">
|
||
<td>42</td>
|
||
<td><code>SupportsScreenMultiCodec</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit43">
|
||
<td>43</td>
|
||
<td><code>SupportsSystemPairing</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit44">
|
||
<td>44</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit45">
|
||
<td>45</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit46">
|
||
<td>46</td>
|
||
<td><code>SupportsHKPairingAndAccessControl</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit47">
|
||
<td>47</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit48">
|
||
<td>48</td>
|
||
<td><code>SupportsTransientPairing</code></td>
|
||
<td><code>SupportsSystemPairing</code> implies <code>SupportsTransientPairing</code></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit49">
|
||
<td>49</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-features-bit50">
|
||
<td>50</td>
|
||
<td><code>MetadataFeature4</code></td>
|
||
<td>bit 4 of MetadataFeatures</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit51">
|
||
<td>51</td>
|
||
<td><code>SupportsUnifiedPairSetupAndMFi</code></td>
|
||
<td>Authentication type 8</td>
|
||
</tr>
|
||
<tr id="airplay-features-bit52">
|
||
<td>52</td>
|
||
<td><code>SupportsSetPeersExtendedMessage</code></td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Note that the Apple TV does not support <code>VideoVolumeControl</code>. It has
|
||
probably been introduced for the upcoming Apple television.</p>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-MetadataFeatures">2.2.2. MetadataFeatures</h3>
|
||
<p class="caption">MetadataFeatures</p>
|
||
<pre><code>
|
||
bit 17 = bit 0
|
||
bit 15 = bit 1
|
||
bit 16 = bit 2
|
||
is apple tv = bit 3
|
||
bit 50 = bit 4
|
||
is apple tv or home pod of certain version = bit 5
|
||
is apple tv or home pod of certain version = bit 6
|
||
</code></pre>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-SupportedAudioFormats">2.2.3. SupportedAudioFormats</h3>
|
||
<p class="caption">SupportedAudioFormats</p>
|
||
<pre><code>
|
||
24 20 16 12 8 4 0
|
||
bit 18 = 0x800 = 1000 0000 0000
|
||
bit 19 = 0x40000 = 0100 0000 0000 0000 0000
|
||
bit 20 = 0x400000 = 0100 0000 0000 0000 0000 0000
|
||
bit 21 = 0x1000000 = 0001 0000 0000 0000 0000 0000 0000
|
||
</code></pre>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-Subtype">2.2.4. Subtype</h3>
|
||
<p class="caption">Subtype</p>
|
||
<pre><code>
|
||
AppleTV = deviceModel has prefix “AppleTV”
|
||
HomePod = deviceModel has prefix “AudioAccessory”
|
||
ThirdPartySpeaker = HasUnifiedAdvertiserInfo or SupportsUnifiedPairSetupAndMFi feature is set
|
||
Unknown = otherwise
|
||
</code></pre>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-Subtype">2.2.5. System Flags</h3>
|
||
<p>The <code>flags</code> bitfield allows the following flags to be defined:</p>
|
||
|
||
<table id="airplay-system-flags">
|
||
<tbody id="airplay-system-flags-interactive" class="noscript">
|
||
<tr>
|
||
<th colspan="3">
|
||
Examples:
|
||
<select id="airplay-system-flags-examples">
|
||
<option value="">Custom</option>
|
||
<option value="0x10644">4K Apple TV Not playing</option>
|
||
<option value="0x30e44">4K Apple TV receiving AirPlay audio</option>
|
||
<option value="0x30e44">4K Apple TV receiving AirPlay video</option>
|
||
<option value="0x30644">4K Apple TV receiving AirPlay screen</option>
|
||
<option value="0x10644">4K Apple TV playing local media</option>
|
||
<option value="0x3ac04">HomePod Slave</option>
|
||
<option value="0x18404">HomePod Group Leader</option>
|
||
<option value="0x4">Airport Express</option>
|
||
<option value="0x4">Denon AVR-X3500H</option>
|
||
<option value="0x804">Denon AVR-X3500H Playing Audio</option>
|
||
<option value="0x4">Denon AVR-X1400H</option>
|
||
<option value="0x4">Denon AVR-X4400H</option>
|
||
<option value="0x484">Libretone LTH200</option>
|
||
<option value="0x404">Sonos Beam</option>
|
||
<option value="0x404">Sonos One</option>
|
||
<option value="0x4">AirServer</option>
|
||
<option value="0x4">Airplay</option>
|
||
</select>
|
||
<input type="text" id="airplay-system-flags-input">
|
||
</th>
|
||
<tr>
|
||
</tbody>
|
||
<tbody id="airplay-system-flags-output" class="noscript">
|
||
<tr>
|
||
<td colspan="3">
|
||
Decimal:
|
||
<span id="airplay-system-flags-dec"></span>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="3">
|
||
Hex:
|
||
<span id="airplay-system-flags-hex"></span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
<tbody>
|
||
<tr>
|
||
<th>bit</th>
|
||
<th>name</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</tbody>
|
||
|
||
<tbody>
|
||
<tr id="airplay-system-flags-bit0">
|
||
<td>0</td>
|
||
<td>Problem has been detected</td>
|
||
<td>Defined in CarPlay section of MFi spec. Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit1">
|
||
<td>1</td>
|
||
<td>Device is not configured</td>
|
||
<td>Defined in CarPlay section of MFi spec. Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit2">
|
||
<td>2</td>
|
||
<td>Audio cable is attached</td>
|
||
<td>Defined in CarPlay section of MFi spec. Seen on AppleTV, Denon AVR, HomePod, Airport Express</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit3">
|
||
<td>3</td>
|
||
<td><code>PINRequired</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit4">
|
||
<td>4</td>
|
||
<td>???</td>
|
||
<td>Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit5">
|
||
<td>5</td>
|
||
<td>???</td>
|
||
<td>Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit6">
|
||
<td>6</td>
|
||
<td><code>SupportsAirPlayFromCloud</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit7">
|
||
<td>7</td>
|
||
<td><code>PasswordRequired</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit8">
|
||
<td>8</td>
|
||
<td>???</td>
|
||
<td>Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit9">
|
||
<td>9</td>
|
||
<td><code>OneTimePairingRequired</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit10">
|
||
<td>10</td>
|
||
<td><code>DeviceWasSetupForHKAccessControl</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit11">
|
||
<td>11</td>
|
||
<td><code>DeviceSupportsRelay</code></td>
|
||
<td>Shows in logs as relayable. When set iOS will connect to the device to get currently playing track.</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit12">
|
||
<td>12</td>
|
||
<td><code>SilentPrimary</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit13">
|
||
<td>13</td>
|
||
<td><code>TightSyncIsGroupLeader</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit14">
|
||
<td>14</td>
|
||
<td><code>TightSyncBuddyNotReachable</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit15">
|
||
<td>15</td>
|
||
<td><code>IsAppleMusicSubscriber</code></td>
|
||
<td>Shows in logs as music</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit16">
|
||
<td>16</td>
|
||
<td><code>CloudLibraryIsOn</code></td>
|
||
<td>Shows in logs as iCML</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit17">
|
||
<td>17</td>
|
||
<td><code>ReceiverSessionIsActive</code></td>
|
||
<td>Shows in logs as airplay-receiving. Set when Apple TV is receiving anything via AirPlay.</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit18">
|
||
<td>18</td>
|
||
<td>???</td>
|
||
<td>Not seen set anywhere</td>
|
||
</tr>
|
||
<tr id="airplay-system-flags-bit19">
|
||
<td>19</td>
|
||
<td>???</td>
|
||
<td>Not seen set anywhere</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="servicediscovery-airplayservice-CanBeRemoteControlled">2.2.6. CanBeRemoteControlled</h3>
|
||
<p><code>SupportsBufferedAudio</code> is set and <code>PINRequired</code> is not set</p>
|
||
|
||
<h3 id="servicediscovery-airplayservice-StateChanges">2.2.7. State Changes</h3>
|
||
<p>Depending on the state of the device the mDNS record is changed to
|
||
reflect this. Primarily it is the <code>flags</code>, <code>gid</code>,
|
||
<code>igl</code>, <code>gcgl</code>, <code>pgid</code> and
|
||
<code>pgcgl</code> fields that are changed.</p>
|
||
|
||
<p class="caption">Apple TV not playing</p>
|
||
<pre><code>flags=0x10644
|
||
gid=712F0759-5D44-41E7-AB67-FAB0AD39E165
|
||
igl=1
|
||
gcgl=1
|
||
</code></pre>
|
||
|
||
<p class="caption">Apple TV receiving AirPlay audio</p>
|
||
<pre><code>flags=0x30e44
|
||
gid=19F5D4B2-8A06-4792-923E-8AFA83913238
|
||
igl=0
|
||
gcgl=0
|
||
pgid=19F5D4B2-8A06-4792-923E-8AFA83913238
|
||
pgcgl=0
|
||
</code></pre>
|
||
|
||
<p class="caption">Apple TV receiving AirPlay video</p>
|
||
<pre><code>flags=0x30e44
|
||
gid=19F5D4B2-8A06-4792-923E-8AFA83913238
|
||
igl=0
|
||
gcgl=0
|
||
pgid=19F5D4B2-8A06-4792-923E-8AFA83913238
|
||
pgcgl=0
|
||
</code></pre>
|
||
|
||
|
||
<p class="caption">Apple TV receiving AirPlay screen</p>
|
||
<pre><code>flags=0x30644
|
||
gid=712F0759-5D44-41E7-AB67-FAB0AD39E165
|
||
igl=1
|
||
gcgl=1
|
||
</code></pre>
|
||
|
||
<p class="caption">Apple TV playing local media</p>
|
||
<pre><code>flags=0x10644
|
||
gid=FA69F5A2-5574-4E4D-A676-CA7F61A904A3
|
||
igl=1
|
||
gcgl=1
|
||
</code></pre>
|
||
|
||
|
||
<h3 id="servicediscovery-airplayservice-Protocol">2.2.8. Protocol</h3>
|
||
|
||
<p>The AirPlay server is both a <em>RTSP</em> server (<a href="http://www.ietf.org/rfc/rfc2326.txt">RFC 2326</a>)
|
||
and a <em>HTTP</em> server (<a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>).
|
||
Two connections are made to this server, the second one being used as a
|
||
<a href="http://tools.ietf.org/id/draft-lentczner-rhttp-00.txt">reverse HTTP</a>
|
||
connection. This allows a client to receive asynchronous events, such
|
||
as playback status changes, from a server.</p>
|
||
|
||
<p>All HTTP requests share some common headers:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>X-Apple-Session-ID</code></td>
|
||
<td>1bd6ceeb…</td>
|
||
<td>UUID for the session</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>X-Apple-Device-ID</code></td>
|
||
<td>0xdc2b61a0ce79</td>
|
||
<td>MAC address</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The reverse connection looks like this:</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /reverse
|
||
Upgrade: PTTH/1.0
|
||
Connection: Upgrade
|
||
X-Apple-Purpose: event
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 101 Switching Protocols
|
||
Date: Thu, 23 Feb 2012 17:33:41 GMT
|
||
Upgrade: PTTH/1.0
|
||
Connection: Upgrade
|
||
</code></pre>
|
||
|
||
<p>The <code>X-Apple-Purpose</code> header makes it clear that this connection is used
|
||
for sending events to the client, whereas <code>X-Apple-Session-ID</code> is used
|
||
to link this connection to the other (non-reverse) one. Events are
|
||
delivered using a <code>POST</code> request for sending an XML property list to the
|
||
<code>/event</code> location.</p>
|
||
|
||
<h1 id="photos">3. Photos</h1>
|
||
<p>Photos are <em>JPEG</em> data transmitted using a <code>PUT</code> request to the AirPlay
|
||
server. They can be displayed immediately, or cached for future use.</p>
|
||
|
||
<h2 id="photos-httprequests">3.1. HTTP requests</h2>
|
||
<h3>GET /slideshow-features</h3>
|
||
|
||
<p>A client can fetch the list of available transitions for slideshows.
|
||
Then it can let the user pick one, before starting a slideshow. The
|
||
<code>Accept-Language</code> header is used to specify in which language the
|
||
transition names should be.</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>GET /slideshow-features HTTP/1.1
|
||
Accept-Language: English
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: cdda804c-33ae-4a0b-a5f2-f0e532fd5abd
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:41 GMT
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 6411
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>themes</key>
|
||
<array>
|
||
<dict>
|
||
<key>key</key>
|
||
<string>Reflections</string>
|
||
<key>name</key>
|
||
<string>Reflections</string>
|
||
</dict>
|
||
...
|
||
</array>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>PUT /photo</h3>
|
||
|
||
<p>Send a JPEG picture to the server. The following headers are supported:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>X-Apple-AssetKey</code></td>
|
||
<td>UUID for the picture</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>X-Apple-Transition</code></td>
|
||
<td>transition that should be used to show the picture</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>X-Apple-AssetAction</code></td>
|
||
<td>specify a caching operation</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example 1:</span> show a picture without any transition (for the first time)</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /photo HTTP/1.1
|
||
X-Apple-AssetKey: F92F9B91-954E-4D63-BB9A-EEC771ADE6E8
|
||
Content-Length: 462848
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
|
||
<JPEG DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:42 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 2:</span> show a picture using the dissolve transition</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /photo HTTP/1.1
|
||
X-Apple-AssetKey: F92F9B91-954E-4D63-BB9A-EEC771ADE6E8
|
||
X-Apple-Transition: Dissolve
|
||
Content-Length: 462848
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
|
||
<JPEG DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:42 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h3>PUT /slideshows/1</h3>
|
||
|
||
<p>Start or stop a slideshow session. When starting, slideshow settings
|
||
such as the slide duration and selected transition theme are
|
||
transmitted. The following parameters are sent in an XML property list:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>settings.slideDuration</td>
|
||
<td>integer</td>
|
||
<td>slide duration in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td>settings.theme</td>
|
||
<td>string</td>
|
||
<td>selected transition theme</td>
|
||
</tr>
|
||
<tr>
|
||
<td>state</td>
|
||
<td>string</td>
|
||
<td><code>playing</code> or <code>stopped</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> send slideshow settings</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /slideshows/1 HTTP/1.1
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 366
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 98a7b246-8e00-49a6-8765-db57165f5b67
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>settings</key>
|
||
<dict>
|
||
<key>slideDuration</key>
|
||
<integer>3</integer>
|
||
<key>theme</key>
|
||
<string>Classic</string>
|
||
</dict>
|
||
<key>state</key>
|
||
<string>playing</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 08 Mar 2012 16:30:01 GMT
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 181
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict/>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>POST /stop</h3>
|
||
|
||
<p>Stop a photo or slideshow session.</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /stop HTTP/1.1
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:55 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h2 id="photos-events">3.2. Events</h2>
|
||
<h3>Photo</h3>
|
||
|
||
<p>This event notifies a client that a photo session has ended. Then the
|
||
server can safely disconnect.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>category</td>
|
||
<td>string</td>
|
||
<td><code>photo</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>sessionID</td>
|
||
<td>integer</td>
|
||
<td>session ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td>state</td>
|
||
<td>string</td>
|
||
<td><code>stopped</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> stop photo session</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>POST /event HTTP/1.1
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 277
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>category</key>
|
||
<string>photo</string>
|
||
<key>sessionID</key>
|
||
<integer>38</integer>
|
||
<key>state</key>
|
||
<string>stopped</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h3>Slideshow</h3>
|
||
|
||
<p>Slideshow events are used to notify the server about the playback state.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>category</td>
|
||
<td>string</td>
|
||
<td><code>slideshow</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>lastAssetID</td>
|
||
<td>integer</td>
|
||
<td>last asset ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td>sessionID</td>
|
||
<td>integer</td>
|
||
<td>session ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td>state</td>
|
||
<td>string</td>
|
||
<td><code>loading</code>, <code>playing</code> or <code>stopped</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> slideshow is currently playing</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>POST /event HTTP/1.1
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 371
|
||
X-Apple-Session-ID: f1634b51-5cae-4384-ade5-54f4159a15f1
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>category</key>
|
||
<string>slideshow</string>
|
||
<key>lastAssetID</key>
|
||
<integer>5</integer>
|
||
<key>sessionID</key>
|
||
<integer>4</integer>
|
||
<key>state</key>
|
||
<string>playing</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h2 id="photos-photocaching">3.3. Photo Caching</h2>
|
||
<p>AirPlay supports preloading picture data to improve transition latency.
|
||
This works by preloading a few pictures (most likely the ones before and
|
||
after the current picture) just after displaying one.</p>
|
||
|
||
<p>Preloading is achieved using the <code>cacheOnly</code> asset action. Upon
|
||
receiving this request, a server stores the picture in its cache. Later,
|
||
a client can request the display of this picture using the
|
||
<code>displayCached</code> asset action and the same asset key. This is much faster
|
||
than a full picture upload because no additional data is transmitted.</p>
|
||
|
||
<p>When asked for a picture which is no longer in the cache, a server
|
||
replies with an HTTP 412 error code (Precondition Failed).</p>
|
||
|
||
<p><span class="ex">Example 1:</span> cache a picture for future display</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /photo HTTP/1.1
|
||
X-Apple-AssetAction: cacheOnly
|
||
X-Apple-AssetKey: B0DDE2C0-6FDD-48F8-9E5B-29CE0618DF5B
|
||
Content-Length: 462848
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
|
||
<JPEG DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:45 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 2:</span> show a cached picture</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /photo HTTP/1.1
|
||
X-Apple-AssetAction: displayCached
|
||
X-Apple-AssetKey: B0DDE2C0-6FDD-48F8-9E5B-29CE0618DF5B
|
||
X-Apple-Transition: Dissolve
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:45 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h2 id="photos-slideshows">3.4. Slideshows</h2>
|
||
<p>Slideshows are using the reverse HTTP connection for asynchronous
|
||
loading of pictures. Three connections are performed in parallel. The
|
||
<code>X-Apple-Purpose</code> header is set to <code>slideshow</code>. A <code>GET</code> request to the
|
||
<code>/slideshows/1/assets/1</code> location is issued to fetch a new picture from
|
||
the AirPlay client. A binary property list with the following parameters
|
||
is expected as reply:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>data</td>
|
||
<td>data</td>
|
||
<td>JPEG picture</td>
|
||
</tr>
|
||
<tr>
|
||
<td>info.id</td>
|
||
<td>integer</td>
|
||
<td>asset ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td>info.key</td>
|
||
<td>integer</td>
|
||
<td>1</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> fetch a new picture</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>GET /slideshows/1/assets/1 HTTP/1.1
|
||
Content-Length: 0
|
||
Accept: application/x-apple-binary-plist
|
||
X-Apple-Session-ID: 98a7b246-8e00-49a6-8765-db57165f5b67
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 58932
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>data</key>
|
||
<data>
|
||
...
|
||
</data>
|
||
<key>info</key>
|
||
<dict>
|
||
<key>id</key>
|
||
<integer>1</integer>
|
||
<key>key</key>
|
||
<string>1</string>
|
||
</dict>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h1 id="video">4. Video</h1>
|
||
<p>In order to play a video on an AirPlay server, HTTP requests are used to
|
||
send a video URL, perform scrubbing, change the playback rate and update
|
||
the timeline.</p>
|
||
|
||
<h2 id="video-httprequests">4.1. HTTP requests</h2>
|
||
<h3>GET /server-info</h3>
|
||
|
||
<p>Fetch general informations about the AirPlay server. These informations
|
||
are returned as an XML property list, with the following properties:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>deviceid</td>
|
||
<td>string</td>
|
||
<td>58:55:CA:1A:E2:88</td>
|
||
<td>MAC address</td>
|
||
</tr>
|
||
<tr>
|
||
<td>features</td>
|
||
<td>integer</td>
|
||
<td>14839</td>
|
||
<td>0x39f7</td>
|
||
</tr>
|
||
<tr>
|
||
<td>model</td>
|
||
<td>string</td>
|
||
<td>AppleTV2,1</td>
|
||
<td>device model</td>
|
||
</tr>
|
||
<tr>
|
||
<td>protovers</td>
|
||
<td>string</td>
|
||
<td>1.0</td>
|
||
<td>protocol version</td>
|
||
</tr>
|
||
<tr>
|
||
<td>srcvers</td>
|
||
<td>string</td>
|
||
<td>120.2</td>
|
||
<td>server version</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The <code>model</code>, <code>deviceid</code>, <code>srcvers</code> and <code>features</code> properties are the
|
||
same as broadcasted by the mDNS AirPlay service.</p>
|
||
|
||
<p><span class="ex">Example:</span> fetch server informations</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>GET /server-info HTTP/1.1
|
||
X-Apple-Device-ID: 0xdc2b61a0ce79
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Thu, 23 Feb 2012 17:33:41 GMT
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 427
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>deviceid</key>
|
||
<string>58:55:CA:1A:E2:88</string>
|
||
<key>features</key>
|
||
<integer>14839</integer>
|
||
<key>model</key>
|
||
<string>AppleTV2,1</string>
|
||
<key>protovers</key>
|
||
<string>1.0</string>
|
||
<key>srcvers</key>
|
||
<string>120.2</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>POST /play</h3>
|
||
|
||
<p>Start video playback. The body contains the following parameters:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>Content-Location</code></td>
|
||
<td>URL</td>
|
||
<td>URL for the video</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>Start-Position</code></td>
|
||
<td>float</td>
|
||
<td>starting position between 0 and 1</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>MP4 movies are supported using progressive download. <a href="http://tools.ietf.org/id/draft-pantos-http-live-streaming-07.txt">HTTP Live Streaming</a>
|
||
might be supported as well, as indicated by the <code>VideoHTTPLiveStreams</code>
|
||
feature flag. The relative starting position, a float value between 0
|
||
(beginning) and 1 (end) is used to start playing a video at the exact same
|
||
position as it was on the client.</p>
|
||
|
||
<p>A binary property list can also be used instead of text parameters, with
|
||
content type <code>application/x-apple-binary-plist</code>.</p>
|
||
|
||
<p><span class="ex">Example 1:</span> video playback from iTunes</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /play HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 163
|
||
Content-Type: text/parameters
|
||
|
||
Content-Location: http://192.168.1.18:3689/airplay.mp4?database-spec='dmap.persistentid:0x63b5e5c0c201542e'&item-spec='dmap.itemid:0x21d'
|
||
Start-Position: 0.174051
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 18:08:25 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 2:</span> video playback from iPhone</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /play HTTP/1.1
|
||
X-Transmit-Date: 2012-03-16T14:20:39.656533Z
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 491
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 368e90a4-5de6-4196-9e58-9917bdd4ffd7
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>Content-Location</key>
|
||
<string>http://redirector.c.youtube.com/videoplayback?...</string>
|
||
<key>Start-Position</key>
|
||
<real>0.024613151326775551</real>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
</code></pre>
|
||
|
||
<h3>POST /scrub</h3>
|
||
|
||
<p>Seek at an arbitrary location in the video. The <code>position</code> argument is a
|
||
float value representing the location in seconds.</p>
|
||
|
||
<p><span class="ex">Example:</span> seek to about 20 seconds</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /scrub?position=20.097000 HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 18:08:42 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h3>POST /rate</h3>
|
||
|
||
<p>Change the playback rate. The <code>value</code> argument is a float value
|
||
representing the playback rate: 0 is paused, 1 is playing at the normal
|
||
speed.</p>
|
||
|
||
<p><span class="ex">Example:</span> pause playback</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /rate?value=0.000000 HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 18:08:37 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h3>POST /stop</h3>
|
||
|
||
<p>Stop playback.</p>
|
||
|
||
<p><span class="ex">Example:</span> stop playback</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /stop HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 18:09:06 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h3>GET /scrub</h3>
|
||
|
||
<p>Retrieve the current playback position. This can be called repeatedly to
|
||
update a timeline on the client. The following parameters are returned:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>duration</code></td>
|
||
<td>float</td>
|
||
<td>duration in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>position</code></td>
|
||
<td>float</td>
|
||
<td>position in seconds</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> fetch current playback progress</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>GET /scrub HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 18:08:31 GMT
|
||
Content-Type: text/parameters
|
||
Content-Length: 40
|
||
|
||
duration: 83.124794
|
||
position: 14.467000
|
||
</code></pre>
|
||
|
||
<h3>GET /playback-info</h3>
|
||
|
||
<p>Retrieve playback informations such as position, duration, rate,
|
||
buffering status and more. An XML property list is returned with the
|
||
following parameters:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td><code>duration</code></td>
|
||
<td>real</td>
|
||
<td>playback duration in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>position</code></td>
|
||
<td>real</td>
|
||
<td>playback position in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>rate</code></td>
|
||
<td>real</td>
|
||
<td>playback rate</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>readyToPlay</code></td>
|
||
<td>boolean</td>
|
||
<td>ready to play</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playbackBufferEmpty</code></td>
|
||
<td>boolean</td>
|
||
<td>buffer empty</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playbackBufferFull</code></td>
|
||
<td>boolean</td>
|
||
<td>buffer full</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playbackLikelyToKeepUp</code></td>
|
||
<td>boolean</td>
|
||
<td>playback likely to keep up</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>loadedTimeRanges</code></td>
|
||
<td>array</td>
|
||
<td>array of loaded time ranges</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>seekableTimeRanges</code></td>
|
||
<td>array</td>
|
||
<td>array of seekable time ranges</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Ranges are defined as dictionaries with the following keys:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>start</code></td>
|
||
<td>real</td>
|
||
<td>range start time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>duration</code></td>
|
||
<td>real</td>
|
||
<td>range duration in seconds</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> get playback info</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>GET /playback-info HTTP/1.1
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 24b3fd94-1b6d-42b1-89a3-47108bfbac89
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Fri, 16 Mar 2012 15:31:42 GMT
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 801
|
||
X-Transmit-Date: 2012-03-16T15:31:42.607066Z
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>duration</key> <real>1801</real>
|
||
<key>loadedTimeRanges</key>
|
||
<array>
|
||
<dict>
|
||
<key>duration</key> <real>51.541130402</real>
|
||
<key>start</key> <real>18.118717650000001</real>
|
||
</dict>
|
||
</array>
|
||
<key>playbackBufferEmpty</key> <true/>
|
||
<key>playbackBufferFull</key> <false/>
|
||
<key>playbackLikelyToKeepUp</key> <true/>
|
||
<key>position</key> <real>18.043869775000001</real>
|
||
<key>rate</key> <real>1</real>
|
||
<key>readyToPlay</key> <true/>
|
||
<key>seekableTimeRanges</key>
|
||
<array>
|
||
<dict>
|
||
<key>duration</key>
|
||
<real>1801</real>
|
||
<key>start</key>
|
||
<real>0.0</real>
|
||
</dict>
|
||
</array>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>PUT /setProperty</h3>
|
||
|
||
<p>Set playback property. The property name is sent as query argument. The
|
||
following properties are defined:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>argument</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>forwardEndTime</code></td>
|
||
<td>forward end time</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>reverseEndTime</code></td>
|
||
<td>reverse end time</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> set forward end time</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>PUT /setProperty?forwardEndTime HTTP/1.1
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 96
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 24b3fd94-1b6d-42b1-89a3-47108bfbac89
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>value</key>
|
||
<dict>
|
||
<key>epoch</key> <integer>0</integer>
|
||
<key>flags</key> <integer>0</integer>
|
||
<key>timescale</key> <integer>0</integer>
|
||
<key>value</key> <integer>0</integer>
|
||
</dict>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Fri, 16 Mar 2012 15:23:11 GMT
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 58
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>errorCode</key>
|
||
<integer>0</integer>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>GET /getProperty</h3>
|
||
|
||
<p>Get playback property. The property name is sent as query argument. The
|
||
following properties are defined:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>argument</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>playbackAccessLog</code></td>
|
||
<td>playback access log</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playbackErrorLog</code></td>
|
||
<td>playback error log</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> get playback access log</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /getProperty?playbackAccessLog HTTP/1.1
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 0
|
||
User-Agent: MediaControl/1.0
|
||
X-Apple-Session-ID: 24b3fd94-1b6d-42b1-89a3-47108bfbac89
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Fri, 16 Mar 2012 15:31:42 GMT
|
||
Content-Type: application/x-apple-binary-plist
|
||
Content-Length: 530
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>errorCode</key>
|
||
<integer>0</integer>
|
||
<key>value</key>
|
||
<array>
|
||
<dict>
|
||
<key>bytes</key> <integer>1818336</integer>
|
||
<key>c-duration-downloaded</key> <real>70</real>
|
||
<key>c-duration-watched</key> <real>18.154102027416229</real>
|
||
<key>c-frames-dropped</key> <integer>0</integer>
|
||
<key>c-observed-bitrate</key> <real>14598047.302367469</real>
|
||
<key>c-overdue</key> <integer>0</integer>
|
||
<key>c-stalls</key> <integer>0</integer>
|
||
<key>c-start-time</key> <real>0.0</real>
|
||
<key>c-startup-time</key> <real>0.27732497453689575</real>
|
||
<key>cs-guid</key> <string>B475F105-78FD-4200-96BC-148BAB6DAC11</string>
|
||
<key>date</key> <date>2012-03-16T15:31:24Z</date>
|
||
<key>s-ip</key> <string>213.152.6.89</string>
|
||
<key>s-ip-changes</key> <integer>0</integer>
|
||
<key>sc-count</key> <integer>7</integer>
|
||
<key>uri</key> <string>http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8</string>
|
||
</dict>
|
||
</array>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h2 id="video-events">4.2. Events</h2>
|
||
<p>This event is used to send the playback state to the client:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>category</td>
|
||
<td>string</td>
|
||
<td><code>video</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>sessionID</td>
|
||
<td>integer</td>
|
||
<td>session id</td>
|
||
</tr>
|
||
<tr>
|
||
<td>state</td>
|
||
<td>string</td>
|
||
<td><code>loading</code>, <code>playing</code>, <code>paused</code> or <code>stopped</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> notify the client that video playback is paused</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>POST /event HTTP/1.1
|
||
Content-Type: application/x-apple-plist
|
||
Content-Length: 321
|
||
X-Apple-Session-ID: 00000000-0000-0000-0000-000000000000
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>category</key>
|
||
<string>video</string>
|
||
<key>sessionID</key>
|
||
<integer>13</integer>
|
||
<key>state</key>
|
||
<string>paused</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Content-Length: 0
|
||
Date: Mon, 08 Mar 2012 18:07:43 GMT
|
||
</code></pre>
|
||
|
||
<h1 id="audio">5. Audio</h1>
|
||
<p>Audio streaming is supported using the <em>RTSP</em> protocol (<a href="http://www.ietf.org/rfc/rfc2326.txt">RFC 2326</a>).</p>
|
||
|
||
<h2 id="audio-rtsprequests">5.1. RTSP requests</h2>
|
||
<h3>OPTIONS</h3>
|
||
|
||
<p>The <code>OPTIONS</code> request asks the RTSP server for its supported methods.
|
||
Apple TV supports the following methods: <code>ANNOUNCE</code>, <code>SETUP</code>, <code>RECORD</code>,
|
||
<code>PAUSE</code>, <code>FLUSH</code>, <code>TEARDOWN</code>, <code>OPTIONS</code>, <code>GET_PARAMETER</code>, <code>SET_PARAMETER</code>,
|
||
<code>POST</code> and <code>GET</code>.</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>OPTIONS * RTSP/1.0
|
||
CSeq: 3
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS,
|
||
GET_PARAMETER, SET_PARAMETER, POST, GET
|
||
Server: AirTunes/130.14
|
||
CSeq: 3
|
||
</code></pre>
|
||
|
||
<h3>ANNOUNCE</h3>
|
||
|
||
<p>The <code>ANNOUNCE</code> request tells the RTSP server about stream properties using
|
||
SDP (<a href="http://www.ietf.org/rfc/rfc4566.txt">RFC 4566</a>). Codec informations and encryption keys are of particular
|
||
interest.</p>
|
||
|
||
<p><span class="ex">Example 1:</span> <code>ANNOUNCE</code> for <em>Apple Lossless</em> audio from iTunes</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 3
|
||
Content-Type: application/sdp
|
||
Content-Length: 348
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
|
||
v=0
|
||
o=iTunes 3413821438 0 IN IP4 fe80::217:f2ff:fe0f:e0f6
|
||
s=iTunes
|
||
c=IN IP4 fe80::5a55:caff:fe1a:e187
|
||
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=fpaeskey:RlBMWQECAQAAAAA8AAAAAPFOnNe+zWb5/n4L5KZkE2AAAAAQlDx69reTdwHF9LaNmhiRURTAbcL4brYAceAkZ49YirXm62N4
|
||
a=aesiv:5b+YZi9Ikb845BmNhaVo+Q
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 3
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 2:</span> <code>ANNOUNCE</code> for <em>AAC</em> audio from an iOS device</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://192.168.1.45/2699324803567405959 RTSP/1.0
|
||
X-Apple-Device-ID: 0xa4d1d2800b68
|
||
CSeq: 16
|
||
DACP-ID: 14413BE4996FEA4D
|
||
Active-Remote: 2543110914
|
||
Content-Type: application/sdp
|
||
Content-Length: 331
|
||
|
||
v=0
|
||
o=AirTunes 2699324803567405959 0 IN IP4 192.168.1.5
|
||
s=AirTunes
|
||
c=IN IP4 192.168.1.5
|
||
t=0 0
|
||
m=audio 0 RTP/AVP 96
|
||
a=rtpmap:96 mpeg4-generic/44100/2
|
||
a=fmtp:96
|
||
a=fpaeskey:RlBMWQECAQAAAAA8AAAAAOG6c4aMdLkXAX+lbjp7EhgAAAAQeX5uqGyYkBmJX+gd5ANEr+amI8urqFmvcNo87pR0BXGJ4eLf
|
||
a=aesiv:VZTaHn4wSJ84Jjzlb94m0Q==
|
||
a=min-latency:11025
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 16
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 3:</span> <code>ANNOUNCE</code> for <em>AAC-ELD</em> audio and <em>H.264</em> video from an iOS device</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://192.168.1.45/846700446248110360 RTSP/1.0
|
||
X-Apple-Device-ID: 0xa4d1d2800b68
|
||
CSeq: 27
|
||
DACP-ID: 14413BE4996FEA4D
|
||
Active-Remote: 2543110914
|
||
Content-Type: application/sdp
|
||
Content-Length: 415
|
||
|
||
v=0
|
||
o=AirTunes 846700446248110360 0 IN IP4 192.168.1.5
|
||
s=AirTunes
|
||
c=IN IP4 192.168.1.5
|
||
t=0 0
|
||
m=audio 0 RTP/AVP 96
|
||
a=rtpmap:96 mpeg4-generic/44100/2
|
||
a=fmtp:96 mode=AAC-eld; constantDuration=480
|
||
a=fpaeskey:RlBMWQECAQAAAAA8AAAAAKKp+t27A+686xfviEphhw8AAAAQE/3LSqv9MHgnEKxkbKh1buE9+ylKg0YuqcyAC7fT0EqJNtdq
|
||
a=aesiv:i/a3nUKYNDSIPP2fC+UKGQ==
|
||
a=min-latency:4410
|
||
m=video 0 RTP/AVP 97
|
||
a=rtpmap:97 H264
|
||
a=fmtp:97
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 27
|
||
</code></pre>
|
||
|
||
<h3>SETUP</h3>
|
||
|
||
<p>The <code>SETUP</code> request initializes a record session. It sends all the
|
||
necessary transport informations. Three UDP channels are setup:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>channel</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>server</td>
|
||
<td>audio data</td>
|
||
</tr>
|
||
<tr>
|
||
<td>control</td>
|
||
<td>sync and retransmit requests</td>
|
||
</tr>
|
||
<tr>
|
||
<td>timing</td>
|
||
<td>master clock sync</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> setup a record session</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>SETUP rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 4
|
||
Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=6001;timing_port=6002
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Transport: RTP/AVP/UDP;unicast;mode=record;server_port=53561;control_port=63379;timing_port=50607
|
||
Session: 1
|
||
Audio-Jack-Status: connected
|
||
Server: AirTunes/130.14
|
||
CSeq: 4
|
||
</code></pre>
|
||
|
||
<h3>RECORD</h3>
|
||
|
||
<p>The <code>RECORD</code> request starts the audio streaming. The <code>RTP-Info</code> header
|
||
contains the following parameters:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>name</th>
|
||
<th>size</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>seq</code></td>
|
||
<td>16-bit</td>
|
||
<td>initial RTP sequence number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>rtptime</code></td>
|
||
<td>32-bit</td>
|
||
<td>initial RTP timestamp</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> start audio stream</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>RECORD rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 5
|
||
Session: 1
|
||
Range: npt=0-
|
||
RTP-Info: seq=20857;rtptime=1146549156
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Audio-Latency: 2205
|
||
Server: AirTunes/130.14
|
||
CSeq: 5
|
||
</code></pre>
|
||
|
||
<h3>FLUSH</h3>
|
||
|
||
<p>The <code>FLUSH</code> request stops the streaming.</p>
|
||
|
||
<p><span class="ex">Example:</span> pause the audio stream</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>FLUSH rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 31
|
||
Session: 1
|
||
RTP-Info: seq=25009;rtptime=1148010660
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
RTP-Info: rtptime=1147914212
|
||
Server: AirTunes/130.14
|
||
CSeq: 31
|
||
</code></pre>
|
||
|
||
<h3>TEARDOWN</h3>
|
||
|
||
<p>The <code>TEARDOWN</code> request ends the RTSP session.</p>
|
||
|
||
<p><span class="ex">Example:</span> close session 1</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>TEARDOWN rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 32
|
||
Session: 1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 32
|
||
</code></pre>
|
||
|
||
<h2 id="audio-rtpstreams">5.2. RTP Streams</h2>
|
||
<p>Audio packets are fully RTP compliant. Control and timing packets,
|
||
however, do not seem to be fully compliant with the RTP standard.</p>
|
||
|
||
<p>The following payload types are defined:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>payload type</th>
|
||
<th>port</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>82</td>
|
||
<td><code>timing_port</code></td>
|
||
<td>timing request</td>
|
||
</tr>
|
||
<tr>
|
||
<td>83</td>
|
||
<td><code>timing_port</code></td>
|
||
<td>timing reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td>84</td>
|
||
<td><code>control_port</code></td>
|
||
<td>time sync</td>
|
||
</tr>
|
||
<tr>
|
||
<td>85</td>
|
||
<td><code>control_port</code></td>
|
||
<td>retransmit request</td>
|
||
</tr>
|
||
<tr>
|
||
<td>86</td>
|
||
<td><code>control_port</code></td>
|
||
<td>retransmit reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td>96</td>
|
||
<td><code>server_port</code></td>
|
||
<td>audio data</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Audio packets</h3>
|
||
|
||
<p>Audio data is sent using the <code>DynamicRTP-Type-96</code> payload type. The
|
||
<code>Marker</code> bit is set on the first packet sent after <code>RECORD</code> or <code>FLUSH</code>
|
||
requests. The RTP payload contains optionally encrypted audio data.</p>
|
||
|
||
<p><span class="ex">Example:</span> encrypted audio packet</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>0000 80 e0 b1 91 f7 79 16 c2 e8 bb 6b 2c bb 5c 8e 51
|
||
0010 aa 7c d2 96 00 c3 fd 60 eb ae 6e 41 31 38 fe ae
|
||
....
|
||
03e0 cb 1c 73 bf e7 05 93 30 fa 85 7f 32 77 8d a8 97
|
||
03f0 a0 c7 c8 78 7b e5 81 a1 4f b4 3e a3 43 db 7c
|
||
|
||
Real-Time Transport Protocol
|
||
10.. .... = Version: RFC 1889 Version (2)
|
||
..0. .... = Padding: False
|
||
...0 .... = Extension: False
|
||
.... 0000 = Contributing source identifiers count: 0
|
||
1... .... = Marker: True
|
||
Payload type: DynamicRTP-Type-96 (96)
|
||
Sequence number: 45457
|
||
Timestamp: 4151908034
|
||
Synchronization Source identifier: 0xe8bb6b2c (3904596780)
|
||
Payload: bb5c8e51aa7cd29600c3fd60ebae6e413138feae909b44f1...
|
||
</code></pre>
|
||
|
||
<h3>Sync packets</h3>
|
||
|
||
<p>Sync packets are sent once per second to the control port. They are used to
|
||
correlate the RTP timestamps currently used in the audio stream to the NTP time
|
||
used for clock synchronization. Payload type is 84, the <code>Marker</code> bit is always
|
||
set and the <code>Extension</code> bit is set on the first packet after <code>RECORD</code> or
|
||
<code>FLUSH</code> requests. The <code>SSRC</code> field is not included in the RTP header.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>bytes</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>RTP header without <code>SSRC</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>current NTP time</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>RTP timestamp for the next audio packet</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> sync packet</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>0000 80 d4 00 04 c7 cd 11 a8 83 ab 1c 49 2f e4 22 e2
|
||
0010 c7 ce 3f 1f
|
||
|
||
Real-Time Transport Protocol
|
||
10.. .... = Version: RFC 1889 Version (2)
|
||
..0. .... = Padding: False
|
||
...0 .... = Extension: False
|
||
.... 0000 = Contributing source identifiers count: 0
|
||
1... .... = Marker: True
|
||
Payload type: Unassigned (84)
|
||
Sequence number: 4
|
||
Timestamp: 3352105384
|
||
Synchronization Source identifier: 0x83ab1c49 (2209029193)
|
||
Payload: 2fe422e2c7ce3f1f
|
||
</code></pre>
|
||
|
||
<h3>Retransmit packets</h3>
|
||
|
||
<p>AirTunes supports resending audio packets which have been lost. Payload
|
||
type is 85 for retransmit queries, the <code>Marker</code> bit is always set and the
|
||
<code>SSRC</code> field is not included in the RTP header.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>bytes</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>RTP header without <code>SSRC</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>sequence number for the first lost packet</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>number of lost packets</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Retransmit replies have payload type 86, with a full audio RTP packet
|
||
after the sequence number.</p>
|
||
|
||
<h3>Timing packets</h3>
|
||
|
||
<p>Timing packets are used to synchronize a master clock for audio. This is
|
||
useful for clock recovery and precise synchronization of several devices
|
||
playing the same audio stream.</p>
|
||
|
||
<p>Timing packets are sent at 3 second intervals. They always have the
|
||
<code>Marker</code> bit set, and payload type 82 for queries and 83 for replies.
|
||
The <code>SSRC</code> field is not included in the RTP header, so it takes only 8
|
||
bytes, followed by three <em>NTP</em> timestamps:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>bytes</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>RTP header without <code>SSRC</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>origin timestamp</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>receive timestamp</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>transmit timestamp</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> timing query/reply</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>0000 80 d2 00 07 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0010 00 00 00 00 00 00 00 00 83 c1 17 cc af ba 9b 32
|
||
|
||
Real-Time Transport Protocol
|
||
10.. .... = Version: RFC 1889 Version (2)
|
||
..0. .... = Padding: False
|
||
...0 .... = Extension: False
|
||
.... 0000 = Contributing source identifiers count: 0
|
||
1... .... = Marker: True
|
||
Payload type: Unassigned (82)
|
||
Sequence number: 7
|
||
Timestamp: 0
|
||
Synchronization Source identifier: 0x00000000 (0)
|
||
Payload: 00000000000000000000000083c117ccafba9b32
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>0000 80 d3 00 07 00 00 00 00 83 c1 17 cc af ba 9b 32
|
||
0010 83 c1 17 cc b0 12 ce b6 83 c1 17 cc b0 14 10 47
|
||
|
||
Real-Time Transport Protocol
|
||
10.. .... = Version: RFC 1889 Version (2)
|
||
..0. .... = Padding: False
|
||
...0 .... = Extension: False
|
||
.... 0000 = Contributing source identifiers count: 0
|
||
1... .... = Marker: True
|
||
Payload type: Unassigned (83)
|
||
Sequence number: 7
|
||
Timestamp: 0
|
||
Synchronization Source identifier: 0x83c117cc (2210469836)
|
||
Payload: afba9b3283c117ccb012ceb683c117ccb0141047
|
||
</code></pre>
|
||
|
||
<h2 id="audio-volumecontrol">5.3. Volume Control</h2>
|
||
<p>Audio volume can be changed using a <code>SET_PARAMETER</code> request. The volume
|
||
is a float value representing the audio attenuation in dB. A value of
|
||
–144 means the audio is muted. Then it goes from –30 to 0.</p>
|
||
|
||
<p><span class="ex">Example:</span> set audio volume</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>SET_PARAMETER rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 6
|
||
Session: 1
|
||
Content-Type: text/parameters
|
||
Content-Length: 20
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
|
||
volume: -11.123877
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 6
|
||
</code></pre>
|
||
|
||
<h2 id="audio-metadata">5.4. Metadata</h2>
|
||
<p>Metadata for the current track are sent using <code>SET_PARAMETER</code> requests.
|
||
This allows the Apple TV to show the track name, artist, album, cover
|
||
artwork and timeline. The <code>RTP-Info</code> header contains a <code>rtptime</code> parameter
|
||
with the RTP timestamp corresponding to the time from which the metadata
|
||
is valid.</p>
|
||
|
||
<h3>Track Informations</h3>
|
||
|
||
<p>Informations about the current track are sent in the <em>DAAP</em> (<a href="http://tapjam.net/daap/">Digital
|
||
Audio Access Protocol</a>) format, with <code>application/x-dmap-tagged</code> content
|
||
type.</p>
|
||
|
||
<p>The following DAAP attributes are displayed on Apple TV:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>attribute</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>dmap.itemname</code></td>
|
||
<td>track name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>daap.songartist</code></td>
|
||
<td>artist</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>daap.songalbum</code></td>
|
||
<td>album</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example:</span> send track informations</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>SET_PARAMETER rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 8
|
||
Session: 1
|
||
Content-Type: application/x-dmap-tagged
|
||
Content-Length: 3242
|
||
RTP-Info: rtptime=1146549156
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
|
||
<DMAP DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 8
|
||
</code></pre>
|
||
|
||
<h3>Cover Artwork</h3>
|
||
|
||
<p>Artworks are sent as <em>JPEG</em> pictures, with <code>image/jpeg</code> content type.</p>
|
||
|
||
<p><span class="ex">Example:</span> send cover artwork</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>SET_PARAMETER rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 9
|
||
Session: 1
|
||
Content-Type: image/jpeg
|
||
Content-Length: 34616
|
||
RTP-Info: rtptime=1146549156
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
|
||
<JPEG DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 9
|
||
</code></pre>
|
||
|
||
<h3>Playback Progress</h3>
|
||
|
||
<p>Playback progress is sent as <code>text/parameters</code>, with a <code>progress</code>
|
||
parameter representing three absolute RTP timestamps values:
|
||
<code>start</code>/<code>curr</code>/<code>end</code>.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>timestamp</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>start</code></td>
|
||
<td>beginning of the current track</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>curr</code></td>
|
||
<td>current playback position</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>end</code></td>
|
||
<td>end of the current track</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>The relative position and track duration can be computed as follows:</p>
|
||
|
||
<ul>
|
||
<li><code>position = rtptime_to_sec(curr - start)</code></li>
|
||
<li><code>duration = rtptime_to_sec(end - start)</code></li>
|
||
</ul>
|
||
|
||
<p><span class="ex">Example:</span> send playback progress</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>SET_PARAMETER rtsp://fe80::217:f2ff:fe0f:e0f6/3413821438 RTSP/1.0
|
||
CSeq: 10
|
||
Session: 1
|
||
Content-Type: text/parameters
|
||
Content-Length: 44
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3)
|
||
AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 1986535575
|
||
|
||
progress: 1146221540/1146549156/1195701740
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 10
|
||
</code></pre>
|
||
|
||
<h2 id="audio-airportexpressauthentication">5.5. AirPort Express Authentication</h2>
|
||
<p>Sending audio data to the AirPort Express requires a <em>RSA</em> based
|
||
authentication. All binary data are encoded using <em>Base64</em> (<a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a>)
|
||
without padding.</p>
|
||
|
||
<h3>Client side</h3>
|
||
|
||
<ul>
|
||
<li><p>In the <code>ANNOUNCE</code> request, the client sends a 128-bit random number in
|
||
the <code>Apple-Challenge</code> header.</p></li>
|
||
<li><p>A 128-bit <em>AES</em> key is generated, encrypted with the RSA public key
|
||
using the <em>OAEP</em> encryption scheme, and sent along with an
|
||
initialization vector in the <code>rsaaeskey</code> and <code>aesiv</code> SDP attributes.</p></li>
|
||
</ul>
|
||
|
||
<h3>Server side</h3>
|
||
|
||
<ul>
|
||
<li><p>The AirPort Express decrypts the AES key with its RSA private key, it
|
||
will be used to decrypt the audio payload.</p></li>
|
||
<li><p>The AirPort Express signs the <code>Apple-Challenge</code> number with its RSA
|
||
private key using the <em>PKCS#1</em> signature scheme and send the result in
|
||
the <code>Apple-Response</code> header.</p></li>
|
||
</ul>
|
||
|
||
<h3>Client side</h3>
|
||
|
||
<ul>
|
||
<li>The client decrypts the <code>Apple-Response</code> value with the RSA public key,
|
||
and checks that it is the same random number it has previously
|
||
generated.</li>
|
||
</ul>
|
||
|
||
<p><span class="ex">Example:</span> AirPort Express challenge/response</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://10.0.1.101/3172942895 RTSP/1.0
|
||
CSeq: 1
|
||
Content-Type: application/sdp
|
||
Content-Length: 567
|
||
User-Agent: iTunes/4.6 (Windows; N)
|
||
Client-Instance: 9FF35780A8BC8D2B
|
||
Apple-Challenge: 09KF45soMYmvj6dpsUGiIg
|
||
|
||
v=0
|
||
o=iTunes 3172942895 0 IN IP4 10.0.1.101
|
||
s=iTunes
|
||
c=IN IP4 10.0.1.103
|
||
t=0 0
|
||
m=audio 0 RTP/AVP 96
|
||
a=rtpmap:96 AppleLossless
|
||
a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100
|
||
a=rsaaeskey:5QYIqmdZGTONY5SHjEJrqAhaa0W9wzDC5i6q221mdGZJ5ubO6Kg
|
||
yhC6U83wpY87TFdPRdfPQl2kVC7+Uefmx1bXdIUo07ZcJsqMbgtje4w2JQw0b
|
||
Uw2BlzNPmVGQOxfdpGc3LXZzNE0jI1D4conUEiW6rrzikXBhk7Y/i2naw13ayy
|
||
xaSwtkiJ0ltBQGYGErbV2tx43QSNj7O0JIG9GrF2GZZ6/UHo4VH+ZXgQ4NZvP/
|
||
QXPCsLutZsvusFDzIEq7TN1fveINOiwrzlN+bckEixvhXlvoQTWE2tjbmQYhMvO
|
||
FIly5gNbZiXi0l5AdolX4jDC2vndFHqWDks/3sPikNg
|
||
a=aesiv:zcZmAZtqh7uGcEwPXk0QeA
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
CSeq: 1
|
||
Apple-Response: u+msU8Cc7KBrVPjI/Ir8fOL8+C5D3Jsw1+acaW3MNTndrTQAeb/a
|
||
5m10UVBX6wb/DYQGY+b28ksSwBjN0nFOk4Y2cODEf83FAh7B
|
||
mkLpmpkpplp7zVXQ+Z9DcB6gC60ZsS3t98aoR7tSzVLKZNgi2X2sC+vGsz
|
||
utQxX03HK008VjcdngHv3g1p2knoETd07T6eVfZCmPqp6Ga7Dj8VIIj/GEP3
|
||
AjjDx3lJnQBXUDmxM484YXLXZjWFXCiY8GJt6whjf7/2c3rIoT3Z7PQpEvPmM
|
||
1MXU9cv4NL59Y/q0OAVQ38foOz7eGAhfvjOsCnHU25aik7/7ToIYt1tyVtap/kA
|
||
Audio-Jack-Status: connected; type=analog
|
||
</code></pre>
|
||
|
||
<h2 id="audio-remotecontrol">5.6. Remote Control</h2>
|
||
<p>Audio speakers can send commands to the AirPlay client to change the
|
||
current track, pause and resume playback, shuffle the playlist, and
|
||
more. This uses a subset of <em>DACP</em> (<a href="http://en.wikipedia.org/wiki/Digital_Audio_Control_Protocol">Digital Audio Control Protocol</a>).
|
||
An AirPlay client advertises this capability by including a <code>DACP-ID</code>
|
||
header in its RTSP requests, with a 64-bit ID for the DACP server. An
|
||
<code>Active-Remote</code> header is included as well, serving as an authentication
|
||
token.</p>
|
||
|
||
<p>The AirPlay server needs to browse the mDNS <code>_dacp._tcp</code> services for a
|
||
matching DACP server. Server names look like <code>iTunes_Ctrl_$ID</code>.</p>
|
||
|
||
<p class="caption">DACP service from iTunes</p>
|
||
|
||
<pre><code>name: iTunes_Ctrl_56B29BB6CB904862
|
||
type: _dacp._tcp
|
||
port: 3689
|
||
txt:
|
||
txtvers=1
|
||
Ver=131075
|
||
DbId=63B5E5C0C201542E
|
||
OSsi=0x1F5
|
||
</code></pre>
|
||
|
||
<p>Once the DACP server has been identified, HTTP requests can be sent to the
|
||
corresponding service port. The <code>Active-Remote</code> header must be included in
|
||
these requests, so no additional pairing is required. The location for
|
||
remote control commands is <code>/ctrl-int/1/$CMD</code>. The following commands are
|
||
available:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>command</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>beginff</code></td>
|
||
<td>begin fast forward</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>beginrew</code></td>
|
||
<td>begin rewind</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>mutetoggle</code></td>
|
||
<td>toggle mute status</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>nextitem</code></td>
|
||
<td>play next item in playlist</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>previtem</code></td>
|
||
<td>play previous item in playlist</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>pause</code></td>
|
||
<td>pause playback</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playpause</code></td>
|
||
<td>toggle between play and pause</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>play</code></td>
|
||
<td>start playback</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>stop</code></td>
|
||
<td>stop playback</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>playresume</code></td>
|
||
<td>play after fast forward or rewind</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>shuffle_songs</code></td>
|
||
<td>shuffle playlist</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>volumedown</code></td>
|
||
<td>turn audio volume down</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>volumeup</code></td>
|
||
<td>turn audio volume up</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><span class="ex">Example:</span> send a pause command</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>GET /ctrl-int/1/pause HTTP/1.1
|
||
Host: starlight.local.
|
||
Active-Remote: 1986535575
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>HTTP/1.1 204 No Content
|
||
Date: Tue, 06 Mar 2012 16:38:51 GMT
|
||
DAAP-Server: iTunes/10.6 (Mac OS X)
|
||
Content-Type: application/x-dmap-tagged
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h1 id="screenmirroring">6. Screen Mirroring</h1>
|
||
<p>Screen mirroring is achieved by transmitting an <em>H.264</em> encoded video
|
||
stream over a TCP connection. This stream is packetized with a 128-byte
|
||
header. <em>AAC-ELD</em> audio is sent using the AirTunes protocol. As for the
|
||
master clock, it is synchronized using <em>NTP</em>.</p>
|
||
|
||
<p>Moreover, as soon as a client starts a video playback, a standard
|
||
AirPlay connection is made to send the video URL, and mirroring is
|
||
stopped. This avoids decoding and re-encoding the video, which would
|
||
incur a quality loss.</p>
|
||
|
||
<h2 id="screenmirroring-httprequests">6.1. HTTP requests</h2>
|
||
<p>Screen mirroring does not use the standard AirPlay service. Instead it
|
||
connects to an apparently hard-coded port 7100. This is a HTTP server
|
||
which supports the following requests:</p>
|
||
|
||
<h3>GET /stream.xml</h3>
|
||
|
||
<p>Retrieve information about the server capabilities. The server sends an
|
||
XML property list with the following properties:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>height</code></td>
|
||
<td>integer</td>
|
||
<td>720</td>
|
||
<td>vertical resolution</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>width</code></td>
|
||
<td>integer</td>
|
||
<td>1280</td>
|
||
<td>horizontal resolution</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>overscanned</code></td>
|
||
<td>boolean</td>
|
||
<td>true</td>
|
||
<td>is the display overscanned?</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>refreshRate</code></td>
|
||
<td>real</td>
|
||
<td>0.01666…</td>
|
||
<td>refresh rate 60 Hz (1/60)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>version</code></td>
|
||
<td>string</td>
|
||
<td>130.14</td>
|
||
<td>server version</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>These properties tell us that the AirPlay server is connected to a
|
||
1280x720, 60 Hz, overscanned display.</p>
|
||
|
||
<p><span class="ex">Example:</span> fetch mirroring server informations</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>GET /stream.xml HTTP/1.1
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Mon, 08 Mar 2012 15:30:27 GMT
|
||
Content-Type: text/x-apple-plist+xml
|
||
Content-Length: 411
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>height</key>
|
||
<integer>720</integer>
|
||
<key>overscanned</key>
|
||
<true/>
|
||
<key>refreshRate</key>
|
||
<real>0.016666666666666666</real>
|
||
<key>version</key>
|
||
<string>130.14</string>
|
||
<key>width</key>
|
||
<integer>1280</integer>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h3>POST /stream</h3>
|
||
|
||
<p>Start the live video transmission. The client sends a binary property
|
||
list with information about the stream, immediately followed by the
|
||
stream itself. At this point, the connection is no longer a valid HTTP
|
||
connection.</p>
|
||
|
||
<p>The following parameters are sent:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>key</th>
|
||
<th>type</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
<tr>
|
||
<td>deviceID</td>
|
||
<td>integer</td>
|
||
<td>181221086727016</td>
|
||
<td>MAC address (A4:D1:D2:80:0B:68)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>sessionID</td>
|
||
<td>integer</td>
|
||
<td>–808788724</td>
|
||
<td>session ID (0xcfcadd0c)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>version</td>
|
||
<td>string</td>
|
||
<td>130.16</td>
|
||
<td>server version</td>
|
||
</tr>
|
||
<tr>
|
||
<td>param1</td>
|
||
<td>data</td>
|
||
<td>(72 bytes)</td>
|
||
<td>AES key, encrypted with FairPlay</td>
|
||
</tr>
|
||
<tr>
|
||
<td>param2</td>
|
||
<td>data</td>
|
||
<td>(16 bytes)</td>
|
||
<td>AES initialization vector</td>
|
||
</tr>
|
||
<tr>
|
||
<td>latencyMs</td>
|
||
<td>integer</td>
|
||
<td>90</td>
|
||
<td>video latency in ms</td>
|
||
</tr>
|
||
<tr>
|
||
<td>fpsInfo</td>
|
||
<td>array</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>timestampInfo</td>
|
||
<td>array</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The <code>param1</code> and <code>param2</code> parameters are optional.</p>
|
||
|
||
<p>As soon as the server receives a <code>/stream</code> request, it will send NTP
|
||
requests to the client on port 7010, which seems hard-coded as well. The
|
||
client needs to export its master clock there, which will be used for
|
||
audio/video synchronization and clock recovery.</p>
|
||
|
||
<p><span class="ex">Example:</span> send stream information</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /stream HTTP/1.1
|
||
X-Apple-Device-ID: 0xa4d1d2800b68
|
||
Content-Length: 503
|
||
|
||
<BINARY PLIST DATA>
|
||
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>deviceID</key>
|
||
<integer>181221086727016</integer>
|
||
<key>fpsInfo</key>
|
||
<array>
|
||
<dict> <key>name</key> <string>SubS</string> </dict>
|
||
<dict> <key>name</key> <string>B4En</string> </dict>
|
||
<dict> <key>name</key> <string>EnDp</string> </dict>
|
||
<dict> <key>name</key> <string>IdEn</string> </dict>
|
||
<dict> <key>name</key> <string>IdDp</string> </dict>
|
||
<dict> <key>name</key> <string>EQDp</string> </dict>
|
||
<dict> <key>name</key> <string>QueF</string> </dict>
|
||
<dict> <key>name</key> <string>Sent</string> </dict>
|
||
</array>
|
||
<key>latencyMs</key>
|
||
<integer>90</integer>
|
||
<key>param1</key>
|
||
<data>
|
||
RlBMWQECAQAAAAA8AAAAANvKuDizduszL1hG9IvIk+AAAAAQukdPJ5Jw/gGBAl22WZdF
|
||
m9ujZEGIV7jm3ZByWm51HjpDwjYY
|
||
</data>
|
||
<key>param2</key>
|
||
<data>
|
||
3qpOHtYWbBPyEWPnGt1BuQ==
|
||
</data>
|
||
<key>sessionID</key>
|
||
<integer>-808788724</integer>
|
||
<key>timestampInfo</key>
|
||
<array>
|
||
<dict> <key>name</key> <string>SubSu</string> </dict>
|
||
<dict> <key>name</key> <string>BePxT</string> </dict>
|
||
<dict> <key>name</key> <string>AfPxT</string> </dict>
|
||
<dict> <key>name</key> <string>BefEn</string> </dict>
|
||
<dict> <key>name</key> <string>EmEnc</string> </dict>
|
||
<dict> <key>name</key> <string>QueFr</string> </dict>
|
||
<dict> <key>name</key> <string>SndFr</string> </dict>
|
||
</array>
|
||
<key>version</key>
|
||
<string>130.16</string>
|
||
</dict>
|
||
</plist>
|
||
</code></pre>
|
||
|
||
<h2 id="screenmirroring-streampackets">6.2. Stream Packets</h2>
|
||
<p>The video stream is packetized using 128-byte headers, followed by an
|
||
optional payload. Only the first 64 bytes of headers seem to be used.
|
||
Headers start with the following little-endian fields:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>size</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>4 bytes</td>
|
||
<td>payload size</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 bytes</td>
|
||
<td>payload type</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 bytes</td>
|
||
<td>0x1e if type = 2, else 6</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8 bytes</td>
|
||
<td>NTP timestamp</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>There are 3 types of packets:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>type</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>video bitstream</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>codec data</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>heartbeat</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<h3>Codec Data</h3>
|
||
|
||
<p>This packet contains the H.264 extra data in <em>avcC</em> format (<a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=55980">ISO/IEC
|
||
14496:15</a>). It is sent at the beginning of the stream, each time the
|
||
video properties might change, when screen orientation changes, and when
|
||
the screen is turned on or off.</p>
|
||
|
||
<p class="caption">H.264 codec data from iPad</p>
|
||
|
||
<pre><code>0000 01 64 c0 28 ff e1 00 10 67 64 c0 28 ac 56 20 0d
|
||
0010 81 4f e5 9b 81 01 01 01 01 00 04 28 ee 3c b0
|
||
</code></pre>
|
||
|
||
<p>The H.264 codec data is interpreted as follows:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>size</th>
|
||
<th>value</th>
|
||
<th>description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1 byte</td>
|
||
<td>1</td>
|
||
<td>version</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1 byte</td>
|
||
<td>100</td>
|
||
<td>profile (high)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1 byte</td>
|
||
<td>0xc0</td>
|
||
<td>compatibility</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1 byte</td>
|
||
<td>40</td>
|
||
<td>level (4.0)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>6 bits</td>
|
||
<td>0x3f</td>
|
||
<td>reserved</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 bits</td>
|
||
<td>3</td>
|
||
<td>NAL units length size - 1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3 bits</td>
|
||
<td>0x7</td>
|
||
<td>reserved</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5 bits</td>
|
||
<td>1</td>
|
||
<td>number of SPS</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 bytes</td>
|
||
<td>16</td>
|
||
<td>length of SPS</td>
|
||
</tr>
|
||
<tr>
|
||
<td>16 bytes</td>
|
||
<td>…</td>
|
||
<td>Sequence parameter set</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1 byte</td>
|
||
<td>1</td>
|
||
<td>number of PPS</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 bytes</td>
|
||
<td>4</td>
|
||
<td>length of PPS</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4 bytes</td>
|
||
<td>…</td>
|
||
<td>Picture parameter set</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p class="caption">Codec data packet from iPad</p>
|
||
|
||
<pre><code>0000 1f 00 00 00 01 00 06 00 1d 9a 9f 59 ef de 00 00
|
||
0010 00 00 58 44 00 00 22 44 00 00 00 00 00 00 00 00
|
||
0020 00 00 00 00 00 00 00 00 00 00 58 44 00 00 22 44
|
||
0030 00 00 50 43 00 00 10 42 00 c0 57 44 00 c0 21 44
|
||
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0080 01 64 c0 28 ff e1 00 10 67 64 c0 28 ac 56 20 0d
|
||
0090 81 4f e5 9b 81 01 01 01 01 00 04 28 ee 3c b0
|
||
</code></pre>
|
||
|
||
<h3>Video Bitstream</h3>
|
||
|
||
<p>This packet contains the video bitstream to be decoded. The payload can
|
||
be optionally AES encrypted. The NTP timestamp found in the header
|
||
serves as presentation timestamp.</p>
|
||
|
||
<p class="caption">Video bitstream packet from iPad</p>
|
||
|
||
<pre><code>0000 c8 08 00 00 00 00 06 00 e9 e6 f5 ac 60 e0 00 00
|
||
0010 58 37 6e f9 40 01 00 00 00 00 00 00 00 00 00 00
|
||
0020 00 00 00 00 00 00 00 00 00 00 58 44 00 00 22 44
|
||
0030 00 00 50 43 00 00 10 42 00 c0 57 44 00 c0 21 44
|
||
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0080 ...
|
||
</code></pre>
|
||
|
||
<h3>Heartbeat</h3>
|
||
|
||
<p>Sent every second, this packet does not contain any payload.</p>
|
||
|
||
<p class="caption">Heartbeat packet from iPad</p>
|
||
|
||
<pre><code>0000 00 00 00 00 02 00 1e 00 00 00 00 00 00 00 00 00
|
||
0010 4d d8 1a 41 00 00 00 00 00 00 20 41 86 c9 e2 36
|
||
0020 00 00 00 00 80 88 44 4b 00 00 00 00 00 00 00 00
|
||
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
</code></pre>
|
||
|
||
<h2 id="screenmirroring-timesynchronization">6.3. Time Synchronization</h2>
|
||
<p>Time synchronization takes place on UDP ports 7010 (client) and 7011
|
||
(server), using the <em>NTP</em> protocol (<a href="http://www.ietf.org/rfc/rfc5905.txt">RFC 5905</a>). The AirPlay server runs
|
||
an NTP client. Requests are sent to the AirPlay client at 3 second
|
||
intervals. The reference date for the timestamps is the beginning of the
|
||
mirroring session.</p>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>0000 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||
0020 00 00 00 00 00 00 00 00 00 00 01 c4 c8 ac 5d b5
|
||
|
||
Network Time Protocol
|
||
Flags: 0x23
|
||
00.. .... = Leap Indicator: no warning (0)
|
||
..10 0... = Version number: NTP Version 4 (4)
|
||
.... .011 = Mode: client (3)
|
||
Peer Clock Stratum: unspecified or invalid (0)
|
||
Peer Polling Interval: invalid (0)
|
||
Peer Clock Precision: 1.000000 sec
|
||
Root Delay: 0.0000 sec
|
||
Root Dispersion: 0.0000 sec
|
||
Reference ID: NULL
|
||
Reference Timestamp: Jan 1, 1970 00:00:00.000000000 UTC
|
||
Origin Timestamp: Jan 1, 1970 00:00:00.000000000 UTC
|
||
Receive Timestamp: Jan 1, 1970 00:00:00.000000000 UTC
|
||
Transmit Timestamp: Jan 1, 1900 00:07:32.783880000 UTC
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>0000 24 01 02 e8 00 00 00 00 00 00 00 00 41 49 52 50
|
||
0010 00 00 00 00 00 00 00 00 00 00 01 c4 c8 ac 5d b5
|
||
0020 00 00 01 c4 c9 6a 0b a1 00 00 01 c4 c9 78 73 d2
|
||
|
||
Network Time Protocol
|
||
Flags: 0x24
|
||
00.. .... = Leap Indicator: no warning (0)
|
||
..10 0... = Version number: NTP Version 4 (4)
|
||
.... .100 = Mode: server (4)
|
||
Peer Clock Stratum: primary reference (1)
|
||
Peer Polling Interval: invalid (2)
|
||
Peer Clock Precision: 0.000000 sec
|
||
Root Delay: 0.0000 sec
|
||
Root Dispersion: 0.0000 sec
|
||
Reference ID: Unidentified reference source 'AIRP'
|
||
Reference Timestamp: Jan 1, 1970 00:00:00.000000000 UTC
|
||
Origin Timestamp: Jan 1, 1900 00:07:32.783880000 UTC
|
||
Receive Timestamp: Jan 1, 1900 00:07:32.786774000 UTC
|
||
Transmit Timestamp: Jan 1, 1900 00:07:32.786994000 UTC
|
||
</code></pre>
|
||
|
||
<h1 id="passwordprotection">7. Password Protection</h1>
|
||
<p>An AirPlay server can require a password for displaying any content from
|
||
the network. This is implemented using standard <em>HTTP Digest
|
||
Authentication</em> (<a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a>), over RTSP for AirTunes, and HTTP for
|
||
everything else. The digest realms and usernames accepted by Apple TV are
|
||
the following:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>service</th>
|
||
<th>realm</th>
|
||
<th>username</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>AirTunes</td>
|
||
<td><code>raop</code></td>
|
||
<td><code>iTunes</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>AirPlay</td>
|
||
<td><code>AirPlay</code></td>
|
||
<td><code>AirPlay</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><span class="ex">Example 1:</span> AirTunes password request</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://fe80::217:f2ff:fe0f:e0f6/3414156527 RTSP/1.0
|
||
CSeq: 3
|
||
Content-Type: application/sdp
|
||
Content-Length: 348
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 448488758
|
||
|
||
<SDP DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 401 Unauthorized
|
||
Server: AirTunes/130.14
|
||
WWW-Authenticate: Digest realm="raop", nonce="ddfd59b4aea7bbbcbbb3b60d3b2768b7"
|
||
CSeq: 3
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>ANNOUNCE rtsp://fe80::217:f2ff:fe0f:e0f6/3414156527 RTSP/1.0
|
||
CSeq: 4
|
||
Content-Type: application/sdp
|
||
Content-Length: 348
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Client-Instance: 56B29BB6CB904862
|
||
DACP-ID: 56B29BB6CB904862
|
||
Active-Remote: 448488758
|
||
Authorization: Digest username="iTunes", realm="raop", nonce="ddfd59b4aea7bbbcbbb3b60d3b2768b7", uri="rtsp://fe80::217:f2ff:fe0f:e0f6/3414156527", response="36f93a97c9038598290729ec0f141b03"
|
||
|
||
<SDP DATA>
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>RTSP/1.0 200 OK
|
||
Server: AirTunes/130.14
|
||
CSeq: 4
|
||
</code></pre>
|
||
|
||
<p><span class="ex">Example 2:</span> AirPlay password request</p>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /play HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 163
|
||
Content-Type: text/parameters
|
||
|
||
Content-Location: http://192.168.1.18:3689/airplay.mp4?database-spec='dmap.persistentid:0x63b5e5c0c201542e'&item-spec='dmap.itemid:0x21e'
|
||
Start-Position: 0.317546
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 401 Unauthorized
|
||
Date: Fri, 09 Mar 2012 15:50:40 GMT
|
||
Content-Length: 0
|
||
WWW-Authenticate: Digest realm="AirPlay", nonce="MTMzMTMwODI0MCDEJP5Jo7HFo81rbAcKNKw2"
|
||
</code></pre>
|
||
|
||
<p class="cli_srv">client → server</p>
|
||
|
||
<pre><code>POST /play HTTP/1.1
|
||
User-Agent: iTunes/10.6 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/535.18.5
|
||
Content-Length: 163
|
||
Content-Type: text/parameters
|
||
Authorization: Digest username="AirPlay", realm="AirPlay", nonce="MTMzMTMwODI0MCDEJP5Jo7HFo81rbAcKNKw2", uri="/play", response="aa085eea3e66a2e56125a4957e70894a"
|
||
|
||
Content-Location: http://192.168.1.18:3689/airplay.mp4?database-spec='dmap.persistentid:0x63b5e5c0c201542e'&item-spec='dmap.itemid:0x21e'
|
||
Start-Position: 0.317546
|
||
</code></pre>
|
||
|
||
<p class="srv_cli">server → client</p>
|
||
|
||
<pre><code>HTTP/1.1 200 OK
|
||
Date: Fri, 09 Mar 2012 15:50:40 GMT
|
||
Content-Length: 0
|
||
</code></pre>
|
||
|
||
<h1 id="contributing">8. Contributing</h1>
|
||
|
||
<h2 id="contributing-data">8.1. Device Data</h2>
|
||
|
||
<p>
|
||
If anybody wants to help out who has some Airplay compatible hardware we
|
||
can always use some more data. Primarily we right now need mDNS records
|
||
and the results from RTSP <code>GET /info/</code> requests. If you have
|
||
a computer other than a windows machine I have written some short guides
|
||
below and if that is not enough I can probably help you if you send
|
||
an e-mail to <a href="brian@maven-group.org">brian@maven-group.org</a>.
|
||
</p>
|
||
<p>
|
||
We right now have data for a couple of 4K Apple TVs, an AirPort Express,
|
||
two stereo paired HomePods, the Libretone LTH200 and a Denon AVR-X3500X
|
||
surround receiver so if your hardware is something else I am especially
|
||
interested in the data from it.
|
||
</p>
|
||
|
||
<p>
|
||
Any data that you send will only be viewed by me and I will change stuff
|
||
like device names, ip addresses, MAC addresses, serial numbers and UUIDs
|
||
before including the data in this spec.
|
||
</p>
|
||
|
||
<h3 id="contributing-data-linux">8.1.1. Linux</h3>
|
||
|
||
<p>If you are on Linux and have <code>avahi</code> installed you can run the following
|
||
shell script to gather all the data.</p>
|
||
|
||
<p class="caption">capture-data.sh</p>
|
||
<pre><code>#!/bin/bash
|
||
avahi-browse -prt _airplay._tcp | awk -v "FS=;" '{ print $7 }' | sed '/^\s*$/d' > devices.txt
|
||
avahi-browse -aprt | grep -f devices.txt > mDNS.txt
|
||
for host in $(cat devices.txt) ; do
|
||
PORT="$(avahi-browse -prt _airplay._tcp | grep ";$host;" | awk -v "FS=;" '{print $9}' | sed '/^\s*$/d')"
|
||
curl https://griff.github.io/airplay-spec/RTSP-get-info-req.bin | nc -w 2 "$host" $PORT > "RTSP-get-info-res-$host.bin"
|
||
done
|
||
tar czvf device-data.tar.gz mDNS.txt RTSP-get-info-res*
|
||
</code></pre>
|
||
|
||
<p>Then you can send the resulting file <code>device-data.tar.gz</code>
|
||
as an e-mail to <a href="mailto:brian@maven-group.org">brian@maven-group.org</a></p>
|
||
|
||
<h3 id="contributing-data-macos">8.1.2. macOS</h3>
|
||
|
||
<p>
|
||
To find mDNS records on macOS I usually use the free <a href="https://itunes.apple.com/dk/app/discovery-dns-sd-browser/id1381004916?mt=12">Discovery</a>.
|
||
From there you can find all the devices that publish a <code>_airplay._tcp</code>
|
||
service, select and copy/paste the results into an e-mail. It is also
|
||
relevant if you can find what other services those devices publish and
|
||
include the service records for those services.
|
||
</p>
|
||
|
||
<p>
|
||
To get the RTSP <code>GET /info</code> results you will need the ip
|
||
address and port number your devices use for <code>_airplay._tcp</code>
|
||
and in the command below replace <code>[ip address]</code>,
|
||
<code>[port]</code> and <code>[device name]</code> with the correct values.
|
||
</p>
|
||
|
||
<pre><code>curl https://griff.github.io/airplay-spec/RTSP-get-info-req.bin | nc -w 2 [ip address] [port] > RTSP-get-info-res-[device name].bin</code></pre>
|
||
|
||
<p>
|
||
Then you can send the mDNS records and <code>RTSP-get-info-res-[device name].bin</code>
|
||
files in an e-mail to <a href="mailto:brian@maven-group.org">brian@maven-group.org</a>
|
||
</p>
|
||
|
||
<h1 id="history">9. History</h1>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Changes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>2012–03–20</td>
|
||
<td>Initial version.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2019–04–30</td>
|
||
<td>Updated with data from reverse engineering <code>AirPlaySender.framework</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2019–05–01</td>
|
||
<td>Cleaned up HTML and added a little interactivity.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2019–05–01</td>
|
||
<td>Added data for Libretone</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2019–05–01</td>
|
||
<td>Add contributing section</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2019–05–02</td>
|
||
<td>Add more example devices and click on flag to change</td>
|
||
</tr>
|
||
</table>
|
||
<h1 id="resources">10. Resources</h1>
|
||
<h2 id="resources-ietfrfcs">10.1. IETF RFCs</h2>
|
||
<ul>
|
||
<li><a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>: Hypertext Transfer Protocol – HTTP/1.1</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a>: HTTP Authentication: Basic and Digest Access Authentication</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc2326.txt">RFC 2326</a>: Real Time Streaming Protocol (RTSP)</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc4566.txt">RFC 4566</a>: SDP: Session Description Protocol</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc3550.txt">RFC 3550</a>: RTP: A Transport Protocol for Real-Time Applications</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc5905.txt">RFC 5905</a>: Network Time Protocol Version 4</li>
|
||
<li><a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a>: The Base16, Base32, and Base64 Data Encodings</li>
|
||
</ul>
|
||
|
||
<h2 id="resources-ietfdrafts">10.2. IETF drafts</h2>
|
||
<ul>
|
||
<li><a href="http://www.ietf.org/id/draft-cheshire-dnsext-multicastdns-15.txt">Multicast DNS</a></li>
|
||
<li><a href="http://www.ietf.org/id/draft-cheshire-dnsext-dns-sd-11.txt">DNS-Based Service Discovery</a></li>
|
||
<li><a href="http://tools.ietf.org/id/draft-lentczner-rhttp-00.txt">Reverse HTTP</a></li>
|
||
<li><a href="http://tools.ietf.org/id/draft-pantos-http-live-streaming-07.txt">HTTP Live Streaming</a></li>
|
||
</ul>
|
||
|
||
<h2 id="resources-appleprotocols">9.3. Apple Protocols</h2>
|
||
<ul>
|
||
<li><a href="http://en.wikipedia.org/wiki/Digital_Audio_Access_Protocol">DAAP</a>: Digital Audio Access Protocol</li>
|
||
<li><a href="http://en.wikipedia.org/wiki/Digital_Audio_Control_Protocol">DACP</a>: Digital Audio Control Protocol</li>
|
||
<li><a href="http://en.wikipedia.org/wiki/Remote_Audio_Output_Protocol">RAOP</a>: Remote Audio Output Protocol</li>
|
||
</ul>
|
||
<div id="footer">
|
||
© 2012 <a href="mailto:clement.vasseur@gmail.com">Clément Vasseur</a>.
|
||
© 2019 <a href="mailto:brian@maven-group.org">Brian Olsen</a>.
|
||
Last update: 2019-05-02.
|
||
</div>
|
||
</div>
|
||
<script type="text/javascript" src="long.js"></script>
|
||
<script type="text/javascript" src="features.js"></script>
|
||
</body>
|
||
</html>
|