Skip to content

Commit 496959a

Browse files
authored
Merge pull request #301 from shiguredo/feature/initial-camera-enabled
Configure.initialCameraEnabled を追加、接続時のカメラ初期化を遅延できるようにする
2 parents d2b2165 + e39d2ac commit 496959a

File tree

5 files changed

+111
-15
lines changed

5 files changed

+111
-15
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
## develop
1313

14+
- [UPDATE] VideoHardMuteActor での映像ハードミュート解除時にカメラキャプチャ未起動なら開始するようにする
15+
- `Configuration.initialCameraEnabled` により接続時にカメラ初期化が行われていない場合の分岐
16+
- @t-miya
1417
- [UPDATE] libwebrtc m144.7559.2.1 に上げる
1518
- @t-miya
1619
- [UPDATE] Statistics, StatisticsEntry をドキュメント対象として公開する
@@ -19,6 +22,9 @@
1922
- [UPDATE] Configuration.simulcastRid を非推奨にする
2023
- 移行先は `Configuration.simulcastRequestRid`
2124
- @zztkm
25+
- [ADD] Configuration に接続確立時にカメラ初期化を行わない設定 `initialCameraEnabled` を追加する
26+
- 接続時に映像ハードミュートを行うために利用する
27+
- @t-miya
2228
- [ADD] MediaChannel に音声ソフトミュートを設定する `setAudioSoftMute(_:)` を追加する
2329
- 送信ストリームの AudioTrack を取得し、MediaStream.audioEnabled を切り替える
2430
- デジタルサイレンスパケットが送られる状態となり、マイクからの音声は送出されない

Sora/Configuration.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ public struct Configuration {
135135
/// デフォルトは `true` です。
136136
public var audioEnabled: Bool = true
137137

138+
/// 接続確立時に端末カメラキャプチャを自動起動するかどうか。
139+
///
140+
/// `cameraSettings.isEnabled` が `true` の場合でも、このフラグが `false` であれば
141+
/// 接続時点ではカメラキャプチャを起動しません。
142+
/// 後から `MediaChannel.setVideoHardMute(false)` で必要に応じて開始できます。
143+
public var initialCameraEnabled: Bool = true
144+
138145
/// サイマルキャストの可否。 `true` であればサイマルキャストを有効にします。
139146
public var simulcastEnabled: Bool = false
140147

Sora/MediaChannel.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,18 @@ public final class MediaChannel {
754754
if mute {
755755
// ソフトミュートによる黒塗りフレーム送出 -> ハードミュート有効化の順になるようにします
756756
senderStream.videoEnabled = false
757-
try await Self.videoHardMuteActor.setMute(mute: true, senderStream: senderStream)
757+
try await Self.videoHardMuteActor.setMute(
758+
mute: true,
759+
senderStream: senderStream,
760+
cameraSettings: configuration.cameraSettings
761+
)
758762
} else {
759763
// ハードミュート無効化 -> ソフトミュートによる黒塗りフレーム送出解除の順になるようにします
760-
try await Self.videoHardMuteActor.setMute(mute: false, senderStream: senderStream)
764+
try await Self.videoHardMuteActor.setMute(
765+
mute: false,
766+
senderStream: senderStream,
767+
cameraSettings: configuration.cameraSettings
768+
)
761769
senderStream.videoEnabled = true
762770
}
763771
Logger.debug(type: .mediaChannel, message: "setVideoHardMute mute=\(mute)")

Sora/PeerChannel.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,9 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
482482
}
483483

484484
// カメラの初期化
485-
if configuration.videoEnabled, configuration.cameraSettings.isEnabled {
485+
if configuration.videoEnabled, configuration.cameraSettings.isEnabled,
486+
configuration.initialCameraEnabled
487+
{
486488
initializeCameraVideoCapture(stream: stream)
487489
}
488490

Sora/VideoMute.swift

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@ import Foundation
55
actor VideoHardMuteActor {
66
// 処理実行中フラグ
77
private var isProcessing = false
8-
// カメラ操作のためのキャプチャラー
9-
private var capturer: CameraVideoCapturer?
8+
// ハードミュートで stop したキャプチャを解除時に restart するための保持キャプチャラー
9+
private var storedCapturer: CameraVideoCapturer?
1010

1111
/// ハードミュートを有効化/無効化します
1212
///
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,25 +27,25 @@ 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
// ミュート無効化する際にキャプチャラーを使用するため保持しておきます
35-
capturer = currentCapturer
35+
storedCapturer = currentCapturer
3636
return
3737
}
3838

3939
// ミュートを無効化します
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 capturerForRestart = storedCapturer {
45+
try await restartCameraVideoCapture(capturerForRestart, 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,76 @@ 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 に対応した CameraVideoCapturer を取得します。
109+
// `.front` / `.back` を優先して利用し、静的プロパティ経由で参照される状態と齟齬が出ないようにします。
110+
let capturer: CameraVideoCapturer
111+
switch cameraSettings.position {
112+
case .front:
113+
guard let front = CameraVideoCapturer.front else {
114+
continuation.resume(
115+
throwing: SoraError.cameraError(reason: "front camera is not found"))
116+
return
117+
}
118+
capturer = front
119+
case .back:
120+
guard let back = CameraVideoCapturer.back else {
121+
continuation.resume(throwing: SoraError.cameraError(reason: "back camera is not found"))
122+
return
123+
}
124+
capturer = back
125+
case .unspecified:
126+
continuation.resume(
127+
throwing: SoraError.cameraError(
128+
reason: "CameraSettings.position should not be .unspecified"
129+
)
130+
)
131+
return
132+
@unknown default:
133+
guard let device = CameraVideoCapturer.device(for: cameraSettings.position) else {
134+
continuation.resume(
135+
throwing: SoraError.cameraError(reason: "camera device is not found for position")
136+
)
137+
return
138+
}
139+
capturer = CameraVideoCapturer(device: device)
140+
}
141+
142+
guard
143+
// 接続時設定に基づいてカメラの解像度、フレームレートを指定します
144+
let format = CameraVideoCapturer.format(
145+
width: cameraSettings.resolution.width,
146+
height: cameraSettings.resolution.height,
147+
for: capturer.device,
148+
frameRate: cameraSettings.frameRate),
149+
let frameRate = CameraVideoCapturer.maxFrameRate(cameraSettings.frameRate, for: format)
150+
else {
151+
continuation.resume(
152+
throwing: SoraError.cameraError(reason: "failed to resolve camera settings"))
153+
return
154+
}
155+
156+
// カメラキャプチャを開始します
157+
// CameraVideoCapturer.start はコールバック形式です
158+
capturer.stream = senderStream
159+
// start 完了まで capturer を確実に生存させるためにクロージャ側でも保持します。
160+
// start 成功時は CameraVideoCapturer.current がセットされ、以後はそちらが保持します。
161+
capturer.start(format: format, frameRate: frameRate) { [capturer] error in
162+
if let error {
163+
continuation.resume(throwing: error)
164+
} else {
165+
continuation.resume(returning: ())
166+
}
167+
}
168+
}
169+
}
170+
}
98171
}

0 commit comments

Comments
 (0)