AirPlay 协议深度解析:从 mDNS 发现到 RTP 投屏的工程实践

发布时间:2026/9/30 7:51:47

AirPlay 协议深度解析:从 mDNS 发现到 RTP 投屏的工程实践 简介这份文档是面向 iOS 开发、逆向工程与流媒体协议研究者的 AirPlay 非官方协议规范整理聚焦苹果设备间媒体共享的底层通信机制。内容系统梳理了 AirPlay 协议族的核心模块包括基于 MulticastDNS 与 Bonjour 的服务发现、AirTunes 与 AirPlay 两类服务的发布方式以及照片共享中的 HTTP 请求、事件处理、缓存与幻灯片机制视频共享的请求与事件流程音频部分的 RTSP 控制、RTP 流传输、音量控制、元数据管理、AirPort Express 认证与远程控制还涵盖屏幕镜像的流媒体包与时间同步、密码保护机制并附有协议演进历史及 IETF RFC、草案与苹果协议等参考资源。资源包为单个 doc 文档约 511KB结构按章节编号组织便于按模块查阅。目前已有 1412 人学习适合需要理解 AirPlay 通信细节、开展协议分析或兼容性开发的读者作为参考资料。1. AirPlay 协议从发现到投屏一条链路拆开看AirPlay 是 Apple 那套「设备发现 音视频投送 镜像」的私有协议族跑在局域网里底层靠 mDNS/Bonjour 做服务发现控制信令走 HTTP/TCP音视频流走 RTP/UDP镜像场景还会用到 H.264 编码加 FairPlay 加密。很多人第一次接触它是因为想把 iPhone 画面投到非 Apple 设备上或者想在自己的 App 里加一个「投屏」按钮结果一搜全是「买盒子」的答案。这篇不聊硬件只聊协议本身它怎么被发现、怎么握手、怎么传流、哪些参数决定成败。适合做音视频开发、智能硬件对接、车机投屏、会议大屏的工程师也适合想自己写一个 AirPlay 接收端来跑通链路的折腾型选手。读完你能判断这个方向值不值得投入最小可跑通的路径长什么样以及最容易翻车的地方在哪。2. AirPlay 的服务发现与握手mDNS 广播里到底藏了什么2.1 为什么 AirPlay 设备一插电就能被 iPhone 看见AirPlay 不靠扫描 IP 段也不靠云端注册。它依赖 mDNSMulticast DNS也就是 Bonjour 的底层实现。设备启动后会向 224.0.0.251:5353 这个组播地址发一条 PTR 查询问「谁有 _airplay._tcp 这个服务」。支持 AirPlay 的设备会回一条 SRV 记录里面带主机名和端口再跟一条 TXT 记录里面塞了一堆键值对。这些键值对才是关键deviceid是设备唯一标识features是一个位掩码决定这台设备支持视频、镜像、音频还是照片model告诉你它是 Apple TV 还是第三方接收端pk是公钥后面配对要用。我一般排查「iPhone 搜不到设备」时第一步就是抓 mDNS 包。用avahi-browse或者dns-sd都能看。Linux 上# 浏览局域网内所有 AirPlay 服务 avahi-browse -rt _airplay._tcp # 如果只想看原始 TXT 记录用 dns-sdmacOS 自带 dns-sd -B _airplay._tcp逻辑说明-rt表示解析resolve并终止terminate输出里会列出服务名、主机、端口和 TXT 内容。参数上_airplay._tcp是 AirPlay 视频/镜像的主服务类型音频单独走_raop._tcp。如果这里看不到任何记录说明要么设备没发广播要么你的机器和它不在同一个二层网络里——很多「搜不到」的根因是 AP 隔离或者 VLAN 划分。TXT 记录里的features字段值得单独说。它是一个 64 位整数不同位代表不同能力。比如 bit 0 是「支持视频」bit 9 是「支持镜像」bit 11 是「支持音频」。第三方接收端如果 features 设错iPhone 会认为它不支持镜像投屏按钮直接灰掉。常见做法是参考开源实现里已经验证过的值比如0x5A7FFFF7,0x1E这种组合但具体要看你实现到哪一步。2.2 配对与加密FairPlay 握手不是必须一次跑通发现之后iPhone 会向设备的 7000 端口默认发一个 HTTP POST路径是/pair-setup。这一步用的是 SRPSecure Remote Password协议交换公钥、算共享密钥最后派生出一对 Ed25519 密钥。之后每次连接走/pair-verify用之前派生的密钥做挑战应答。整个过程是二进制 plist 格式不是 JSON解析时要注意。如果你只是想做音频投送很多开源实现会跳过完整 FairPlay直接用ANNOUNCESETUP走 RTSP 风格的信令。但镜像场景绕不开加密因为视频流本身用 AES-128-CBC 加密密钥在 SETUP 阶段通过 FairPlay 交换。我踩过的坑是pair-setup 返回的 plist 里pk字段是公钥salt和seq用来算 SRP 的 proof少一个字段后面全挂。一个最小握手流程可以这样验证# 第一步发 pair-setup 请求需要构造二进制 plist body curl -v -X POST http://192.168.1.100:7000/pair-setup \ -H Content-Type: application/octet-stream \ --data-binary pair_setup.bin # 第二步拿到响应后再发 pair-verify curl -v -X POST http://192.168.1.100:7000/pair-verify \ -H Content-Type: application/octet-stream \ --data-binary pair_verify.bin参数说明pair_setup.bin和pair_verify.bin需要按协议规范构造不是随便填的。响应里如果返回200且 body 里有pk和proof说明第一步过了。如果返回403大概率是 SRP 计算错了或者设备已经配对过、需要先发/pair-pin-start走 PIN 码流程。提示调试阶段可以用 Wireshark 抓包过滤tcp.port 7000看请求和响应的十六进制比对着开源实现里的 plist 结构逐字段核对比盲猜快得多。3. 音视频流怎么传RTP 打包、时间戳与缓冲策略3.1 音频走 RAOP视频走 AirPlay两条流别混AirPlay 音频和视频是分开的。音频走 RAOPRemote Audio Output Protocol服务类型是_raop._tcp端口通常 5000 左右。视频和镜像走_airplay._tcp端口 7000。音频流用 RTP 封装 AAC 或 ALAC视频流用 RTP 封装 H.264。两者都靠 RTCP 做同步但音频对延迟更敏感视频对带宽更敏感。音频的 SETUP 请求里会带audioFormat、ctcompression type、srsample rate、chchannels。常见组合是ct2表示 AAC-LCsr44100ch2。如果接收端不支持这个格式iPhone 会降级到 PCM但延迟会变大。我一般会在 SETUP 响应里明确写支持ct2和ct1PCM让发送端自己选。视频的 SETUP 更复杂要协商分辨率、帧率、编码 profile。iPhone 镜像默认发 1080p 或 720pH.264 High Profile码率自适应。接收端如果解码能力弱可以在 SETUP 响应里把width和height改小但 iPhone 不一定听——它有自己的策略。3.2 RTP 包结构时间戳和序列号怎么用每个 RTP 包头 12 字节后面跟 payload。音频的 payload 是 AAC 帧视频是 H.264 NAL 单元。时间戳字段是 32 位音频按采样率递增视频按 90000 Hz 时钟递增。序列号每发一个包加一用来检测丢包。接收端要做三件事第一按序列号排序丢掉重复包第二按时间戳做 jitter buffer音频一般 50-200ms视频可以到 500ms第三用 RTCP 的 SRSender Report做音视频同步。我见过有人只收流不排序结果声音断断续续查了半天以为是网络问题其实是序列号回绕没处理。一个简单的 RTP 解析片段import struct def parse_rtp_header(data): # RTP 头固定 12 字节 version, payload_type, seq, timestamp, ssrc struct.unpack(!BBHII, data[:12]) version version 6 payload_type payload_type 0x7F return { version: version, payload_type: payload_type, seq: seq, timestamp: timestamp, ssrc: ssrc, payload: data[12:] } # 用法从 UDP socket 收到包后直接解析 # pkt parse_rtp_header(raw_data) # 然后按 pkt[seq] 排序按 pkt[timestamp] 做缓冲逻辑说明struct.unpack里!BBHII对应大端序的 1 字节版本填充、1 字节负载类型、2 字节序列号、4 字节时间戳、4 字节 SSRC。参数上payload_type对音频通常是 96动态分配对视频也是 96具体要看 SETUP 阶段的协商结果。如果version不是 2说明包格式不对可能抓到了 RTCP 包。注意RTP 序列号是 16 位从 65535 回到 0 时排序逻辑要处理回绕否则 jitter buffer 会以为丢了一大片包疯狂重传请求。4. 镜像场景的坑H.264 解码、时钟同步与带宽自适应4.1 镜像流不是标准 H.264 文件流iPhone 镜像发出来的 H.264 是实时流没有 SPS/PPS 的重复发送只在开头发一次。如果接收端启动晚了没收到 SPS/PPS后面所有帧都解不了。常见做法是在 SETUP 响应里要求发送端在每个 IDR 帧前重复 SPS/PPS但 iPhone 不一定支持。更稳的办法是缓存第一个 SPS/PPS后面手动拼到每个 IDR 帧前面。另一个坑是时间戳。镜像流的 RTP 时间戳基于 90000 Hz但 iPhone 的屏幕刷新率是 60Hz 或 120Hz时间戳增量不固定。如果接收端用固定帧率去算显示时间画面会忽快忽慢。正确做法是用 RTCP 的 SR 包里的 NTP 时间戳和 RTP 时间戳做映射算出每个帧的实际显示时间。4.2 带宽自适应别跟 iPhone 对着干iPhone 会根据网络状况动态调整码率和分辨率。如果接收端反馈丢包多它会降码率如果反馈延迟大它会降分辨率。很多人想让 iPhone 固定发 1080p就在 SETUP 里写死参数结果 iPhone 直接断开。我的经验是在 SETUP 响应里如实报告接收能力比如width1920、height1080、fps60但不要拒绝它后续的调整请求。如果带宽不够宁可让它降也不要硬撑否则花屏比低分辨率更难看。排查花屏时先看 RTCP 的丢包统计。如果丢包率超过 5%基本就是网络问题如果丢包率低但花屏大概率是解码器没处理好 SPS/PPS 或者参考帧丢失。可以用ffmpeg拉流测试# 从 AirPlay 接收端拉 H.264 流看能不能解 ffmpeg -f rtp -i rtp://192.168.1.100:6000 -c:v copy test.h264 # 如果 test.h264 能播放说明流没问题问题在接收端解码 ffplay test.h264参数说明-f rtp指定输入格式-i后面是接收端的 RTP 地址和端口-c:v copy表示不转码直接存。如果 ffmpeg 报错「SPS/PPS not found」说明流里缺关键帧头需要在接收端补。5. 避坑与排查AirPlay 对接中最容易翻车的 5 个点5.1 现象iPhone 能看到设备但点投屏没反应原因TXT 记录里的features位掩码设错iPhone 认为设备不支持镜像。或者/pair-setup返回了非 200但 mDNS 广播还在iPhone 以为设备可用。解决用avahi-browse -rt _airplay._tcp确认 TXT 里features的值对照开源实现里验证过的值改。同时抓 7000 端口的包看 pair-setup 的响应码。如果是 403检查 SRP 计算里的salt和seq是否和请求一致。5.2 现象音频能播但视频黑屏原因视频 RTP 流收到了但 H.264 解码器没拿到 SPS/PPS或者解密密钥不对。镜像流的视频是加密的密钥在 SETUP 阶段通过 FairPlay 交换如果 FairPlay 没跑通视频流是密文解码器直接报错。解决先确认 SETUP 响应里有没有streams数组里面type为 110 的流是视频encryption字段如果是AES-128-CBC说明要解密。检查 FairPlay 握手是否返回了ekey和eiv这两个是解密用的。5.3 现象投屏延迟越来越大最后卡死原因jitter buffer 只增不减或者 RTCP 反馈没发iPhone 以为网络很好一直加码率。接收端解码不过来缓冲区爆掉。解决给 jitter buffer 设上限音频 200ms视频 500ms超过就丢旧帧。同时定期发 RTCP RR 包报告丢包率和延迟让 iPhone 降码率。RTCP 包不用自己构造用libavformat或者gstreamer的rtpbin会自动处理。5.4 现象mDNS 能发现但 TCP 连接被拒绝原因设备防火墙没放行 7000 端口或者服务只监听了 IPv6iPhone 走 IPv4 连不上。解决netstat -tlnp | grep 7000看监听地址。如果是:::7000说明只监听了 IPv6改成0.0.0.0:7000或者同时监听。防火墙用iptables -L或ufw status确认。5.5 现象配对成功一次后第二次连不上原因pair-verify 用的密钥没持久化设备重启后丢了。或者 iPhone 那边缓存了旧的配对信息设备这边已经重置。解决把 pair-setup 派生的 Ed25519 密钥存到文件或数据库重启后加载。如果设备重置了iPhone 需要「忘记此设备」再重新配对。调试时可以在设备端删掉配对记录让 iPhone 重新走 pair-setup。6. 进阶技巧用 Wireshark 和开源实现交叉验证如果你想把 AirPlay 接收端做到能日常用光看协议文档不够得拿真实流量对着开源实现逐字段核对。我一般会同时开三个窗口Wireshark 抓包、开源实现的日志、iPhone 的操作。Wireshark 过滤tcp.port 7000 || udp.port 6000看信令和流。开源实现推荐看shairport-sync音频和RPiPlay镜像前者代码干净后者虽然老但协议流程完整。一个具体技巧在 SETUP 阶段iPhone 会发一个二进制 plist里面streams数组的每个元素都有controlPort和dataPort。控制端口走 RTCP数据端口走 RTP。如果你只监听了 dataPort 没监听 controlPortRTCP 反馈发不出去iPhone 会以为网络单向过一会儿就断。我当初就是漏了 controlPort查了两天才发现。另一个技巧是时间同步。镜像场景下音视频同步靠 RTCP SR 里的 NTP 时间戳。接收端要把 NTP 时间转成 Unix 时间再和本地时钟对齐。如果本地时钟和 iPhone 差太多音画不同步会很明显。可以用 NTP 客户端定期同步或者直接在接收端用 RTCP SR 里的时间戳做相对同步不依赖绝对时间。最后说一个我自己的习惯每次改完 SETUP 响应里的参数一定用dns-sd -B _airplay._tcp确认设备还在广播再用 iPhone 连一次。因为有些参数改错会导致 mDNS 广播正常但 TCP 握手失败iPhone 不会报错只是投屏按钮转圈然后消失。这种时候只能靠抓包看 7000 端口有没有 SYN 重传。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/30 7:51:47

