关于 iPhone 如何使用 USB 投屏的方案能找到的资料非常有限,很多开源的代码也很难一次跑起来,所以我把最近的一些调研做一个简单的总结和分享
业务中刚好需要用到使用 USB 数据线投屏到电脑的场景,在安卓上可以使用 scrcpy,在 iOS 中,没有跟 scrcpy 类似功能的开源解决方案,所以最近调研了很多方向,本文就浅谈一下 iPhone 音视频投屏的实现。
要自定义开发 iOS 设备的投屏,一般而言,有下面这几种方式:
使用苹果自带的 AVFoundation 库
使用苹果AVFoundation库获取 iOS设备上的视频内容,这个方法是可行的,但是大部分的开源库都是获取 iPhone 的摄像头上的视频流,而非手机上的视频流,而且 chatgpt4 给的答案也很果断的说不可以:
所以我不确定是不是一个 Hack 的方式,ios-minicap就是这样实现的,厉害的是,这个7年前的项目现在也能跑起来,核心原理是利用 [AVCaptureDevice devicesWithMediaType: AVMediaTypeMuxed]
获取设备,然后使用 libjpeg-turbo
把 CMSampleBufferRef
编码成 jpeg。
本地开发的时候,需要修改下 CMakeLists.txt
文件,替换 jpeg-turbo 的文件路径,比如我使用 brew 安装的,替换成下面这样:
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腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!