要实现airPlay的镜像Server端功能,要从以下几个方面来考虑

  • 1.发现过程
  • 2.协商过程
  • 3.视频传输
  • 4.解密过程

 

发现过程

使用Bonjour,可以参考mDNSResponderjmdns,注册两个服务:airtunesairplayAirplayTxt以及RaopTxt照着demo填写就可以了,也可以根据自己的要求修改其中的值,

需要注意的如下:

airtunes:   “12345@wirelessdisplay” , ”_raop._tcp.”airplay:    “wirelessdisplay” , ”_airplay._tcp.”

参数中”_airplay._tcp.”和’’_raop._tcp.”不可修改。

发布了服务之后,ios设备中应该就可以搜索到Server端了

协商过程

c-s: pair-setup

s-c: xxx

c-s: pair-verify

s-c: xxx

c-s: fp-setup

s-c: xxx

c-s: fp-setup

s-c: xxx

setup

setup response

setup

setup respnose

pair-setup,pair-verify配对验证,Server端根据收到的信息进行回复,格式类似RTSP

fp-setup(第一次和第二次)Fairplay相关。

第一次setup阶段会收到一大串数据,是按照plist格式生成的,用相应的方法进行解析可以得到ekeyeiv等信息,用于后续的解密。

第二次setup阶段获得type,通过type来判断视频数据或音频数据,通知接收端建立传输通道准备进行音视频数据的发送,加密过的屏幕镜像数据通过指定的端口(一般为7100)发送到接收端。

接收端收到发送过来的数据后进行解密,解密后的数据就可以进行播放了。

中间也可能会有GET_PARAMETERSET_PARAMETER来调整音量等信息。

 

解密部分目前主要有两种方法:

1.从apptvmacOS获取。

2.从市面上已有的可投屏产品中获取。

AIRPLAY镜像投屏过程中,音视频数据都是加密过的,对于接收端来说,需要正确解密后才能对音视频数据进行处理,音频和视频的解密过程还不一样。音频相对简单一点,视频会复杂一些。这一块的解密过程是没有公开的,是苹果自身的Fairplay DRM协议部分。现在市面上的第三方Airplay接收端无非都是通过逆向过程破解了相关部分。

本文针对音频的处理做一个介绍,音频部分的处理相对简单一点。

解密过程:

1 音频采用AES CBC128进行加密,这一部分可以使用开源的openssl库进行处理

2 该算法需要解密的输入参数包括aeskeyaeskiv,通过ANNOUNCE请求中携带,ANNOUNCE请求同时还会携带音频的编码信息。

通过解密过程后,我们会得到AAC编码的音频数据,播放器播放AAC数据还需要对其进行解码。

在我们实现的接收端程序,协商出来的是AAC-ELD编码。对于AAC的解码,可以使用一些开源的库,如fdk,ffmpeg等,也可以使用android提供的MediaCodec进行解码。

但笔者曾经在某些Android手机上发现,解码AAC-ELD有问题。推荐大家用fdk进行解码。

使用fdkaac进行解码,其实在网上也能找到很多例子,但笔者发现很多例子有一处错误,在低版本的fdk上不会出现错误,但是在高版本的fdk会出现crash这样的问题。话不多说,直接通过部分代码来说明过程。

初始化解码器:

  1. UCHAReld_conf[] = { 0xF8, 0xE8, 0x50, 0x00 };          //44100,2channels,s16
  2. UCHAR*aac_eld_conf[] = { eld_conf };                   //TODO just for aac eld config
  3. staticUINT aac_eld_conf_len = sizeof(eld_conf);
  4. decoder = aacDecoder_Open(TT_MP4_RAW, 1);
  5. AAC_DECODER_ERROR ret = aacDecoder_ConfigRaw(decoder, aac_eld_conf, &aac_eld_conf_len);
  6. buffer = newINT_PCM[960];
  7. buffer_size = 1920;
  8. pcm_size = 960;

上述代码中eld_conf这一块的值对应android MediaCodec aacCSD buffer #0具体什么含义看规范吧。

每次编码和发送的采用数为480,故下面申请对应长度的Buffer

 

解码过程:

  1. bytesValid = dataLen;
  2. while(bytesValid){  
  3. ret = aacDecoder_Fill(decoder, reinterpret_cast<UCHAR**>(&p_frame), (UINT*)&size, &bytesValid);
  4. if (ret != AAC_DEC_OK) {
  5. printf(“aacDecoder_Fill return %x.\r\n”, ret);
  6. return;
  7. }
  8. for(;;) {
  9. ret = aacDecoder_DecodeFrame(decoder, buffer, pcm_size, 0);
  10. if(ret == AAC_DEC_OK) {
  11. dump_audio_data((unsigned char*)buffer,buffer_size);
  12. elseif (ret == AAC_DEC_NOT_ENOUGH_BITS)
  13. break;
  14. else{
  15. printf(“aacDecoder_DecodeFrame return %x.\r\n”, ret);
  16. return;
  17. }
  18. }
  19. }

aacDecoder_DecodeFrame填入的参数为pcm_size,其单位为short,而不是byte。网上的例子很多都是在这里错误。请各位务必注意。

必捷网络现提供技术全国领先的Airpaly SDK苹果无线投屏软件开发工具包,支持同时多路,支持镜像/视频/图片/音乐,支持MAC OS/iOS6/iOS7/iOS8/iOS9/iOS10/iOS11投屏,支持Linux/Android/Windows/X86/ARM/MIPS等,可根据需求定制开发,通过电话或者在线咨询联系我们获取更多无线投屏sdk(airplay sdk、miracast sdk)相关信息,欢迎咨询!