iOS网络调试:请求发出、系统代理与绕过代理排查指南

做iOS开发和网络调试的人都知道,最头疼的问题不是接口报错,而是请求压根没动静。代码里调了接口,界面上没有任何反应,抓包工具里也看不到一条记录。这时候你脑子里会蹦出三个问题:请求到底发出去了没有?它有…

2026/9/30 7:46:46

Kafka集群迁移实战:基于MirrorMaker2的平滑切换方案

在Kafka运维和架构改造的日常里,集群迁移算不上高频,但每次遇到都是硬仗。尤其是跨机房搬迁、版本升级、或者干脆把自建集群整体挪到云上,稍有不慎就是丢消息、重复消费、业务链路中断的连环事故。我经手过好几次Kafka集群迁移,从…

2026/9/30 8:46:55

ROS导航仿真入门:从SLAM建图到move_base自主导航全流程

ROS学习系列走到第7篇,意味着你已经不是第一天对着终端敲命令的新人了。前面的章节里,你可能已经见过turtlesim里那只到处乱跑的海龟,写过自定义的消息类型,也大概弄懂了节点和话题之间是怎么传数据的。但"导航仿真"这一…

2026/9/30 8:46:55

Linux配置新世纪五笔实战:fcitx5码表与Rime方案

1. 为什么在 Linux 上想把新世纪五笔"据为己有"我平时主要用 Linux 干活,中文输入这块折腾过不少输入法框架,最后停在 fcitx5 上。原因很简单:fcitx5 在当前 Linux 输入法生态里属于又稳又灵活的那一档,配置不反人类&am…

2026/9/30 8:46:55

SpringBoot+Vue3高校宣讲会管理系统:从设计到部署全解析

1. 项目背景与选题逻辑:为什么高校宣讲会管理系统值得做一套每年秋招春招一到,高校就业办和各大企业的HR就忙得脚不沾地。很多学校到现在还在拿Excel表格登记宣讲会场地、用微信群转发企业招聘信息,学生想看场次安排得同时加四五个群&#xf…

2026/9/30 8:46:55

AI工程从零开始:提示词、Agent编排与质量监控实战指南

很多人一看到"AI工程从零开始"这个题目,都会本能地以为是从搭一个神经网络开始训大模型。其实我在一线做了这么久的AI项目落地,最真实的感受正好相反——从零开始的AI工程,第一步不是炼丹,而是搞清楚你要解决的问题到底…

2026/9/30 8:41:55

数据中台与数据服务:一体两面的关系与建设实践

数据服务与数据中台的关系,这话题说起来有点“老生常谈”,但我发现身边真正把它想明白的人并不多。很多人以为数据中台就是搞一套大数据平台,数据服务就是写一堆API接口;还有人觉得中台是“战略”,服务是“落地”&…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