自动化工具 硬货系列 (二)!!使用 python 实现高帧率、低延时、支持多个 iOS 设备同时屏幕共享的工具

YueChen · March 17, 2021 · Last by YueChen replied at February 29, 2024 · 19641 hits
本帖已被设为精华帖!

前段时间发布了一个 python 获取 iOS 性能数据的文章,也算开了个小口子能在获取 iOS 测试数据上更加方便了些,如果对 iOS 性能相关兴趣可以移步:https://testerhome.com/topics/27159

屏幕共享和远控平台现在发展也比较火热,但是 iOS 设备在画面同步和视频录制上,一直都没有一个比较不错的方案,也简单调研了下之前可以获取到 iOS 屏幕数据的方法:

  • iOS-minicap stf 团队实现的帧率非常不错,缺点无法多台设备
  • Airplay mirror 苹果多媒体多屏互动技术,效果都很好,有一点点缺陷就是同一个 wifi 下每次需要手机主动触发屏幕镜像才可以同步画面
  • com.apple.mobile.screenshotr 协议,这个也挺慢的
  • XCTest 和 XCUITest 二次改造的 wda 可以支持多台设备,但是帧率延时还是偏低
  • 基于 WebRtc iOS 屏幕共享,这个需要 SDK 嵌入 APP 支持,而且需要端上主动触发,操作起来不是很方便。
  • 等等...

本项目介绍

该项目是 python 实现可以通过 USB 连接 iOS 设备进行屏幕共享,支持:

  • 高帧率(30〜60fps)
  • 高画质
  • 低延迟
  • 秒启动
  • 非侵入性 (无需任何安装和代码嵌入)
  • 支持 iOS 多设备并行

项目地址:https://github.com/YueChen-C/ios-screen-record 先点个小星星吧

Mac OSX 安装

  1. brew install libusb pkg-config
  2. 如需使用 gstreamer 媒体服务则需要安装 brew install gstreamer gst-plugins-bad gst-plugins-good gst-plugins-base gst-plugins-ugly
  3. python install -r requirements.txt

使用

usb 连接你的 iOS 手机,解锁并信任喲 (手机锁屏不行)

# 可以使用 vlc 工具播放udp地址: udp/h264://@:8880
# 直接转发 h264 到 udp 广播,因为 mac 限制 udp 大小,要切割包,所以延时会变高,暂时仅作为测试使用
$ main.py --udid=xxxx udp

# 录制 h264/wav 文件, 使用 vlc 工具打开文件
$ main.py --udid=xxxx record -h264File=/home/out.h264  -wavFile=/home/out.wav

# gstreamer 媒体流工具渲染显示画面,推荐方式
$ main.py --udid=xxxx gstreamer

基本原理

usb 相关说明

每个 usb 连接设备时都会有一些配置信息,我们数据交互时,会使用某个配置与 usb 设备进行交互,这里用个 iOS 设备举例:

当我们使用 LibUsb 这个库 https://libusb.info/ 获取 iOS USB 设备信息时可以获取到配置信息 bNumConfigurations 5 个, 下面部分信息片段:

DEVICE ID 05ac:12a8 on Bus 020 Address 031 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x05ac
 idProduct              : 0x12a8
 bcdDevice              : 0x1208 Device 18.08
 iManufacturer          :    0x1 Apple Inc.
 iProduct               :    0x2 iPhone
 iSerialNumber          :    0x3 *********************
 bNumConfigurations     :    0x5
 CONFIGURATION 1: 500 mA ==================================

如何开启隐藏配置

事实上在 iOS USB 级别上还有个隐藏配置信息,用来传输屏幕音视频相关数据,pyhton 开启方式 device.ctrl_transfer(0x40, 0x52, 0, 2, b'') 发送了这个指令之后,再次获取配置信息时,就会发现 bNumConfigurations 的数量变成了 6 个,多出来这个配置信息就是我们要使用的,使用这个 USB 配置,并连接相应端口后,就能传输音视频画面了

接口端点定位

虽然我们使用这个音视频传输配置,但是这个配置下面还有多个 INTERFACE 接口,但是只有 bInterfaceSubClass=0x2A 这个接口才是需要用的,因此要还需要定位到这个配置下,然后会看到 INTERFACE 下面还有两个端口 ENDPOINT 0x86: Bulk IN(用来接收数据) 和 ENDPOINT 0x5: Bulk OUT(用来发送数据),到此 usb 设置相关基本完成了

