Skip to content

Commit 92897f3

Browse files
committed
Configure.initialCameraEnabled を追加、接続時のカメラ初期化を遅延できるようにする
1 parent d2b2165 commit 92897f3

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
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: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)