ios开发——AirPlay

359 阅读5分钟

研究的是airplay的receiver,也就是接收方。

首先,ipad能在家庭无线网络里神奇般的搜寻到receiver; 搜到后,还能继续勾搭互联互通上; 基本推算出来的原理大概如此;

那么,搜到recevier并且确定receiver的IP;那么肯定是用了bonjour服务;其实就是mDNS这个技术;mDNS就是mini DNS的意思;公网用网址域名对应IP,那么小网络怎么办?只能寄托mDNS,即很小的微型DNS,这个功能是由路由器代替实现的;

原理如下,每当有局域网的client申请路由器代为广播,谁需要airplay服务就找他;那这个client就向路由器申请mDNS的service,提供名字,功能类型,端口,等等; 然后路由器会有一个hash表,存着各个client申请的service表; 每当有client希望找到airplay的服务的时候,它就会去问路由器,谁提供airplay服务?路由器会表里查询,并且告诉你IP; 同理,airprint也是这个类似原理;

看文档得知,Apple TV一共publish两个服务;一个是airtunes的ROAP协议;一个是airplay的service,包含照片,视频,镜像; 好吧,下面代码演练一下;

NSNetService*publish=[[NSNetService alloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:7000];
[publish publish];
if(airplay== nil)
airplay= [[AirPlayControlleralloc] initWithWindow:window];
[airplaystartServer];
- (void)publishBonjour
{
if(type)
{
netService= [[NSNetServicealloc] initWithDomain:domaintype:typename:nameport:[asyncSocketlocalPort]];
[netServicesetDelegate:self];
}
其中domain= @"local.";
[httpServersetType:@"_airplay._tcp."];
这么推测下来,我的前期准备airplay都是对的,但是仅在photo里发现,肯定有问题;最终的问题,就是看端口了;
蛋疼的是,我看到这段代码
dispatch_sync(socketQueue, ^{
// No need for autorelease pool
if(socket4FD!= SOCKET_NULL)
result= [self localPortFromSocket4:socket4FD];
elseif(socket6FD!= SOCKET_NULL)
result= [self localPortFromSocket6:socket6FD];
});

在socketQueue的任务队列里,寻找端口号
实际在如下函数里,传入port为0,系统会自动给你分配一个随机端口;

success = [asyncSocket acceptOnInterface:interfaceport:porterror:&err];

NSNetService*publish=[[NSNetService alloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:56486];
NSDictionary*txtRecordDic=[NSDictionary dictionaryWithObjectsAndKeys:
@"0x7", @"features",
[DeviceInfoplatform], @"model",
[DeviceInfodeviceId], @"deviceid",
nil];
NSData*txtRecordData =nil;
if(txtRecordDic)
txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDic];
[publish setTXTRecordData:txtRecordData];//very Important
[publish publish];

deviceid是必备的,不能随意修改的;而且数值也必须要符合device ID的规则,例如: 10:9A:DD:65:19:3D

代码压缩成如下:

NSNetService*publish=[[NSNetServicealloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:56486];
NSDictionary*txtRecordDic=[NSDictionarydictionaryWithObjectsAndKeys:
[DeviceInfodeviceId], @"deviceid",
nil];
NSData*txtRecordData =nil;
if(txtRecordDic)
txtRecordData = [NSNetServicedataFromTXTRecordDictionary:txtRecordDic];
[publish setTXTRecordData:txtRecordData];//very Important
[publish publish];

好吧,研究到底告一段落,这段研究,实现了mDNS最神奇的一部分,苹果的规则,要求airplay字段,tcp类型,local局域网内,并且附带一个text,内容必须是device id,而且数值要符合规范.
接下来的几天,我将继续研究airplay的实现原理;
比如iphone/ipad搜到airplay的receiver后,必然要有业务交集;比如send一个照片,send一段音频,放一段视频,等等,我作为receiver该如何交接请求等等.

Ok,还记得new了一个GCDAsyncSocket,并且让他监听这个端口; 现在我用photo里选择airplay,同步一个照片显示过去;

果然,在TCP的如下函数,收到了动作

- (void)socket:(G_C_DAsyncSocket*)sock didAcceptNewSocket:(G_C_DAsyncSocket*)newSocket
它把接收到的新socket的信息筛选,重新打包,用Http标准信息包发了一个报文过去
if([method isEqualToString:@"GET"]&& [path isEqualToString:@"/server-info"])
{
NSString*str = @"<?xml version=\"1.0\"encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTDPLIST 1.0//EN\"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plistversion=\"1.0\"><dict><key>deviceid</key><string>58:55:CA:06:BD:9E</string><key>features</key><integer>119</integer><key>model</key><string>AppleTV2,1</string><key>protovers</key><string>1.0</string><key>srcvers</key><string>101.10</string></dict></plist>";
NSData*response = [strdataUsingEncoding:NSUTF8StringEncoding];
HTTPDataResponse*res = [[HTTPDataResponsealloc] initWithData:response];
[res setHttpHeaderValue:@"text/x-apple-plist+xml"forKey:@"Content-Type"];
return[res autorelease];
}

这段代码,显示了苹果的设计airplay的思路;
即用tcp长连接来简单/核心的控制,来回事件相应;用http消息来处理内容,消息;
所以代码常常从TCP socket到Http message互相倒;

airtunes如何实现:

self.netService= [[NSNetServicealloc] initWithDomain:@""type:@"_raop._tcp"name:[NSStringstringWithFormat:@"%@@%@", [selfMACAddressToRawString], self.name] port:MSShairportServerPort];

_raop._tcp airtunes的类型;仅在mirror和ipod里能看到;
name是mac地址;个人觉得这个是可选项,可自定义
port 是5000

AirPlay 是 Apple 的一种无线流媒体技术,它允许用户从其 iOS 设备、macOS 或其他支持的设备流式传输音频、视频、照片和屏幕内容到 Apple TV 或兼容的扬声器和智能电视上。AirPlay 的实现结合了多种技术和协议,以下是其主要实现原理:

Wi-Fi 网络:

AirPlay 使用 Wi-Fi 网络来传输数据。设备上的内容通过 Wi-Fi 发送到接收设备(如 Apple TV)。这要求发送设备和接收设备在同一个 Wi-Fi 网络下,虽然新版的 AirPlay 2 可以支持部分离线使用。

Bonjour 协议:

AirPlay 依赖于 Bonjour 服务发现协议来找到网络中的可用设备。Bonjour 使用多播 DNS(mDNS)来动态发现网络上的设备和服务,这使用户可以方便地找到支持 AirPlay 的设备。

音视频编解码:

对于音频,AirPlay 使用了 Apple Lossless 音频编解码(ALAC)来确保高质量的音频传输。 对于视频,AirPlay 对视频内容进行实时编码和传输,支持 H.264 编码以平衡质量和带宽。

流式传输控制:

AirPlay 允许用户控制播放(如播放、暂停、快进、音量调节等)直接从 iOS 设备或者 Mac 上。控制信息通过网络发送到接收设备。

屏幕镜像:

AirPlay 提供屏幕镜像功能,可以将设备屏幕的实时内容显示到支持的显示设备上。这个功能通过编解码当前屏幕内容并通过 Wi-Fi 实时传输来实现。 屏幕镜像使用了一种低延迟的视频传输,确保在大多数使用场景下能够提供流畅的用户体验。

AirPlay 2:

AirPlay 2 是 AirPlay 的升级版本,增强了多房间音频支持以及更好的音频同步。在 AirPlay 2 中,可以同时控制多台设备的音频流,可以在多个房间中同步播放音频。 AirPlay 2 也支持通过 iCloud 管理更复杂的音频设置和设备。

通过这些技术,AirPlay 提供了一种无缝、用户友好的方式来在苹果设备之间共享媒体内容,增强了苹果生态系统中的内容消费体验。