INTERFACE 2: Vendor Specific ===========================
    bLength            :    0x9 (9 bytes)
    bDescriptorType    :    0x4 Interface
    bInterfaceNumber   :    0x2
    bAlternateSetting  :    0x0
    bNumEndpoints      :    0x2
    bInterfaceClass    :   0xff Vendor Specific
    bInterfaceSubClass :   0x2a
    bInterfaceProtocol :   0xff
    iInterface         :   0x11 Valeria
     ENDPOINT 0x86: Bulk IN ===============================
      bLength          :    0x7 (7 bytes)
      bDescriptorType  :    0x5 Endpoint
      bEndpointAddress :   0x86 IN
      bmAttributes     :    0x2 Bulk
      wMaxPacketSize   :  0x200 (512 bytes)
      bInterval        :    0x0
     ENDPOINT 0x5: Bulk OUT ===============================
      bLength          :    0x7 (7 bytes)
      bDescriptorType  :    0x5 Endpoint
      bEndpointAddress :    0x5 OUT
      bmAttributes     :    0x2 Bulk
      wMaxPacketSize   :  0x200 (512 bytes)
      bInterval        :    0x0

如果想分析 usb 数据的话执行:sudo ifconfig XHC20 up 命令后使用 wiershark 抓网卡 XHC20 就可以看到 部分 usb 数据交互

开始传输数据

大概流程

  1. 启用隐藏设备配置信息
  2. 锁定开启传输端点
  3. 等待接收 PING 包
  4. 用 PING 包响应
  5. 等待 SYNC CWPA 数据包接收设备音频 时间戳 >>>开始音频交互
  6. 创建本地时间戳记录,将该时间戳放入 SYNC CWPA 并发送
  7. 发送 ASYN_HPD1(参数参考 ios 的 CoreAudio 框架)
  8. 发送 ASYN_HPA1(参数参考 ios 的 CoreAudio 框架)和在步骤 6 中接收到的设备音频 时间戳
  9. 接收同步 AFMT 并返回没有错误信号 (表示准备就绪)
  10. 接收 CVRP 视频 时间戳 >>>开始视频交互
  11. 使用本地视频 时间戳 回复
  12. 使用步骤 10 的时间戳 发送 NEED 消息
  13. 接收两个 ASYN
  14. 接收 CLOK 消息,创建新的时间戳记录并回复消息
  15. 接收 TIME 消息,使用 14 步创建的时间回复消息

前面交互完成后就能正式接收音视频消息了

如果想具体了解相关传输报文协议可以查看下方链接,本项目是参考这个大佬文章,最终使用 python 来实现的 https://github.com/danielpaulus/quicktime_video_hack/blob/master/doc/technical_documentation.md

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 34 条回复 时间 点赞
陈恒捷 将本帖设为了精华贴 18 Mar 00:05

赞,已 star

这个会有什么限制么,比如 iOS 版本之类的?

🐮🍺

YueChen #5 · March 18, 2021 Author
陈恒捷 回复

测试 10 ~ 14,目前都可以连接传输数据

大佬能看下这个错误怎么解决吗😥 ,试着拿一台 iOS12.0.1 的 6sp 跑了一下一直报 Resource busy

$ python3 main.py -u xxxx udp
2021-03-18 15:21:28,603 - util.py[line:53] - INFO: Find Device UDID: xxxx
2021-03-18 15:21:28,604 - consumer.py[line:100] - INFO: listen UDP: udp/h264://127.0.0.1:8880
2021-03-18 15:21:28,604 - util.py[line:84] - INFO: Enabling hidden QT config
Traceback (most recent call last):
  File "main.py", line 60, in <module>
    main()
  File "main.py", line 56, in main
    args.func(args)
  File "main.py", line 21, in cmd_record_udp
    start_reading(consumer, device, stopSignal)
  File "/home/curtain/ios-screen-record/screen/util.py", line 147, in start_reading
    device.set_configuration()
  File "/home/curtain/.local/lib/python3.6/site-packages/usb/core.py", line 905, in set_configuration
    self._ctx.managed_set_configuration(self, configuration)
  File "/home/curtain/.local/lib/python3.6/site-packages/usb/core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
  File "/home/curtain/.local/lib/python3.6/site-packages/usb/core.py", line 159, in managed_set_configuration
    self.backend.set_configuration(self.handle, cfg.bConfigurationValue)
  File "/home/curtain/.local/lib/python3.6/site-packages/usb/backend/libusb1.py", line 812, in set_configuration
    _check(self.lib.libusb_set_configuration(dev_handle.handle, config_value))
  File "/home/curtain/.local/lib/python3.6/site-packages/usb/backend/libusb1.py", line 604, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 16] Resource busy
YueChen #7 · March 18, 2021 Author
Curtain 回复

重置配置确实有一定概率失败,尤其是第一次,多试几次,要不重启手机下试试?

太强了吧,支持,follow 到天涯海角😝

太猛了,兄弟,有没有兴趣,再搞个 Android 的?

JamesChung 回复

