• airplay:实现


    一.电视机向224.0.0.251发送mdns组播消息并注册

    iPhone设备发现屏幕镜像设备依靠的是mdns协议,这是一个用于局域网发现设备的协议,仿照dns协议,镜像设备启动后,会注册自己到路由器的组播地址224.0.0.251,当iPhone设备发起搜索协议的时候,会发送搜索的信息到224.0.0.251,这时路由器会转发信息到所有曾经注册到224.0.0.251的镜像设备

    参考:https://www.jianshu.com/p/f35ff6e36777


    ._airplay._tcp.local   airPlayPort    7000
    ._raop._tcp.local      airTunesPort   49152

    字典信息

    1. "deviceid" -> "58:55:CA:1A:E2:88"
    2. "features" -> "0x5A7FFEE6""
    3. "flags" -> "0x4"
    4. "model" -> "AppleTV3,1"
    5. "pk" -> "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
    6. "pi" -> "2e388006-13ba-4041-9a67-25dd4a43d536"
    7. "rhd" -> "5.6.0.0"
    8. "pw" -> "false"
    9. "srcvers" -> "220.68"
    10. "vv" -> "2"

    iPhone收到上面的信息之后,还会再次查询txt信息,依然把上面的字典信息返回,这时iPhone上面会看到新的镜像设备My AirPlay Device

    二.建立会话,建立连接与密钥交换

    参考:https://www.jianshu.com/p/ae7eb3fba1e9

    三.一些请求与认证的信息
    1.GET   |   /info

    iPhone没有发送什么信息过来,只有一个请求,这是屏幕镜像设备需要准备比较多的数据,形成一个字典,字典里面可能包含信息对,也可能包含字典,还可以包含字典,并把数据保存为plist二进制形式发送给手机,例如根字典数据如下

     字典数据参考

    1. NSDictionary r_node = new NSDictionary();
    2. r_node["txtAirPlay"] = new NSData(AirPlayServer_mdns.bytesProperties);
    3. r_node["features"] =new NSNumber((UInt64)0x1E << 32 | 0x5A7FFFF7);
    4. r_node["audioFormats"] = audio_formats_node;
    5. r_node["pi"] = new NSString("2e388006-13ba-4041-9a67-25dd4a43d536");
    6. r_node["vv"] = new NSNumber(2);
    7. r_node["statusFlags"] = new NSNumber(68);
    8. r_node["keepAliveLowPower"] = new NSNumber(1);
    9. r_node["sourceVersion"] = new NSString("220.68");
    10. r_node["pk"] = new NSData(HexStringToBytes("b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"));
    11. r_node["keepAliveSendStatsAsBody"] = new NSNumber(1);
    12. r_node["deviceID"] = new NSString("58:55:CA:1A:E2:88");
    13. r_node["name"] = new NSString("My AirPlay Device");
    14. r_node["model"] = new NSString("AppleTV2,1");
    15. r_node["macAddress"] = new NSString("58:55:CA:1A:E2:88");
    16. NSArray audio_formats_node = new NSArray();
    17. NSDictionary audio_format_0_node = new NSDictionary();
    18. audio_format_0_node["type"] = new NSNumber(100);
    19. ...
    20. audio_formats_node.Add(audio_format_0_node);
    21. NSDictionary audio_format_1_node = new NSDictionary();
    22. audio_format_1_node["type"] = new NSNumber(101);
    23. ...
    24. audio_formats_node.Add(audio_format_1_node);
    25. NSArray audio_latencies_node = new NSArray();
    26. NSDictionary audio_latencies_0_node = new NSDictionary();
    27. audio_latencies_0_node["outputLatencyMicros"] = new NSNumber(0);
    28. ...
    29. audio_latencies_node.Add(audio_latencies_0_node);
    30. NSDictionary audio_latencies_1_node = new NSDictionary();
    31. audio_latencies_1_node["outputLatencyMicros"] = new NSNumber(0);
    32. ...
    33. audio_latencies_node.Add(audio_latencies_1_node);
    34. r_node["audioLatencies"] = audio_latencies_1_node;
    35. NSArray displays_node = new NSArray();
    36. NSDictionary displays_0_node = new NSDictionary();
    37. displays_0_node["uuid"] = new NSString("e0ff8a27-6738-3d56-8a16-cc53aacee925");
    38. displays_0_node["widthPhysical"] = new NSNumber(0);
    39. displays_0_node["heightPhysical"] = new NSNumber(0);
    40. ...
    41. displays_node.Add(displays_0_node);
    42. r_node["displays"] = displays_node;
    1. GET /info RTSP/1.0
    2. X-Apple-ProtocolVersion: 1
    3. Content-Type: application/x-apple-binary-plist
    4. CSeq: 0
    5. DACP-ID: DBA1F21D1459CFDD
    6. Active-Remote: 1345566021
    7. User-Agent: AirPlay/665.13.1
    8. content-length: 70
    1. RTSP/1.0 200 OK
    2. CSeq: 0
    3. content-length: 689
     2.POST   |   /pair-setup

    该请求,iPhone没有携带重要的信息,镜像设备发送一个ed25519的public key到iPhone,发送内容作为RTSP的Body部分,该ed25519秘钥对可以在使用的时候才生成

    1. POST /pair-setup RTSP/1.0
    2. Content-Type: application/octet-stream
    3. CSeq: 1
    4. DACP-ID: DBA1F21D1459CFDD
    5. Active-Remote: 1345566021
    6. User-Agent: AirPlay/665.13.1
    7. content-length: 32
    1. RTSP/1.0 200 OK
    2. CSeq: 1
    3. content-length: 32
     3.POST   |   /pair-verify

    这两次请求是非常关键的,首先iPhone发送了自己的加密信息中的公钥部分,也包含签名需要的信息,然后镜像设备进行了签名,并把签名结果返回给iPhone,如果iPhone验证了签名成功,则把再次签名的结果发送给镜像设备来验证,如果镜像设备验证成功,说明双方都得到了对方身份已确认,稍微详细点的信息可以看散列与加密算法的几处实际应用场景中的场景4:AirPlay协议

    1. POST /pair-verify RTSP/1.0
    2. X-Apple-PD: 1
    3. X-Apple-AbsoluteTime: 721822232
    4. Content-Type: application/octet-stream
    5. CSeq: 2
    6. DACP-ID: DBA1F21D1459CFDD
    7. Active-Remote: 1345566021
    8. User-Agent: AirPlay/665.13.1
    9. content-length: 68
    1. RTSP/1.0 200 OK
    2. CSeq: 2
    3. content-length: 96

    4.POST   |   /pair-verify
    1. POST /pair-verify RTSP/1.0
    2. X-Apple-PD: 1
    3. X-Apple-AbsoluteTime: 721822233
    4. Content-Type: application/octet-stream
    5. CSeq: 3
    6. DACP-ID: DBA1F21D1459CFDD
    7. Active-Remote: 1345566021
    8. User-Agent: AirPlay/665.13.1
    9. content-length: 68
    5.POST   |   /fp-setup

    两次请求,body部分都带有数据,分别调用fairplay函数的setup和handshake,返回这两个函数的返回值即可

    1. POST /fp-setup RTSP/1.0
    2. X-Apple-ET: 32
    3. Content-Type: application/octet-stream
    4. CSeq: 4
    5. DACP-ID: DBA1F21D1459CFDD
    6. Active-Remote: 1345566021
    7. User-Agent: AirPlay/665.13.1
    8. content-length: 16
    1. RTSP/1.0 200 OK
    2. CSeq: 5
    3. content-length: 32

    6.SETUP  |   rtsp://172.16.0.105/13682783630232207885

    iPhone请求第二次,iPhone发给镜像设备key,该key经过步骤5和6初始化之后的fairplay解码成一个aes加密算的aeskey,未来传输的视频编码会用aeskey可以来加密,镜像设备准备好事件反馈端口和时间对齐端口发送给iPhone

    1. SETUP rtsp://172.16.0.105/14027482186540797578 RTSP/1.0
    2. Content-Type: application/x-apple-binary-plist
    3. CSeq: 6
    4. DACP-ID: DBA1F21D1459CFDD
    5. Active-Remote: 1345566021
    6. User-Agent: AirPlay/665.13.1
    7. content-length: 656
    1. RTSP/1.0 200 OK
    2. CSeq: 6
    3. content-length: 0
    7.GET_PARAMETER   |   rtsp://172.16.0.105/13682783630232207885

    iPHone查询镜像设备的一些信息,目前在body播放只有如下内容"volume\r\n", 镜像设备可以返回给iPhone的body部分为"volume:0.0\r\n"

    1. GET_PARAMETER rtsp://172.16.0.105/14027482186540797578 RTSP/1.0
    2. Content-Type: text/parameters
    3. CSeq: 8
    4. DACP-ID: DBA1F21D1459CFDD
    5. Active-Remote: 1345566021
    6. User-Agent: AirPlay/665.13.1
    7. content-length: 8
    1. RTSP/1.0 200 OK
    2. CSeq: 8
    3. content-length: 18

    8.RECORD  |   rtsp://172.16.0.105/13682783630232207885

    iPhone发送Record命令,镜像设备返回的RTSP Header中增加一条记录,body为空
    response.AddHeader("Audio-Latency", request.GetHeader("2205"));

    1. RECORD rtsp://172.16.0.105/14027482186540797578 RTSP/1.0
    2. CSeq: 9
    3. DACP-ID: DBA1F21D1459CFDD
    4. Active-Remote: 1345566021
    5. User-Agent: AirPlay/665.13.1
    6. content-length: 0
    1. RTSP/1.0 200 OK
    2. CSeq: 9
    3. Audio-Latency: 11025
    4. Audio-Jack-Status: connected; type=analog
    5. content-length: 0

    9.SETUP  |   rtsp://172.16.0.105/13682783630232207885 
    1. SETUP rtsp://172.16.0.105/14027482186540797578 RTSP/1.0
    2. Content-Type: application/x-apple-binary-plist
    3. CSeq: 10
    4. DACP-ID: DBA1F21D1459CFDD
    5. Active-Remote: 1345566021
    6. User-Agent: AirPlay/665.13.1
    7. content-length: 204
    1. RTSP/1.0 200 OK
    2. CSeq: 6
    3. content-length: 0

    10.SET_PARAMETER  |   rtsp://172.16.0.105/13682783630232207885
    iPhone发送音量或者进度条信息,可以不用处理,返回RTSP 200

    四.建立会话连接后,会向手机端会询问心跳消息(POST  /feedback)回复2秒一次

    iPhone会不间断发送/feedback,里面包含时间信息,可以不用处理,返回RTSP 200

    这时,在步骤11中准备的新tcp服务器,可以开始收到经过步骤7中的提供的秘钥进行aes加密的视频数据了。

    音频数据依然会通过roap.tcp.local来传输,所以现在收到的数据不包含音频数据。

    1. RTSP/1.0 200 OK
    2. CSeq: 6
    3. content-length: 0
    2.电视机通过组播7000端口接收视频流

    3.通过解析裸流当中的类型区分开音频和视频流

    音频数据依然会通过roap.tcp.local来传输

  • 相关阅读:
    2022版 的IDEA创建一个maven项目(超详细)
    面试突击78:@Autowired 和 @Resource 有什么区别?
    Amazon Braket 与量子计算
    Golang时间
    图(最小生成树、最短路径、关键路径)
    前端常见安全问题
    【算法入门-Python】02_递归
    RESTFul:RESTful简介、RESTful的实现、RESTFul案例
    linux内核等待队列wait_queue_head_t
    在线PDF查看器和PDF编辑器:GrapeCity Documents PDF (GcPdf)
  • 原文地址:https://blog.csdn.net/title71/article/details/134447239