工作中需要研究airplay以实现IOS的镜像服务端,将了解到的内容记录下来。
Airplay可以实现将设备中音频、视频、图像通过本地无线网络发送远端设备;Airplay功能实现利用了很多标准协议,包括multicast DNS,HTTP,RTSP,RTP,RAOP,NTP,见下表1:
AirPlay Technology | Display photos and slideshows | Stream audio | Stream video | Display mirroring |
Commonly Know as | - | AirPlay, AirTunes | AirPlay | AirPlay Mirroring |
Used Protocols, Technology | HTTP | RAOP, RTSP, RTP | HTTP | HTTP, NTP |
Bonjour是苹果为基于组播域名服务(multicast DNS)的开放性零设置网络标准所起的名字。使用Bonjour的设备在网络中自动传播它们自己的服务信息并聆听其它设备的服务信息,设备之间就象在打招呼,这也是命名为Bonjour(法语:你好)的原因。
将ios手机接入wifi后用wireshark抓包能够看到192.168.1.100 向 224.0.0.251 port 5353 发送广播.
操作手机,打开媒体播放器时,ios会用MDNS发送Queries,其中有两项服务_raop._tcp.local和_airplay._tcp.local,如下图:
Airplay用Bonjour做设备发现,在windows中安装BonjourSDKforWin,在任务管理器中有mDNSResponder.exe进程用于mdns协议处理。
----------------------------------------------------------------------------------
BonjourSDK Demo
BonjourSDK文件夹中Samples目录是一个示例工程,将该工程编译运行,进行测试并对MDNS抓包分析如下:
dns-sd.exe -I (Test registering and then immediately updating TXT record)
pid_t pid = getpid();/*current process id*/
Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
static const char TXT[] = "\x09" "Test Data";
printf("Registering Service Test._testtxt._tcp.local.\n");
err = DNSServiceRegister(&client, 0, opinterface /*kDNSServiceInterfaceIndexAny*/, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL);
if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0);
break;
上面这段代码在 mDns 中注册一个服务,对应在 MDNS 数据包中会有一个 Query 和 Authoritative ,DNSServiceRegister函数的第三个参数为kDNSServiceInterfaceIndexAny指定Query中Type=ANY,第四个参数是服务名称、第五个参数是支持协议名称,第四、五个参数组合成Name,服务端口号为registerPort,在这个DEMO中是该进程ID(7948),如下如所示:
由于上面有query Test._Testtxt._tcp.local,那么mDNS正常对这个query进行应答,由于在query指定TYPE=ANY,所以Answers中除了Test._testtxt.tcp.local外,还有其它服务信息。
DEMO程序中DNSServiceUpdateRecord用于更新TXT文本,如下图所示,在Answers中的TXT字段信息中TXT内容为DEMO程序中所设置的“Test Data”。
----------------------------------------------------------------------------------
Airplayer
在网上找到一款可以在windows使用的Airplay服务端(AirPlayer),当然首先需要安装Bonjour,AirPlayer可以实现将ios音频、图像同步在pc端播放,也可以将ios屏幕镜像到pc端,我使用wireshark抓包情况如下:
1.当Airplayer程序启动后,首先向mDNS注册了两个服务,分别支持两个不同的协议_airplay和_raop,_airplay协议用于视频传输,_raop用于音频流传输(可参考表1)。
2.接下来Airplayer程序会针对_airplay和_raop这两个服务Answers进行服务描述。
iTools[CMCC-BAIJIE]._airplay._tcp.local描述airplay协议服务端口为64885,0C54A5569D80@iTools[CMCC-BAIJIE]._raop._tcp.local描述raop协议服务端口为64886;
重点在于两个服务分别对TXT的描述部份,如下图:
协议TXT描述内容必需按Airplay协议标准要求进行描述,该标准在网上未能找到官方文档,但有一个非官方资料可供参考,地址如下:
http://nto.github.io/AirPlay.html
我们以_airplay协议TXT描述内容为例,对照Airplay.html文档来看应该不难理解吧..
iTools[CMCC-BAIJIE]._airplay._tcp.local: type TXT, class IN, cache flush
deviceid=0c:54:a5:56:9d:80
features=0x100029FF
flags=0x4
model=AppleTV3,1
srcvers=150.33
vv=1
在Airplay.html文档中内容描述如下:
AirPlay service
name: Apple TVtype: _airplay._tcpport: 7000txt: deviceid=58:55:CA:1A:E2:88 features=0x39f7 model=AppleTV2,1 srcvers=130.14
The following fields are available in the TXT record:
name | value | description |
model | AppleTV2,1 | device model |
deviceid | 58:55:CA:1A:E2:88 | MAC address of the device |
features | 0x39f7 | bitfield of supported features |
pw | 1 | server is password protected |
The pw field appears only if the AirPlay server is password protected. Otherwise it is not included in the TXT record.
The features bitfield allows the following features to be defined:
bit | name | description |
0 | Video | video supported |
1 | Photo | photo supported |
2 | VideoFairPlay | video protected with FairPlay DRM |
3 | VideoVolumeControl | volume control supported for videos |
4 | VideoHTTPLiveStreams | http live streaming supported |
5 | Slideshow | slideshow supported |
7 | Screen | mirroring supported |
8 | ScreenRotate | screen rotation supported |
9 | Audio | audio supported |
11 | AudioRedundant | audio packet redundancy supported |
12 | FPSAPv2pt5_AES_GCM | FairPlay secure auth supported |
13 | PhotoCaching | photo preloading supported |
这里需要说明的是,在Screen Mirroring(镜像)的功能处理中,airplay提供的端口号64885并未使用,而是client直接连接7100这个端口,关于这点在《Unofficial AirPlay Protocol Specification》中有相关描述。
Screen mirroring does not use the standard AirPlay service. Instead it connects to an apparently hard-coded port 7100
Airplay中的roap服务0C54A5569D80@iTools[CMCC-BAIJIE]._raop._tcp.local,这里的0C54A5569D80@iTools[CMCC-BAIJIE]做为name使用,且必须以这样的格式命名,不然airplay的手机端不会识别,0C54A5569D80串字符为本机的物理地址。
当pc上的接收端程序DEMO或Airplayer应用退出时,也就是所注册的服务关闭时,mDNS广播服务注销消息,在数据包Answers中的服务项指定Time to live = 0,宣告服务停止。
=======================================
现在开始尝试注册自己的服务,希望最终能够达到可以镜像同步的目地。首先需要先注册一个自己的_airplay服务并发布到mDNS,这是第一步,也就是先要在IOS手机的airplay选项中找到自己,如果自己重新实现mDNS服务耗时就太长了,先做DEMO的话我们可以利用一下SDK中的例子,简单修改一下就OK,我在dns-sd 示例工程的main函数中加入下面代码:
#ifdef _DEMO
Demo:
{
#define kRaopPort 50001
#define kAirplayPort 50002
static DNSServiceRef airplayRef = NULL;
static DNSServiceRef raopRef = NULL;
Opaque16 AirplayPort = { { kAirplayPort >> 8, kAirplayPort & 0xFF } };
Opaque16 RaopPort = { { kRaopPort >> 8, kRaopPort & 0xFF } };
static const char AirplayTXT[] =
"\x1A" "deviceid=0c:54:a5:56:9d:80" \
"\x0F" "features=0x3FFF"; \
//"\x10" "model=AppleTV3,1";
//"\x0E" "srcvers=150.33";
static const char RaopTXT[] =
"\x06" "tp=UDP" \
"\x08" "sm=false" \
"\x08" "sv=false" \
"\x04" "ek=1" \
"\x06" "et=0,1" \
"\x06" "cn=0,1" \
"\x04" "ch=2" \
"\x05" "ss=16" \
"\x08" "sr=44100" \
"\x08" "pw=false" \
"\x04" "vn=3" \
"\x09" "txtvers=1";
err = DNSServiceRegister(&airplayRef, 0, opinterface, "JieTools", "_airplay._tcp.", "", NULL, AirplayPort.NotAnInteger, 0, NULL, reg_reply, NULL);
if (!err) err = DNSServiceUpdateRecord(airplayRef, NULL, 0, sizeof(AirplayTXT)-1, AirplayTXT, 0);
err = DNSServiceRegister(&raopRef, 0, opinterface, "0C54A5569D80@JieTools", "_raop._tcp.", "", NULL, RaopPort.NotAnInteger, 0, NULL, reg_reply, NULL);
if (!err) err = DNSServiceUpdateRecord(raopRef, NULL, 0, sizeof(RaopTXT)-1, RaopTXT, 0);
while(1)getchar();
return 0;
}
#endif
前两行定义指定服务端口,而后的AirplayTXT与RaopTXT分别两个服务的描述内容,下面对AirplayTXT做简单说明:
"\x1A"这样的写法,是为字符串前添加长度字值,为16进制,deviceid后面的值是本机网卡的物理地址,features这个参数不能少,它是airplay服务所支持的特性或能力描述,其它的参数可以忽略。
RaopTXT描述内容是我通过抓包COPY下来的,没有修改过;再接下来调用了两个mDNS SDK中的两个API,DNSServiceRegister用于注册,DNSServiceUpdateRecord用来更新服务的TXTRecord信息。
这里有两组调用服务注册,这里需要注意的是,如果你想实现_airplay服务,那么就必须将这两个服务一起注册,并且服务名称必须一致,如第四个参数是服务名称“JieTools”及“0C54A5569D80@JieTools”,注意命名规则。
OK,不出意外的话,运行它,打开你的手机,就能在airplay中发现自己注册的这个服务了。
未完待续 .....