好吧,几经联系,找到了变态的德国原籍作者,此人还很狂热的在iTunesU发布了德语音频得瑟讲解,但是太悲剧,完全听不懂; 好在,我看懂了代码..哦也 注意,今儿只讲第一代开源的airtunes在实际中的原理,以及展望可以在其他平台实现的方式 那些私信问我如何实现airplay到另外一台iphone查看的人,你们消停下;个人建议你们使用gamekit来小范围内数据传输共享; airplay是私有协议,而且仅在apple tv里有实现代码;而apple tv不同于airportexpress; TV可以随时升级,而那基座很难升级; 破解airport express的key才有意义;破解TV的结局就是随时升级,换个key,你又没有普遍实现价值了; 言归正传,airtunes如何实现; self.netService= [[NSNetServicealloc] initWithDomain:@""type:@"_raop._tcp"name:[NSStringstringWithFormat:@"%@@%@", [selfMACAddressToRawString], self.name] port:MSShairportServerPort]; 概括下, _raop._tcp airtunes的类型;仅在mirror和ipod里能看到; name是mac地址;个人觉得这个是可选项,可自定义 port 是5000 以上是publishservice的实现; 这里我插播一下,在win和android平台上的实现方法;那就是你悲剧了;你首先要找Zeroconfig的开源库,其次你要了解神马是NetService;然后写一个基础库类似…NetWork.cpp里面支持upnp等等乱七八糟的东西; 有些平台似乎还支持度不高;Bonjour的世界里,互相识别的代码实现都很简单;但是跨到其他平台,简直是大悲剧; NSNetservice是这么自我介绍的,Http ftp是有名的service;其实你也可以自定义service;一句话就简洁明了了; 可是win的开发库要遇到这么简介透彻的事情,简直是无法想象; 作为开发者,咱有必要去深究底层原理么?既然流行社会化大分工,那软件代码都要让别人用得畅快,而不是让开发者深入到spec,protocol的海洋里拼命苦逼;国内很多小有成就的牛人,就擅长吹牛各种苦逼学习经历,原理等等作为谈资,软件开发又不是什么都要学会,把手头里干得好,不就行了么? 这一点,苹果的理念真是让人觉得好爽… 只要大家都能轻松学会的,那就是成功的设计; 好吧,下面就是进入正题了; 你的servicepublish之后,同时起来一个监听5000端口的tcp的socket连接; 当你在ipod里选择那个设备作为接收端后,点击音乐播放,你就会收到一个tcp in coming connection; 这里要吐槽一下os x世界里的tcp udp通信,咋就用的这么不习惯呢? 好好的accept,bind, listen , send ,receive不用;非丫的要玩CFNetwork,搞神马CFReadStream CFWriteStream;虽然原理大致相同,但是好不习惯; 好吧,你收到了connection,你于是接受内容呗;
OPTIO_NS * RTSP/1.0
CSeq: 0
Apple-Challenge: Sx5Dd77BJM5q1v37RhtrQg==
DACP-ID: CB33FA1059D0B517
Active-Remote: 96355174 这是一段原始数据,Optio_ns代表类型;这是第一次握手的必经过程; 经过解析后变成如下数据:
request: {
"Active-Remote" = 96355174;
"Apple-Challenge" = "Sx5Dd77BJM5q1v37RhtrQg==";
Body = "";
CSeq = 0;
"DACP-ID" = CB33FA1059D0B517;
Method = "OPTIO_NS * RTSP/1.0";
}
有没有感觉很像JSON?其实就是NSDictionary啦,方便键值编程判断梳理而用;后面文章只发梳理后的数据;
这是一段测试数据,我收到了,必然要回复,对不对; 经过梳理后,我返回了如下数据
RTSP/1.0 200 OK
CSeq: 0
Audio-Jack-Status: connected; type=analog
Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN,OPTIO_NS, GET_PARAMETER, SET_PARAMETER
Apple-Respo_nse:vWJZGiyR/a0f5QOF23ThtJZeKcBklL4BCBh9hOMLDKaF9n9C1hbV8vI5EPXLis1B7fZlYZWUzGh8ElLFA79PX28TSOyrahc6E50HdTQzUeX8Gn5riakuN7wLrPAtErytZPPOUSus0lzaB2VMMgMsHCR5XbiTlbJgNCw9UVFuGVaVyKjZMk1sCrGihYQlPihW296qNvkDLLM+WHbcCgRnuUbZ+TUQqXHu5qLupjWTUlzifCX4E+VnpudXsVYxbUz5I93+72xBzbKYBi8xKyk2kCDH2ZGxUTht2MeAV2KdjSx8ehCoqJdgkxJsDD2g/+NqsWhIdkgp9yTsixlppwG/Zg 注意,以上数据使用OpenSSL加密;key在哪儿?显然,肯定是从airport的rom里挖出来的,感谢德国那变态小伙儿… 描述一下以上的返回的数据键值: CSeq:必须是接收到的CSeq的数值,保持同样;(是不是类似邮件中的那种批处理单号呢?) Audio-Jack-Status:这键值必须是等于: connected; type=analog Apple-Challenge: 这个要细说,根据发过来的Apple-Challenge的数值,进行一定的处理: 1. 原Apple-Challenge的数值要转64位 2. 将ipod的IP地址获得,转为16进制编码,将IP地址分为左块,右块,中间的IPV6自适应,依次以拉丁的编码字符串追加到第一段的后面 3. 继续将自身的IP用utf8的编码追加后面 4. 再用key加密 5. 用base64编码形式 Public: 如果你是收到option的method,那你必须要回答: @"ANNOUNCE, SETUP, RECORD, PAUSE,FLUSH, TEARDOWN, OPTIO_NS, GET_PARAMETER, SET_PARAMETER" 当然了,你的头消息必须是: @"RTSP/1.0 200 OK"; 这段算是Option第一次握手的消息反馈 当你send回去后,你会立即收到第二条消息:
{
"Active-Remote" = 2751919765;
Body = "v=0
\no=AirTunes 15733635783794265891 0 IN IP4 192.168.1.102
\ns=AirTunes
\nc=IN IP4 192.168.1.102
\nt=0 0
\nm=audio 0 RTP/AVP 96
\na=rtpmap:96 AppleLossless
\na=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100
\na=rsaaeskey:JudLbvhjioWkbjIAdb9+sM5MACk8gr7tnftoxC709ClcWR7P+qxrPh3roLc2CHniZvSub+9Tq5IJL4I49vZhHADiKeyxFYGipG/R/kRRzl4sGzKXpDO8infacAMWDb1Ls/7XgAa/8wRG0bSZyiFlZT4CrMLZeXmH/cZhdjWgJ7i1Ae/9QB2OGvRVytySjRpMNcxkvOLhiaK6pecL3fN7fDTD3O984ki1x93C6efr+5pIARjao5TU4yn5TR5o4/cfHEU8/XA43oMk8utZ5dzzO+/CpZ1Ur6oBeLPFt12gamUye4lvY8wvvM20M1CNORVjIJ0ffXoYCzCrqQQM4UOGNg==
\na=aesiv:1xiXS3aeXnfS2fKQ5iTR1w==
\na=min-latency:11025
\n";
CSeq = 1;
"Content-Length" = 611;
"Content-Type" = "application/sdp";
"DACP-ID" = CB33FA1059D0B517;
Method ="ANNOUNCE rtsp://192.168.1.105/15733635783794265891 RTSP/1.0";
"X-Apple-Device-ID" = 0x4cb1991cef64;
}
解释一下: 注意看到CSeq变大了;我们有理由认为, CSeq是一个序列号,用来保证在网络频繁交互的时候,所问得所答; 这一点设计不错;回想过去,我们之前的一些网络交互的应用,咋就没想到弄个序列号来控制呢? Body可以看到很大;有很大的信息量;里面包含了aesiv的值;rsaaeskey的键值;ftmp的数值,大概意思是44100kHz的意思? 于是回复一个消息过去:
RTSP/1.0 200 OK
Audio-Jack-Status: connected; type=analog
CSeq: 1 以上基本代表,音乐服务已经搭设好了; 于是收到第三条消息
request: {
"Active-Remote" = 2751919765;
Body = "";
CSeq = 2;
"DACP-ID" = CB33FA1059D0B517;
Method ="SETUP rtsp://192.168.1.105/15733635783794265891/audio RTSP/1.0";
Transport ="RTP/AVP/UDP;unicast;mode=record;timing_port=58372;events;control_port=49885";
} 好吧,我累了,我先写这么多.明晚继续. |