CameraVideoCapturer

public final class CameraVideoCapturer : Sendable

カメラをキャプチャするクラスです。

解像度やフレームレートなどの設定は start 実行時に指定します。 カメラはパブリッシャーまたはグループの接続時に自動的に起動 (起動済みなら再起動) されます。

カメラの設定を変更したい場合は、 change を実行します。

共有状態は CameraStateOwner が、device は instance ごとの lock 付き storage が、 handlers は型全体で共有する lock 付き storage (private static let handlersStorage) が 保持します。この型が持つ instance の stored property は次の 3 つだけです。

  • id: instance を識別する ID
  • deviceStorage: AVCaptureDevice を保持する lock 付き storage
  • nativeStorage: RTCCameraVideoCapturer とその delegate を保持する storage

不変条件は次のとおりです。RTCCameraVideoCapturer の start / stop と AVCaptureDevice の使用は libwebrtc の capture session queue 上でのみ行います。 camera queue への hop は CameraQueueExecutor に閉じ込めており、start / stop / restart / change / flip はすべてこの queue を経由します。 frame callback (CameraVideoCapturerDelegate.capturer(_:didCapture:)) も libwebrtc の capture session queue 上で発火する前提とします (upstream の実装を 確認できないため、現行の挙動を前提とします)。

一方で current / isRunning / format / frameRate は owner の snapshot と resource テーブルから、stream は owner の resource テーブルから (弱参照のため capturer は MediaStream を保持しません)、device / position は instance の lock 付き storage から、handlers は型全体で共有する lock 付き storage から、captureSession は init で確定して以後差し替えない storage から読むため、任意のスレッドから呼べます。 device の setter も任意のスレッドから呼べるため、command の実行中に device を差し替えると、 その command が読む device は差し替え前後で変わり得ます。 handlers の get / set が排他するのは参照だけであり、返した CameraVideoCapturerHandlers が 持つイベントハンドラのプロパティは、その instance がプロパティごとに持つ HandlerStorage が 排他します。 どちらも任意のスレッドから読み書きできます。

Sendable に準拠するのは、共有状態を owner が、non-Sendable な実資源を lock 付き storage が保持しているためです。

インスタンスの取得

  • 利用可能なデバイスのリスト RTCCameraVideoCapturer.captureDevices を返します。

    Declaration

    Swift

    public static var devices: [AVCaptureDevice] { get }
  • 前面のカメラに対応するデバイス

    owner が instance を 1 つだけ遅延生成して強参照で保持し、2 回目以降は同じ instance を 返します。public static let のままだと @unchecked Sendable の除去後に non-Sendable 型の static let になり Swift 6 でエラーになるため computed property と しています。

    Declaration

    Swift

    public static var front: CameraVideoCapturer? { get }
  • 背面のカメラに対応するデバイス

    Declaration

    Swift

    public static var back: CameraVideoCapturer? { get }
  • 起動中のデバイス

    owner の snapshot を読みます。書き込みは owner の publish へ一本化するため setter は設けません (外部からは従来どおり読み取り専用)。

    Declaration

    Swift

    public static var current: CameraVideoCapturer? { get }
  • RTCCameraVideoCapturer が保持している AVCaptureSession

    Declaration

    Swift

    public var captureSession: AVCaptureSession { get }
  • 指定したカメラ位置にマッチした最初のデバイスを返します。 captureDevice(for: .back) とすれば背面カメラを取得できます。

    Declaration

    Swift

    public static func device(for position: AVCaptureDevice.Position) -> AVCaptureDevice?
  • 指定された設定に最も近い AVCaptureDevice.Format? を返します。

    Declaration

    Swift

    public static func format(
      width: Int32, height: Int32, for device: AVCaptureDevice, frameRate: Int? = nil
    ) -> AVCaptureDevice.Format?
  • 指定された FPS 値をサポートしているレンジが存在すれば、その値を返します。 存在しない場合はサポートされているレンジの中で最大の値を返します。

    Declaration

    Swift

    public static func maxFrameRate(_ frameRate: Int, for format: AVCaptureDevice.Format) -> Int?
  • 引数に指定された capturer を停止し、反対の position を持つ CameraVideoCapturer を起動します。 CameraVideoCapturer の起動には、 capturer と近い設定のフォーマットとフレームレートが利用されます。 また、起動された CameraVideoCapturer には capturer の保持する MediaStream が設定されます。

    切り替え先の stream は start より前に設定します。RTCCameraVideoCapturer は start の completion より前から frame callback を発生させることがあり、静的に再利用される front / back capturer に前回利用時の stream が残っていると、旧 stream へ frame が 送信されるためです。stream を先行設定し、start 失敗時は元の stream へ rollback します。 連続実行時の競合は owner の state が持つ flip 実行中フラグで防ぎます。 引数には CameraVideoCapturer.current を渡してください。

    Declaration

    Swift

    public static func flip(
      _ capturer: CameraVideoCapturer, completionHandler: @escaping ((Error?) -> Void)
    )

