当前位置: 首页 > 工具软件 > AirPlay > 使用案例 >

[资料]airplay协议

宁锐
2023-12-01

相关资料

Unofficial AirPlay Protocol Specification

Android 开发 AirPlay Server

AirReceiver 使用Java写的运行在PC端的AirPlay接收端

DroidAirPlay 基于AirReceiver代码转换成安卓项目的实现代码

AndroidAirPlayReceiver基于DroidAirPlay改造后能直接使用的安卓工程

https://www.ietf.org/rfc/rfc2326.txt

相关技术

服务发现:
mDns
ssdp

流程

转自知乎大佬 刘同学被占用了

https://zhuanlan.zhihu.com/p/34324606

Airplay Mirroring客户端的同屏交互过程,分为三个主要步骤:

1, 设备广播与发现

2, 信息交互与能力协商

3, 音视频数据接收与解扰

设备广播与发现:

Airplay设备间的广播与发现通过Bonjour协议进行。Bonjour也被称为ZeroConf, mDNS等,可以用来在局域网内进行数据记录广播与发现。该协议比较成熟,网上可以找到诸多介绍。对于实现的Airplay(包括Mirroring)接收端而言,首先需要注册两类服务,即airtunes和airplay。 Airtunes服务主要用来处理广播视音频接收能力协商,是最为重要的服务内容,对应Bonjour记录名称为’_raop._tcp’,注册服务端口不限,一般为了避免冲突,建议采用较高的端口数;Airplay服务主要用来兼容传统的streaming等服务,对应记录名称为’_airplay._tcp’,注册端口一般为7000。

具体的服务广播内容,可以进行局域网抓包,找到对应记录内容。

当接收端通过Bonjour广播器服务能力后,发送端(如iPhone等各类iOS设备)就可以发现该接收端。

信息交互与能力协商:

当发送端发现接收端后,可以开始信息交互与能力协商过程。该部分协议协议格式类似rtsp协议格式。主要分为两个阶段,设备匹配与和能力协商。

当发送端链接服务端后,设备匹配过程即开始。通信双方会进行fairplay加密协议进行信息交换,当完成信息交换后,客户端后续必须使用这部分信息来处理加密过的密钥,才能获得进一步视音频解密密钥。在iOS9之后,在fairplay过程之前,增加一个设备匹配过程,即pair-setup、pair-verify过程,其主要算法是较为标准的非对称公钥交换算法。

当两端成功匹配后,开始进行能力协商与信息交换,这些信息包括,设备名称、代号,音视频接收相关端口配置,视频接收能力以及加密密钥等,相关信息使用binary plist格式进行封装。

可以参考https://github.com/espes/Slave-in-the-Magic-Mirror找到相关协议交互的一些细节。

音视频数据接收与解密

双方协商成功后,发送端开始向接收端发送视音频数据,mirroring数据是通过TCP进行发送,为h.264 ES流格式。音频是通过RTP协议进行发送,根据内容的不同音频编码为ALAC或者AAC-ELD。

音视频流都是通过AES进行了加密处理,密钥需要通过上面一步的进过信息交互后的fairplay模组对setup过程中接收到的加密密钥进行解密,获得的AES解密需要的IV和KEY,然后经过AES解扰,即可以获得最终的视音频清流。

其他需要注意的地方:

Airplay没过Session传送过来的视频h264码流,只有开头一个关键帧. 因此这种情况并不适合直播这种需要固定GOP的场景. 还需要做进一步的转码的工作,或者直接在压缩域进行处理,获得合理的GOP结构。

广播发现

协议交互

https://blog.csdn.net/mirkerson/article/details/8601333?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242

/info

request:

response:

