iOS设备有线投屏的方案

2023-11-09 18:54:55 浏览数 (2)

关于 iPhone 如何使用 USB 投屏的方案能找到的资料非常有限,很多开源的代码也很难一次跑起来,所以我把最近的一些调研做一个简单的总结和分享

业务中刚好需要用到使用 USB 数据线投屏到电脑的场景,在安卓上可以使用 scrcpy,在 iOS 中,没有跟 scrcpy 类似功能的开源解决方案,所以最近调研了很多方向,本文就浅谈一下 iPhone 音视频投屏的实现。

要自定义开发 iOS 设备的投屏,一般而言,有下面这几种方式:

使用苹果自带的 AVFoundation 库

使用苹果AVFoundation库获取 iOS设备上的视频内容,这个方法是可行的,但是大部分的开源库都是获取 iPhone 的摄像头上的视频流,而非手机上的视频流,而且 chatgpt4 给的答案也很果断的说不可以:

gptgpt

所以我不确定是不是一个 Hack 的方式,ios-minicap就是这样实现的,厉害的是,这个7年前的项目现在也能跑起来,核心原理是利用 [AVCaptureDevice devicesWithMediaType: AVMediaTypeMuxed] 获取设备,然后使用 libjpeg-turboCMSampleBufferRef 编码成 jpeg。

本地开发的时候,需要修改下 CMakeLists.txt 文件,替换 jpeg-turbo 的文件路径,比如我使用 brew 安装的,替换成下面这样:

代码语言:shell复制
include_directories(
        - /usr/local/opt/jpeg-turbo/include
          /opt/homebrew/Cellar/jpeg-turbo/2.1.5.1/include
)

target_link_libraries (ios_minicap
        "-framework Foundation"
        "-framework CoreFoundation"
        "-framework CoreMedia"
        "-framework CoreVideo"
        "-framework CoreMediaIO"
        "-framework AVFoundation"
        -/usr/local/opt/jpeg-turbo/lib/libturbojpeg.a)
         /opt/homebrew/Cellar/jpeg-turbo/2.1.5.1/lib/libturbojpeg.a)

ios-minicap 是不支持音频的,如果需要接收音频,需要自己处理音频流。

利用 AVFoundation 本身底层使用的 usbmuxd 通信

usbmuxd 的主要作用是在计算机和苹果设备之间建立一个 TCP/IP 连接,这样就可以通过 USB 端口与设备进行通信。它监听并管理设备的连接和断开,同时协调多个客户端之间的通信。这使得像 iTunes 这样的应用程序可以与设备同步,或者开发者可以使用 Xcode 等工具进行调试,usbmuxd 也是大部分主流投屏 sdk 使用的方式。

有个叫 Daniel Paulus 的老哥逆向工程后使用 golang 开发了一个 quicktime_video_hack的项目,底层主要依赖 libusb 库,基于 GStreamer 框架做编解码,这个项目本地运行 activate 命令启用视频流配置的时候,一直报错 Assertion failed: (list_empty(&darwin_cached_devices)), function darwin_init, file darwin_usb.c, line 603,主要是 gousb 版本的问题,手动更新 go get -u github.com/google/gousb@latest,或者手动调用 C.libusb_init(&ctx)可以解决。

使用 ReplayKit 或 ReplayKit2

录屏功能是 iOS 10 新推出的特性,苹果在 iOS 9 的 ReplayKit 保存录屏视频的基础上,增加了视频流实时直播功能,iOS 11 增强为 ReplayKit2,进一步提升了 Replaykit 的易用性和通用性,并且可以对整个手机实现屏幕录制,而非某些做了支持ReplayKit功能的App。

系统录屏采用的是扩展方式,有单独的进程,但是 iOS 系统为了保证系统流畅,给扩展程序的资源相对较少,扩展程序内存占用过大也会被 Kill 掉。有大部分直播软件使用的就是这种实现方式,但它的实时性不如前面2种。

使用采集卡

让 iOS 设备通过 Lightning AV 转换器将画面与声音输出到采集卡,再由采集卡的软件传输数据。这个方案不推荐,一是要硬件的支持有一定成本,二是实时性更差,多了一条链路,维护成本也更大。

总结

最终,我们采取的方案是 AVFoundation,但没有使用 libjpeg-turbo,而是用 macOS 内置的 CoreGraphics 编码,从视频采集 -> socket传输 -> nodejs接收并通过 websocket 发送 -> web渲染 ,整体的耗时在 80ms 左右。值的注意的是,使用 macOS10.10 以上的sdk,需要加上这段代码才能允许访问 iPhone 设备:

代码语言:objective-c复制
#ifdef __MAC_10_10
    // Enable iOS device to show up as AVCapture devices
    // From WWDC video 2014 #508 at 5:34
    // https://developer.apple.com/videos/wwdc/2014/#508
    CMIOObjectPropertyAddress prop = {
        kCMIOHardwarePropertyAllowScreenCaptureDevices,
        kCMIOObjectPropertyScopeGlobal,
        kCMIOObjectPropertyElementMain};
    UInt32 allow = 1;
    CMIOObjectSetPropertyData(kCMIOObjectSystemObject, &prop, 0, NULL, sizeof(allow), &allow);
#endif

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