概观
Reincubate 的Camo SDK 能够将实时视听数据从您的应用程序发送到 macOS 和 Windows 上的 Camo Studio。它通过 USB 运行,并针对低延迟和性能进行了优化;尽可能少地消耗应用程序中的资源。
目前,SDK 仅支持在 iOS 12 或更高版本的物理 iOS 或 iPadOS 设备(不包括模拟器和 Mac Catalyst)上运行的 iOS 应用程序。 Camo SDK 不具备开箱即用的 Objective-C 兼容性,因此如果您的应用主要是 Objective-C,您可能需要创建一个包装器。
API 参考
如果您想直接进入,可以查看完整的 API 参考,其中记录了 Camo SDK 提供的每个符号。否则,本文档将引导您完成与 SDK 的示例集成。
安装迷彩SDK
当您收到 Camo SDK 时,您将收到一个名为CamoProducerKit.xcframework
的文件。这个独立的框架拥有你需要的一切,没有外部依赖,所以安装很容易。
- 在项目导航器中选择应用程序的项目。
- 从项目的目标侧栏中选择应用程序的目标。如果您没有看到侧边栏,它可能位于页面顶部的下拉列表中。
- 确保您位于“常规”选项卡上,然后向下滚动到“框架、库和嵌入内容”标题。
- 将
CamoProducerKit.xcframework
文件拖放到该标题下方的文件列表中。 - 确保 SDK 设置为“嵌入和签名”。
完成后,您应该看到如图所示的框架:

与您的应用程序集成
控制 Camo 服务
CamoProducerKit 的核心是CamoController ,它为您的应用程序提供了一个用于控制 Camo SDK 的集中式界面。
在您的应用程序中,您应该初始化CamoController 的一个实例。您可以选择在应用启动时执行此操作或将其保存以备后用。 start()
明确启动之前,Camo 控制器将使用很少的资源。但是,它会在初始化时准备音频和视频编码。
import CamoProducerKit class MyVideoManager { let controller = CamoController() // ... }
当您想在您的应用程序中激活 Camo 集成,启动Camo 服务以方便连接时,您可以调用start()
。完成后,您可以通过调用stop()
来停止它。
controller.start() // ... controller.stop()
此代码是一个开始,但它仍然无法接受来自 Camo Studio 的新连接。
响应新连接
在您的应用程序能够接受来自 Camo Studio 的新连接之前,您需要实现CamoControllerDelegate 。通过实现这两个委托方法,您可以收到有关连接状态更改的通知,并决定是接受还是拒绝连接。
extension MyVideoManager: CamoControllerDelegate { // Upon receiving a connection request, you can decide whether or not to accept it. func camoControllerShouldAcceptIncomingConnection(_ controller: CamoController) -> Bool { // You could return false if you aren't ready to accept connections, such as during onboarding. return true } // Called whenever the connection state changes, such as if the Camo service starts or stops, or if a new connection is made. func camoController(_ controller: CamoController, stateDidChangeTo state: CamoControllerState?) { // From here, you can update your UI and other state print("New state:", state) } }
完成后,您现在应该能够打开您的应用程序,连接到 USB,并在 Camo Studio 中查看您的设备。
确定连接状态
CamoController提供了一个CamoControllerState属性,该属性也包含在状态更改委托方法中,如上所示。此枚举提供对您的应用程序有用的信息,例如在 UI 中显示的已连接计算机的名称。
以下是如何更新 UI 以反映连接状态的示例:
func camoController(_ controller: CamoController, stateDidChangeTo state: CamoControllerState?) { let statusText = { () -> String in guard case let .running(serviceState) = state else { return "Camo service not running" } switch serviceState { case .active(let connection): return "Connected to \(connection.name)" case .paused(let connection): return "Paused, but connected to \(connection.name)" case .notConnected: return "Camo service running, but no connection" } }() DispatchQueue.main.async { self.statusTextLabel.text = statusText } }
发送视听数据
为了让客户端尽可能灵活,Camo SDK 不提供或控制任何捕获。相反,您负责为CamoController提供音频和视频数据。这可以通过两个简单的 API 调用来完成:
// upon receiving video from the camera or elsewhere camoController.enqueueVideo(sampleBuffer: sampleBuffer) // upon receiving audio from the microphone or elsewhere camoController.enqueuePCMAudio(data: chunk)
发送视频
如果您可以访问视频管道中的 CMSampleBuffer,将这些数据传递给 Camo SDK 就很简单了。
因此,使用基本视频 AVCaptureSession 进行设置非常简单。这是一个实际的例子。
class CaptureController: AVCaptureVideoDataOutputSampleBufferDelegate { // ... func startSession() throws { guard let camera = AVCaptureDevice.default(for: .video) else { fatalError("No camera found") } let input = try AVCaptureDeviceInput(device: camera) captureSession.addInput(input) let output = AVCaptureVideoDataOutput() output.alwaysDiscardsLateVideoFrames = true output.setSampleBufferDelegate(self, queue: videoDataOutputQueue) captureSession.addOutput(output) captureSession.startRunning() } // ... func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { camoController.enqueueVideo(sampleBuffer: sampleBuffer) } }
发送视频帧时,请确保:
- 帧持续时间为 30 FPS。
- 像素格式为
kCVPixelFormatType_32BGRA
。
您可以在 API 参考中查看更多详细信息。
发送音频
发送音频类似于视频,但可能需要更多步骤,具体取决于您的应用程序接收它的方式。有关示例实现,请参阅 Camo SDK 附带的演示应用程序。
发送音频数据时,请确保:
- 您的采样率为 48 kHz。
- 音频编解码器是 LPCM。
- 通道数为2。
- 位深度为 32。
- 每个音频包的样本数取决于 Studio(macOS 或 Windows),您可以从CamoController.audioSamplesRequired 获取。
您可以在 API 参考中查看更多详细信息。