プロパティ

  • 出力先のストリーム

    owner の resource テーブルへ同期で読み書きします。owner の queue を 同期 wait しないため、libwebrtc の capture session queue 上からの代入でも deadlock しません。

    Declaration

    Swift

    public var stream: MediaStream? { get set }
  • カメラが起動中であれば true

    owner の snapshot から読みます。更新は owner の reducer 経由で行います。

    Declaration

    Swift

    public var isRunning: Bool { get }
  • イベントハンドラ

    lock 付き storage から同一インスタンスを返します。利用者の CameraVideoCapturer.handlers.onCapture = ... という in-place 変更を維持します。

    Declaration

    Swift

    public static var handlers: CameraVideoCapturerHandlers { get set }
  • カメラの位置

    Declaration

    Swift

    public var position: AVCaptureDevice.Position { get }
  • 使用中のデバイス

    lock 付き storage から同期で読み書きします。owner の queue を同期 wait しません。

    Declaration

    Swift

    public var device: AVCaptureDevice { get set }
  • フレームレート

    owner の snapshot から読みます。更新は owner の reducer 経由で行います。

    Declaration

    Swift

    public var frameRate: Int? { get }
  • フォーマット

    AVCaptureDevice.Format は non-Sendable のため owner の resource テーブルが 保持し、ここから lock 付きで読み出します。更新はカメラ操作が resource テーブルへ 同期で行います (frameRate は reducer の state が保持します)。

    Declaration

    Swift

    public var format: AVCaptureDevice.Format? { get }
  • 引数に指定した device を利用して CameraVideoCapturer を初期化します。 自動的に初期化される静的プロパティ、 front/back を定義しています。 上記以外のデバイスを利用したい場合のみ CameraVideoCapturer を生成してください。

    Declaration

    Swift

    public init(device: AVCaptureDevice)

カメラの操作

  • カメラを起動します。

    このメソッドを実行すると、 UIDevice の beginGeneratingDeviceOrientationNotifications() が実行されます。 beginGeneratingDeviceOrientationNotifications() または endGeneratingDeviceOrientationNotifications() を使う際は 必ず対に実行するように注意してください。

    Declaration

    Swift

    public func start(
      format: AVCaptureDevice.Format,
      frameRate: Int,
      completionHandler: @escaping ((Error?) -> Void)
    )
  • カメラを停止します。

    このメソッドを実行すると、 UIDevice の endGeneratingDeviceOrientationNotifications() が実行されます。 beginGeneratingDeviceOrientationNotifications() または endGeneratingDeviceOrientationNotifications() を使う際は 必ず対に実行するように注意してください。

    Declaration

    Swift

    public func stop(completionHandler: @escaping ((Error?) -> Void))
  • 停止前と同じ設定でカメラを再起動します。

    Declaration

    Swift

    public func restart(completionHandler: @escaping ((Error?) -> Void))
  • カメラを停止後、指定されたパラメーターで起動します。

    Declaration

    Swift

    public func change(
      format: AVCaptureDevice.Format? = nil, frameRate: Int? = nil,
      completionHandler: @escaping ((Error?) -> Void)
    )