-
Notifications
You must be signed in to change notification settings - Fork 10
setVideoSoftMute / setVideoHardMute を追加 #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
72de657
93968fa
71df963
3b615c0
3f4d3a6
09358c8
949fa6e
addbd7c
582cc4a
f3608c4
ebe9fb2
c53cea1
6ad44d2
1cac780
45f3704
bf55a88
e673bc2
4b011a1
6dd72c2
5ab5279
fdb6980
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,18 +48,23 @@ public protocol MediaStream: AnyObject { | |
| // MARK: - 映像と音声の可否 | ||
|
|
||
| /// 映像の可否。 | ||
| /// ``false`` をセットすると、サーバーへの映像の送受信を停止します。 | ||
| /// ``true`` をセットすると送受信を再開します。 | ||
| /// `false` をセットすると、サーバーへの映像の送受信を停止します。 | ||
| /// `true` をセットすると送受信を再開します。 | ||
| var videoEnabled: Bool { get set } | ||
|
|
||
| /// 音声の可否。 | ||
| /// ``false`` をセットすると、サーバーへの音声の送受信を停止します。 | ||
| /// ``true`` をセットすると送受信を再開します。 | ||
| /// `false` をセットすると、サーバーへの音声の送受信を停止します。 | ||
| /// `true` をセットすると送受信を再開します。 | ||
| /// | ||
| /// サーバーへの送受信を停止しても、マイクはミュートされませんので注意してください。 | ||
| var audioEnabled: Bool { get set } | ||
|
|
||
| /// 音声トラックを保持している場合は ``true`` を返します。 | ||
| /// 映像トラックを保持している場合は `true` を返します。 | ||
| /// | ||
| /// SDK 内部で利用する判定用のプロパティです。 | ||
|
||
| var hasVideoTrack: Bool { get } | ||
|
|
||
| /// 音声トラックを保持している場合は `true` を返します。 | ||
| /// | ||
| /// SDK 内部で利用する判定用のプロパティです。 | ||
| var hasAudioTrack: Bool { get } | ||
|
|
@@ -206,6 +211,10 @@ class BasicMediaStream: MediaStream { | |
| nativeAudioTrack != nil | ||
| } | ||
|
|
||
| var hasVideoTrack: Bool { | ||
| nativeVideoTrack != nil | ||
| } | ||
|
|
||
| var remoteAudioVolume: Double? { | ||
| get { | ||
| nativeAudioTrack?.source.volume | ||
|
|
||
zztkm marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import Foundation | ||
|
|
||
| // 映像ハードミュートの同時呼び出しによるレースコンディション防止を目的とした Actor です | ||
| // MediaChannel.setVideoHardMute(_:) での使用を想定しています | ||
| actor VideoHardMuteActor { | ||
| // 処理実行中フラグ | ||
| private var isProcessing = false | ||
| // カメラ操作のためのキャプチャラー | ||
| private var capturer: CameraVideoCapturer? | ||
|
|
||
| /// ハードミュートを有効化/無効化します | ||
| /// | ||
| /// - Parameters: | ||
| /// - mute: `true` で有効化、`false` で無効化 | ||
| /// - senderStream: 送信ストリーム | ||
| /// - Throws: | ||
| /// - 既に処理実行中、またはカメラキャプチャラーが無効な場合は `SoraError.mediaChannelError` | ||
| /// - カメラ操作の失敗時は `SoraError.cameraError` | ||
| func setMute(mute: Bool, senderStream: MediaStream) async throws { | ||
| guard !isProcessing else { | ||
| throw SoraError.mediaChannelError(reason: "video hard mute operation is in progress") | ||
| } | ||
| isProcessing = true | ||
| defer { isProcessing = false } | ||
|
|
||
| // ミュートを有効化します | ||
| if mute { | ||
| guard let currentCapturer = await currentCameraVideoCapturer() else { | ||
| // 既にハードミュート済み(再開用キャプチャラーを保持)なら、冪等として何もしません | ||
| if capturer != nil { return } | ||
| throw SoraError.mediaChannelError(reason: "CameraVideoCapturer is unavailable") | ||
| } | ||
| try await stopCameraVideoCapture(currentCapturer) | ||
| // ミュート無効化する際にキャプチャラーを使用するため保持しておきます | ||
| capturer = currentCapturer | ||
| return | ||
| } | ||
|
|
||
| // ミュートを無効化します | ||
| // 現在のキャプチャラーが取得できる場合は既に再開済みとして成功扱いにします | ||
| let currentCapturer = await currentCameraVideoCapturer() | ||
| if currentCapturer != nil { return } | ||
| // 前回停止時のキャプチャラーが保持できていない場合エラー | ||
| guard let stored = capturer else { | ||
| throw SoraError.mediaChannelError(reason: "CameraVideoCapturer is unavailable") | ||
| } | ||
| try await restartCameraVideoCapture(stored, senderStream: senderStream) | ||
| } | ||
|
|
||
| // 現在のカメラキャプチャラーを取得します | ||
| private func currentCameraVideoCapturer() async -> CameraVideoCapturer? { | ||
| // libwebrtc のカメラ用キュー(SoraDispatcher)を利用して実行します | ||
| await withCheckedContinuation { continuation in | ||
| SoraDispatcher.async(on: .camera) { | ||
| continuation.resume(returning: CameraVideoCapturer.current) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // カメラキャプチャを停止します | ||
| private func stopCameraVideoCapture(_ capturer: CameraVideoCapturer) async throws { | ||
| // libwebrtc のカメラ用キュー(SoraDispatcher)を利用して実行します | ||
| try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in | ||
| SoraDispatcher.async(on: .camera) { | ||
| // CameraVideoCapturer.stop はコールバック形式です | ||
| capturer.stop { error in | ||
| if let error { | ||
| continuation.resume(throwing: error) | ||
| } else { | ||
| continuation.resume(returning: ()) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // カメラキャプチャを再開します | ||
| private func restartCameraVideoCapture( | ||
| _ capturer: CameraVideoCapturer, | ||
| senderStream: MediaStream | ||
| ) async throws { | ||
| // libwebrtc のカメラ用キュー(SoraDispatcher)を利用して実行します | ||
| try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in | ||
| SoraDispatcher.async(on: .camera) { | ||
| // マルチストリームの場合、停止時と現在の送信ストリームが異なることがあるので再設定します | ||
| capturer.stream = senderStream | ||
| // CameraVideoCapturer.restart はコールバック形式です | ||
| capturer.restart { error in | ||
| if let error { | ||
| continuation.resume(throwing: error) | ||
| } else { | ||
| continuation.resume(returning: ()) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI が 18.4 では通らなくなってしまったため上げています