Android 这个就很棒了
https://github.com/Genymobile/scrcpy

Author only

期待个 win 版的

期待个 win 版的 +1,如果能在 Windows 上安装获取 ios 的手机屏幕就好了

想咨询下大神,这个只能通过 vlc 工具进行播放,不能像 stf 那样直接通过浏览器访问?

YueChen #15 · April 06, 2021 Author
kisom 回复

这个现在只是提供了一个底层能力,vlc 播放的就已经是 h264 裸流了。如果你想用浏览器访问,就自建个什么媒体服务器把这个 h264 流转发到 web 前端播放就好了。

本地设备在播放音乐时经常出现 [Errno 5] Input/Output Error 导致的视频转发或录制停止, 不知道大神遇到过这种情况么?

YueChen #17 · April 14, 2021 Author
Tiper 回复

我测试了下用 gstreamer 模式和 record 模式测试了下 qq 音乐,和一些本地一些视频,看起来都挺正常的。
运行时长?或者特殊音频数据可以给我测试下。

我把 quicktime hack 移植到 windows 平台了(这是作者本人都没搞定的工作),并且用 qt+ffmpeg 重写了显示客户端。可以流畅播放,可惜这里不能贴视频。群主可以搜索一下有个工具叫 “虫洞”,但是它用的是 libusb-win32 这个驱动,我还是用的 winusb 驱动。

prife 回复

不开源有卵用

YueChen #20 · June 21, 2021 Author
prife 回复

windows 上我也实现了,不过需要重置 Apple 的驱动成 libusb 或者 winusb 确实可以录制和同屏
但是有个问题就是驱动重置之后 iTunes 就找不到 iPhone 设备了,会导致其他功能不能用了,iPhone 本身驱动又不开源,这就很恶心了

YueChen #21 · June 21, 2021 Author
prife 回复

“虫洞” 你说的这个工具我去试了下,同样会影响 iTunes 正常使用

22Floor has deleted
23Floor has deleted
YueChen #24 · June 21, 2021 Author

这个我处理下,不需要 gstreamer 服务的话不加载

YueChen 回复

windows 能提供参考下吗?最近也在研究这个,一直没有进展

YueChen #26 · July 09, 2021 Author
woshi123 回复

先提供方法吧,使用 https://zadig.akeo.ie/ 将 iPhone 驱动给替换成 libusb 或者 winusb ,然后使用对应的三方库,同样还是上述流程,只不过中间有些三方库的使用区别。

能支持跨平台不?必要用 macos?win 上可以不

冒个泡泡 回复

看 windows 分支

YueChen 回复

有办法不重置 USB 吗?我一直没解决这个问题。我发现只要 quicktime 启动录制设备时,就可以不重置 USB 也能录制视频。但是不知道如何处理多个 iOS 设备录制

zhangpei 回复

重置 USB 会导致 WDA 服务挂掉。。。

zhangpei 回复

重置之后不关闭就好了,保持激活状态,需要录制的时候主动发个 ping 消息就可以了

pip_.zip/pip install --ignore-installed --no-user --prefix /private/var/folders/km/8hs4jth149jbtyzq3j66c68h0000gn/T/pip-build-env-twbzbepb/overlay --no-warn-script-location --no-binary :none: --only-binary :none: -i https://pypi.org/simple -- setuptools wheel pycairo Check the logs for full command output.
Collecting ioscreen
Using cached ioscreen-1.0.0.dev9-py3-none-any.whl (24 kB)
Using cached ioscreen-1.0.0.dev4-py3-none-any.whl (22 kB)
ERROR: Cannot install ioscreen==1.0.0.dev4, ioscreen==1.0.0.dev9 and ioscreen==1.1.0.dev9 because these package versions have conflicting dependencies.

The conflict is caused by:
ioscreen 1.1.0.dev9 depends on PyGObject==3.38.0
ioscreen 1.0.0.dev9 depends on PyGObject==3.38.0
ioscreen 1.0.0.dev4 depends on PyGObject==3.38.0

To fix this you could try to:

  1. loosen the range of package versions you've specified
  2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies
WARNING: You are using pip version 21.2.4; however, version 21.3.1 is available.
You should consider upgrading via the '/usr/local/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip' command.

有方法骗过手机,认为另一端是 Mac 吗?其实是其他的 os

我试了下 centos 7.9 下,没法获取到视频。ping 包发送出去之后,就没有任何数据返回了

iOS 的 usb 投屏,需要一个 windows 端,有没有大佬接单的?条件优厚

YueChen 回复

录制保存的 wav 播放不了吗?我看 gstreamer 上是要加 wav 头的 和 pps ppt 的,record 直接写到一个后缀是.wav 的文件,这个不行吧

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up