<?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>pk</key>
	<data>
	XYMxJlYMsZoUGTcneJbw/UN7poAeshCsTDnZAHLXDag=
	</data>
	<key>name</key>
	<string>Apple TV</string>
	<key>vv</key>
	<integer>2</integer>
	<key>statusFlags</key>
	<integer>68</integer>
	<key>keepAliveLowPower</key>
	<integer>1</integer>
	<key>keepAliveSendStatsAsBody</key>
	<integer>1</integer>
	<key>pi</key>
	<string>b08f5a79-db29-4384-b456-a4784d9e6055</string>
	<key>sourceVersion</key>
	<string>220.68</string>
	<key>deviceID</key>
	<string>00:50:56:C0:00:01</string>
	<key>macAddress</key>
	<string>00:50:56:C0:00:01</string>
	<key>model</key>
	<string>AppleTV3,2</string>
	<key>audioFormats</key>
	<array>
		<dict>
			<key>audioInputFormats</key>
			<integer>67108860</integer>
			<key>audioOutputFormats</key>
			<integer>67108860</integer>
			<key>type</key>
			<integer>100</integer>
		</dict>
		<dict>
			<key>audioInputFormats</key>
			<integer>67108860</integer>
			<key>audioOutputFormats</key>
			<integer>67108860</integer>
			<key>type</key>
			<integer>101</integer>
		</dict>
	</array>
	<key>audioLatencies</key>
	<array>
		<dict>
			<key>audioType</key>
			<string>default</string>
			<key>inputLatencyMicros</key>
			<false/>
			<key>outputLatencyMicros</key>
			<false/>
			<key>type</key>
			<integer>100</integer>
		</dict>
		<dict>
			<key>audioType</key>
			<string>default</string>
			<key>inputLatencyMicros</key>
			<false/>
			<key>outputLatencyMicros</key>
			<false/>
			<key>type</key>
			<integer>101</integer>
		</dict>
	</array>
	<key>features</key>
	<integer>130367356918</integer>
	<key>displays</key>
	<array>
		<dict>
			<key>height</key>
			<integer>1080</integer>
			<key>width</key>
			<integer>1920</integer>
			<key>rotation</key>
			<false/>
			<key>widthPhysical</key>
			<false/>
			<key>heightPhysical</key>
			<false/>
			<key>widthPixels</key>
			<integer>1920</integer>
			<key>heightPixels</key>
			<integer>1080</integer>
			<key>refreshRate</key>
			<real>0.016667</real>
			<key>maxFPS</key>
			<integer>60</integer>
			<key>features</key>
			<integer>14</integer>
			<key>overscanned</key>
			<false/>
			<key>uuid</key>
			<string>e5f7a68d-7b0f-4305-984b-974f677a150b</string>
		</dict>
	</array>
</dict>
</plist>

SETUP

response:

<?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>timingPort</key>
	<integer>6000</integer>
	<key>eventPort</key>
	<integer>47010</integer>
	<key>streams</key>
	<array>
		<dict>
			<key>type</key>
			<integer>96</integer>
			<key>controlPort</key>
			<integer>6001</integer>
			<key>dataPort</key>
			<integer>6003</integer>
		</dict>
	</array>
</dict>
</plist>


<?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>et</key>
	<integer>32</integer>
	<key>eiv</key>
	<data>
	aAW66U8aj8bSvSFEJYVr1w==
	</data>
	<key>timingProtocol</key>
	<string>NTP</string>
	<key>sessionUUID</key>
	<string>F2F647DB-1E87-4AA6-B2AF-AA6CE0BF9D3F</string>
	<key>osName</key>
	<string>iPhone OS</string>
	<key>osBuildVersion</key>
	<string>17F80</string>
	<key>sourceVersion</key>
	<string>420.45</string>
	<key>timingPort</key>
	<integer>53648</integer>
	<key>isScreenMirroringSession</key>
	<true/>
	<key>osVersion</key>
	<string>13.5.1</string>
	<key>ekey</key>
	<data>
	RlBMWQECAQAAAAA8AAAAACbSNQTFG57dB11iWsNtMj8AAAAQvJ5sT/YIec4lRLGGRXsi
	HZ0UBENC+6P6Vco8NOvFLRsXN1Qi
	</data>
	<key>deviceID</key>
	<string>F8:95:EA:78:14:F0</string>
	<key>model</key>
	<string>iPhone10,3</string>
	<key>name</key>
	<string>姝︽眽鐨勬祴璇曟満iPhoneX 2</string>
	<key>macAddress</key>
	<string>F8:95:EA:83:87:59</string>
</dict>
</plist>


<?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>timingPort</key>
	<integer>0</integer>
	<key>eventPort</key>
	<integer>46001</integer>
	<key>streams</key>
	<array>
		<dict>
			<key>type</key>
			<integer>110</integer>
			<key>dataPort</key>
			<integer>46000</integer>
		</dict>
	</array>
</dict>
</plist>



音视频接收

 类似资料: