@@ -13,10 +13,11 @@ actor VideoHardMuteActor {
1313 /// - Parameters:
1414 /// - mute: `true` で有効化、`false` で無効化
1515 /// - senderStream: 送信ストリーム
16+ /// - cameraSettings: カメラ設定
1617 /// - Throws:
17- /// - 既に処理実行中、またはカメラキャプチャラーが無効な場合は `SoraError.mediaChannelError`
18+ /// - 既に処理実行中の場合は `SoraError.mediaChannelError`
1819 /// - カメラ操作の失敗時は `SoraError.cameraError`
19- func setMute( mute: Bool , senderStream: MediaStream ) async throws {
20+ func setMute( mute: Bool , senderStream: MediaStream , cameraSettings : CameraSettings ) async throws {
2021 guard !isProcessing else {
2122 throw SoraError . mediaChannelError ( reason: " video hard mute operation is in progress " )
2223 }
@@ -26,9 +27,8 @@ actor VideoHardMuteActor {
2627 // ミュートを有効化します
2728 if mute {
2829 guard let currentCapturer = await currentCameraVideoCapturer ( ) else {
29- // 既にハードミュート済み(再開用キャプチャラーを保持)なら、冪等として何もしません
30- if capturer != nil { return }
31- throw SoraError . mediaChannelError ( reason: " CameraVideoCapturer is unavailable " )
30+ // キャプチャ未起動の場合は停止対象がないため、冪等として成功扱いにします
31+ return
3232 }
3333 try await stopCameraVideoCapture ( currentCapturer)
3434 // ミュート無効化する際にキャプチャラーを使用するため保持しておきます
@@ -40,11 +40,12 @@ actor VideoHardMuteActor {
4040 // 現在のキャプチャラーが取得できる場合は既に再開済みとして成功扱いにします
4141 let currentCapturer = await currentCameraVideoCapturer ( )
4242 if currentCapturer != nil { return }
43- // 前回停止時のキャプチャラーが保持できていない場合エラー
44- guard let stored = capturer else {
45- throw SoraError . mediaChannelError ( reason: " CameraVideoCapturer is unavailable " )
43+ // 前回停止時のキャプチャラーが保持できていれば restart、なければ新規に start します
44+ if let stored = capturer {
45+ try await restartCameraVideoCapture ( stored, senderStream: senderStream)
46+ return
4647 }
47- try await restartCameraVideoCapture ( stored , senderStream: senderStream)
48+ try await startCameraVideoCapture ( cameraSettings : cameraSettings , senderStream: senderStream)
4849 }
4950
5051 // 現在のカメラキャプチャラーを取得します
@@ -95,4 +96,59 @@ actor VideoHardMuteActor {
9596 }
9697 }
9798 }
99+
100+ // カメラキャプチャを開始します
101+ private func startCameraVideoCapture(
102+ cameraSettings: CameraSettings ,
103+ senderStream: MediaStream
104+ ) async throws {
105+ // libwebrtc のカメラ用キュー(SoraDispatcher)を利用して実行します
106+ try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < Void , Error > ) in
107+ SoraDispatcher . async ( on: . camera) {
108+ // 接続時設定の position に対応した device を取得します
109+ // 未指定 (.unspecified) 時はエラーとします
110+ if cameraSettings. position == . unspecified {
111+ continuation. resume (
112+ throwing: SoraError . cameraError (
113+ reason: " CameraSettings.position should not be .unspecified "
114+ )
115+ )
116+ return
117+ }
118+
119+ let device =
120+ CameraVideoCapturer . device ( for: cameraSettings. position)
121+ guard let device else {
122+ continuation. resume ( throwing: SoraError . cameraError ( reason: " camera device is not found " ) )
123+ return
124+ }
125+
126+ guard
127+ // 接続時設定に基づいてカメラの解像度、フレームレートを指定します
128+ let format = CameraVideoCapturer . format (
129+ width: cameraSettings. resolution. width,
130+ height: cameraSettings. resolution. height,
131+ for: device,
132+ frameRate: cameraSettings. frameRate) ,
133+ let frameRate = CameraVideoCapturer . maxFrameRate ( cameraSettings. frameRate, for: format)
134+ else {
135+ continuation. resume (
136+ throwing: SoraError . cameraError ( reason: " failed to resolve camera settings " ) )
137+ return
138+ }
139+
140+ // カメラキャプチャを開始します
141+ // CameraVideoCapturer.start はコールバック形式です
142+ let capturer = CameraVideoCapturer ( device: device)
143+ capturer. stream = senderStream
144+ capturer. start ( format: format, frameRate: frameRate) { error in
145+ if let error {
146+ continuation. resume ( throwing: error)
147+ } else {
148+ continuation. resume ( returning: ( ) )
149+ }
150+ }
151+ }
152+ }
153+ }
98154}
0 commit comments