跳到主要内容
帮助

概览

发布于 2021年4月8日· 更新于 2025年12月9日

Reincubate 的 Camo SDK 能够将应用中的实时音视频数据发送到 macOS 和 Windows 上的 Camo Studio。它支持通过 USB 或无线方式工作。USB 针对低延迟和性能进行了优化,尽可能减少应用中的资源占用;无线方式则让你摆脱线缆的束缚。

Camo SDK 可供商业合作伙伴和协作方使用。如需了解更多信息,请通过 enterprise@reincubate.com 联系我们的团队。

目前,该 SDK 仅支持在运行 iOS 12 或更高版本的实体 iOS 或 iPadOS 设备(不包括模拟器和 Mac Catalyst)上运行的 iOS 应用。Camo SDK 并不原生兼容 Objective-C,因此如果你的应用主要使用 Objective-C 编写,可能需要创建一个封装层。


API 参考

如果你想直接上手,可以查看完整的 API 参考,其中记录了 Camo SDK 提供的每一个符号。否则,本文档将带你了解一个示例集成流程。


安装 Camo SDK

收到 Camo SDK 时,你会收到一个名为 CamoProducerKit.xcframework 的文件。这个独立框架包含了你所需的一切,没有任何外部依赖,因此安装非常简单。

  1. 在 Project Navigator 中选择你的应用项目。
  2. 在项目的 target 侧边栏中选择你的应用 target。如果没有看到侧边栏,它可能位于页面顶部的下拉菜单中。
  3. 确保你位于“General”标签页,然后向下滚动到“Frameworks, Libraries, and Embedded Content”标题处。
  4. 将 CamoProducerKit.xcframework 文件拖放到该标题下方的文件列表中。
  5. 确保该 SDK 设置为“Embed & Sign”。

完成后,你应该会看到如图所示的框架:

示例应用中嵌入的 Camo SDK

示例应用中嵌入的 Camo 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 中查看你的设备。

无线连接

要使用无线连接,用户必须授予本地网络访问权限。 应在 Info.plist 中将 Bonjour 服务添加为 NSBonjourServices:

  • _camo._tcp
  • _camopairing._tcp

调用 camoController.start() 后,SDK 就已准备好进行配对,并与 Camo Studio 建立网络连接。

配对

当用户点击 Camo Studio 中的“+”按钮时,配对流程就会开始。Camo Studio 会显示一个二维码,需要在应用端进行扫描。

配对步骤:

  1. 点击 Camo Studio 中的“+”以显示二维码
  2. 扫描二维码并将其数据获取为字符串
  3. 通过调用 camoController.pairStudioWithQRCodeData(codeData) { success in } 将该数据传递给 SDK
  4. pairStudioWithQRCodeData 方法带有一个回调,用于告知你配对是否成功
  5. SDK 会与 Camo Studio 完成配对,并自动建立连接。

如果你需要取消配对(例如用户关闭了二维码扫描器或应用),应调用 camoController.cancelPairing()

配对有 30 秒的超时限制。如果在调用 pairStudioWithQRCodeData 后的 30 秒内未完成配对,pairStudioWithQRCodeData 方法的回调将会以 success 值 false 被调用。

配对的设备

配对完成后,SDK 会将配对数据保存到已配对设备列表中。CamoController 提供了 2 个方法用于操作该列表:

  • pairedDevices 用于获取该列表(如果你想在界面中显示该列表)
  • removePairedDevice 用于忽略该设备

下次要与 Camo Studio 建立无线连接时,只需点击 Camo Studio 中的“+”按钮并等待连接建立即可,无需再次扫描二维码。

确定连接状态

CamoController 提供了一个 CamoControllerState 属性,如上文所示,该属性也包含在状态变化委托方法中。该枚举为你的应用提供了有用的信息,例如可在界面中显示的已连接电脑名称。

以下是一个示例,展示如何更新界面以反映连接状态:

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 参考中查看更多详情。

需要帮助吗?

我们会尽力在一个工作日内回复所有消息。

support@reincubate.com
帮助